OSDN Git Service

Add VC++ Project files for PuTTY DLL without exported functions.
authors_kawamoto <s_kawamoto@users.sourceforge.jp>
Tue, 25 Oct 2011 13:12:55 +0000 (22:12 +0900)
committers_kawamoto <s_kawamoto@users.sourceforge.jp>
Tue, 25 Oct 2011 13:12:55 +0000 (22:12 +0900)
299 files changed:
FFFTP.sln
FFFTP.vc90.sln
FFFTP.vc90.vcproj
FFFTP.vcproj
FFFTP_Eng_Release/FFFTP.exe
FFFTP_English.vc90.vcproj
FFFTP_English.vcproj
Release/FFFTP.exe
putty/BE_ALL.C [new file with mode: 0644]
putty/BE_ALL_S.C [new file with mode: 0644]
putty/BE_NONE.C [new file with mode: 0644]
putty/BE_NOSSH.C [new file with mode: 0644]
putty/BE_NOS_S.C [new file with mode: 0644]
putty/BUILDSCR [new file with mode: 0644]
putty/CHARSET/CHARSET.H [new file with mode: 0644]
putty/CHARSET/ENUM.C [new file with mode: 0644]
putty/CHARSET/FROMUCS.C [new file with mode: 0644]
putty/CHARSET/INTERNAL.H [new file with mode: 0644]
putty/CHARSET/LOCALENC.C [new file with mode: 0644]
putty/CHARSET/MACENC.C [new file with mode: 0644]
putty/CHARSET/MIMEENC.C [new file with mode: 0644]
putty/CHARSET/README [new file with mode: 0644]
putty/CHARSET/SBCS.C [new file with mode: 0644]
putty/CHARSET/SBCS.DAT [new file with mode: 0644]
putty/CHARSET/SBCSDAT.C [new file with mode: 0644]
putty/CHARSET/SBCSGEN.PL [new file with mode: 0644]
putty/CHARSET/SLOOKUP.C [new file with mode: 0644]
putty/CHARSET/TOUCS.C [new file with mode: 0644]
putty/CHARSET/UTF8.C [new file with mode: 0644]
putty/CHARSET/XENC.C [new file with mode: 0644]
putty/CMDGEN.C [new file with mode: 0644]
putty/CMDLINE.C [new file with mode: 0644]
putty/CONFIG.C [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/MAIN.C [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/MAKEFILE [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/MALLOC.C [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/MALLOC.H [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/PTY.C [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/PTY.H [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/README [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/SEL.C [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/SEL.H [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/TELNET.C [new file with mode: 0644]
putty/CONTRIB/CYGTERMD/TELNET.H [new file with mode: 0644]
putty/CONTRIB/KH2REG.PY [new file with mode: 0644]
putty/CPROXY.C [new file with mode: 0644]
putty/DIALOG.C [new file with mode: 0644]
putty/DIALOG.H [new file with mode: 0644]
putty/DOC/BLURB.BUT [new file with mode: 0644]
putty/DOC/CHM.BUT [new file with mode: 0644]
putty/DOC/CHM.CSS [new file with mode: 0644]
putty/DOC/CONFIG.BUT [new file with mode: 0644]
putty/DOC/ERRORS.BUT [new file with mode: 0644]
putty/DOC/FAQ.BUT [new file with mode: 0644]
putty/DOC/FEEDBACK.BUT [new file with mode: 0644]
putty/DOC/GS.BUT [new file with mode: 0644]
putty/DOC/INDEX.BUT [new file with mode: 0644]
putty/DOC/INTRO.BUT [new file with mode: 0644]
putty/DOC/LICENCE.BUT [new file with mode: 0644]
putty/DOC/MAKEFILE [new file with mode: 0644]
putty/DOC/MAN-PG.BUT [new file with mode: 0644]
putty/DOC/MAN-PL.BUT [new file with mode: 0644]
putty/DOC/MAN-PSCP.BUT [new file with mode: 0644]
putty/DOC/MAN-PSFT.BUT [new file with mode: 0644]
putty/DOC/MAN-PTEL.BUT [new file with mode: 0644]
putty/DOC/MAN-PTER.BUT [new file with mode: 0644]
putty/DOC/MAN-PUTT.BUT [new file with mode: 0644]
putty/DOC/MANCFG.BUT [new file with mode: 0644]
putty/DOC/MANPAGES.BUT [new file with mode: 0644]
putty/DOC/PAGEANT.BUT [new file with mode: 0644]
putty/DOC/PGPKEYS.BUT [new file with mode: 0644]
putty/DOC/PLINK.BUT [new file with mode: 0644]
putty/DOC/PSCP.BUT [new file with mode: 0644]
putty/DOC/PSFTP.BUT [new file with mode: 0644]
putty/DOC/PUBKEY.BUT [new file with mode: 0644]
putty/DOC/SITE.BUT [new file with mode: 0644]
putty/DOC/SSHNAMES.BUT [new file with mode: 0644]
putty/DOC/UDP.BUT [new file with mode: 0644]
putty/DOC/USING.BUT [new file with mode: 0644]
putty/DOC/VIDS.BUT [new file with mode: 0644]
putty/ICONS/CICON.PL [new file with mode: 0644]
putty/ICONS/ICON.PL [new file with mode: 0644]
putty/ICONS/MAKEFILE [new file with mode: 0644]
putty/ICONS/MKICON.PY [new file with mode: 0644]
putty/IMPORT.C [new file with mode: 0644]
putty/INT64.C [new file with mode: 0644]
putty/INT64.H [new file with mode: 0644]
putty/LDISC.C [new file with mode: 0644]
putty/LDISC.H [new file with mode: 0644]
putty/LDISCUCS.C [new file with mode: 0644]
putty/LICENCE [new file with mode: 0644]
putty/LOGGING.C [new file with mode: 0644]
putty/MACOSX/INFO.PLI [new file with mode: 0644]
putty/MACOSX/MAKEFILE [new file with mode: 0644]
putty/MACOSX/OSX.H [new file with mode: 0644]
putty/MACOSX/OSXCLASS.H [new file with mode: 0644]
putty/MACOSX/OSXCTRLS.M [new file with mode: 0644]
putty/MACOSX/OSXDLG.M [new file with mode: 0644]
putty/MACOSX/OSXMAIN.M [new file with mode: 0644]
putty/MACOSX/OSXSEL.M [new file with mode: 0644]
putty/MACOSX/OSXWIN.M [new file with mode: 0644]
putty/MACOSX/PUTTY.ICN [new file with mode: 0644]
putty/MACOSX/README.OSX [new file with mode: 0644]
putty/MINIBIDI.C [new file with mode: 0644]
putty/MISC.C [new file with mode: 0644]
putty/MISC.H [new file with mode: 0644]
putty/MKAUTO.SH [new file with mode: 0644]
putty/MKFILES.PL [new file with mode: 0644]
putty/MKUNXARC.SH [new file with mode: 0644]
putty/NETWORK.H [new file with mode: 0644]
putty/NOCPROXY.C [new file with mode: 0644]
putty/NOGSS.C [new file with mode: 0644]
putty/NOPRINT.C [new file with mode: 0644]
putty/NOTIMING.C [new file with mode: 0644]
putty/PGSSAPI.C [new file with mode: 0644]
putty/PGSSAPI.H [new file with mode: 0644]
putty/PINGER.C [new file with mode: 0644]
putty/PORTFWD.C [new file with mode: 0644]
putty/PPROXY.C [new file with mode: 0644]
putty/PROXY.C [new file with mode: 0644]
putty/PROXY.H [new file with mode: 0644]
putty/PSCP.C [new file with mode: 0644]
putty/PSFTP.C [new file with mode: 0644]
putty/PSFTP.H [new file with mode: 0644]
putty/PUTTY.H [new file with mode: 0644]
putty/PUTTYMEM.H [new file with mode: 0644]
putty/PUTTYPS.H [new file with mode: 0644]
putty/PuTTY.vc90.vcproj [new file with mode: 0644]
putty/PuTTY.vcproj [new file with mode: 0644]
putty/RAW.C [new file with mode: 0644]
putty/README [new file with mode: 0644]
putty/RECIPE [new file with mode: 0644]
putty/RESOURCE.H [new file with mode: 0644]
putty/RLOGIN.C [new file with mode: 0644]
putty/Release/PuTTY.dll [new file with mode: 0644]
putty/SERCFG.C [new file with mode: 0644]
putty/SETTINGS.C [new file with mode: 0644]
putty/SFTP.C [new file with mode: 0644]
putty/SFTP.H [new file with mode: 0644]
putty/SIGN.SH [new file with mode: 0644]
putty/SSH.C [new file with mode: 0644]
putty/SSH.H [new file with mode: 0644]
putty/SSHAES.C [new file with mode: 0644]
putty/SSHARCF.C [new file with mode: 0644]
putty/SSHBLOWF.C [new file with mode: 0644]
putty/SSHBN.C [new file with mode: 0644]
putty/SSHCRC.C [new file with mode: 0644]
putty/SSHCRCDA.C [new file with mode: 0644]
putty/SSHDES.C [new file with mode: 0644]
putty/SSHDH.C [new file with mode: 0644]
putty/SSHDSS.C [new file with mode: 0644]
putty/SSHDSSG.C [new file with mode: 0644]
putty/SSHGSS.H [new file with mode: 0644]
putty/SSHGSSC.C [new file with mode: 0644]
putty/SSHGSSC.H [new file with mode: 0644]
putty/SSHMD5.C [new file with mode: 0644]
putty/SSHNOGSS.C [new file with mode: 0644]
putty/SSHPRIME.C [new file with mode: 0644]
putty/SSHPUBK.C [new file with mode: 0644]
putty/SSHRAND.C [new file with mode: 0644]
putty/SSHRSA.C [new file with mode: 0644]
putty/SSHRSAG.C [new file with mode: 0644]
putty/SSHSH256.C [new file with mode: 0644]
putty/SSHSH512.C [new file with mode: 0644]
putty/SSHSHA.C [new file with mode: 0644]
putty/SSHZLIB.C [new file with mode: 0644]
putty/STORAGE.H [new file with mode: 0644]
putty/TELNET.C [new file with mode: 0644]
putty/TERMINAL.C [new file with mode: 0644]
putty/TERMINAL.H [new file with mode: 0644]
putty/TESTBACK.C [new file with mode: 0644]
putty/TESTDATA/BIGNUM.PY [new file with mode: 0644]
putty/TESTDATA/COLOURS.TXT [new file with mode: 0644]
putty/TESTDATA/LATTRS.TXT [new file with mode: 0644]
putty/TESTDATA/SCOCOLS.TXT [new file with mode: 0644]
putty/TESTDATA/UTF8.TXT [new file with mode: 0644]
putty/TESTDATA/VT100.TXT [new file with mode: 0644]
putty/TIME.C [new file with mode: 0644]
putty/TIMING.C [new file with mode: 0644]
putty/TREE234.C [new file with mode: 0644]
putty/TREE234.H [new file with mode: 0644]
putty/UNIX/CONFIGUR.AC [new file with mode: 0644]
putty/UNIX/GTKCFG.C [new file with mode: 0644]
putty/UNIX/GTKCOLS.C [new file with mode: 0644]
putty/UNIX/GTKCOLS.H [new file with mode: 0644]
putty/UNIX/GTKDLG.C [new file with mode: 0644]
putty/UNIX/GTKFONT.C [new file with mode: 0644]
putty/UNIX/GTKFONT.H [new file with mode: 0644]
putty/UNIX/GTKWIN.C [new file with mode: 0644]
putty/UNIX/MAKEFILE.GTK [new file with mode: 0644]
putty/UNIX/MAKEFILE.IN [new file with mode: 0644]
putty/UNIX/MAKEFILE.UX [new file with mode: 0644]
putty/UNIX/UNIX.H [new file with mode: 0644]
putty/UNIX/UXAGENTC.C [new file with mode: 0644]
putty/UNIX/UXCFG.C [new file with mode: 0644]
putty/UNIX/UXCONS.C [new file with mode: 0644]
putty/UNIX/UXGEN.C [new file with mode: 0644]
putty/UNIX/UXGSS.C [new file with mode: 0644]
putty/UNIX/UXMISC.C [new file with mode: 0644]
putty/UNIX/UXNET.C [new file with mode: 0644]
putty/UNIX/UXNOISE.C [new file with mode: 0644]
putty/UNIX/UXPLINK.C [new file with mode: 0644]
putty/UNIX/UXPRINT.C [new file with mode: 0644]
putty/UNIX/UXPROXY.C [new file with mode: 0644]
putty/UNIX/UXPTERM.C [new file with mode: 0644]
putty/UNIX/UXPTY.C [new file with mode: 0644]
putty/UNIX/UXPUTTY.C [new file with mode: 0644]
putty/UNIX/UXSEL.C [new file with mode: 0644]
putty/UNIX/UXSER.C [new file with mode: 0644]
putty/UNIX/UXSFTP.C [new file with mode: 0644]
putty/UNIX/UXSIGNAL.C [new file with mode: 0644]
putty/UNIX/UXSTORE.C [new file with mode: 0644]
putty/UNIX/UXUCS.C [new file with mode: 0644]
putty/UNIX/UX_X11.C [new file with mode: 0644]
putty/UNIX/XKEYSYM.C [new file with mode: 0644]
putty/UNIX/XPMPTCFG.C [new file with mode: 0644]
putty/UNIX/XPMPTERM.C [new file with mode: 0644]
putty/UNIX/XPMPUCFG.C [new file with mode: 0644]
putty/UNIX/XPMPUTTY.C [new file with mode: 0644]
putty/VERSION.C [new file with mode: 0644]
putty/WCWIDTH.C [new file with mode: 0644]
putty/WILDCARD.C [new file with mode: 0644]
putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV [new file with mode: 0644]
putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV [new file with mode: 0644]
putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV [new file with mode: 0644]
putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV [new file with mode: 0644]
putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV [new file with mode: 0644]
putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV [new file with mode: 0644]
putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV [new file with mode: 0644]
putty/WINDOWS/MAKEFILE.BOR [new file with mode: 0644]
putty/WINDOWS/MAKEFILE.CYG [new file with mode: 0644]
putty/WINDOWS/MAKEFILE.LCC [new file with mode: 0644]
putty/WINDOWS/MAKEFILE.VC [new file with mode: 0644]
putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP [new file with mode: 0644]
putty/WINDOWS/MSVC/PLINK/PLINK.DSP [new file with mode: 0644]
putty/WINDOWS/MSVC/PSCP/PSCP.DSP [new file with mode: 0644]
putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP [new file with mode: 0644]
putty/WINDOWS/MSVC/PUTTY.DSW [new file with mode: 0644]
putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP [new file with mode: 0644]
putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP [new file with mode: 0644]
putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP [new file with mode: 0644]
putty/WINDOWS/PAGEANT.ICO [new file with mode: 0644]
putty/WINDOWS/PAGEANT.MFT [new file with mode: 0644]
putty/WINDOWS/PAGEANT.RC [new file with mode: 0644]
putty/WINDOWS/PAGEANTS.ICO [new file with mode: 0644]
putty/WINDOWS/PLINK.RC [new file with mode: 0644]
putty/WINDOWS/PSCP.ICO [new file with mode: 0644]
putty/WINDOWS/PSCP.RC [new file with mode: 0644]
putty/WINDOWS/PSFTP.RC [new file with mode: 0644]
putty/WINDOWS/PUTTY.ICO [new file with mode: 0644]
putty/WINDOWS/PUTTY.ISS [new file with mode: 0644]
putty/WINDOWS/PUTTY.MFT [new file with mode: 0644]
putty/WINDOWS/PUTTY.RC [new file with mode: 0644]
putty/WINDOWS/PUTTYCFG.ICO [new file with mode: 0644]
putty/WINDOWS/PUTTYGEN.ICO [new file with mode: 0644]
putty/WINDOWS/PUTTYGEN.MFT [new file with mode: 0644]
putty/WINDOWS/PUTTYGEN.RC [new file with mode: 0644]
putty/WINDOWS/PUTTYINS.ICO [new file with mode: 0644]
putty/WINDOWS/PUTTYTEL.RC [new file with mode: 0644]
putty/WINDOWS/RCSTUFF.H [new file with mode: 0644]
putty/WINDOWS/README.TXT [new file with mode: 0644]
putty/WINDOWS/SIZETIP.C [new file with mode: 0644]
putty/WINDOWS/VERSION.RC2 [new file with mode: 0644]
putty/WINDOWS/WEBSITE.URL [new file with mode: 0644]
putty/WINDOWS/WINCFG.C [new file with mode: 0644]
putty/WINDOWS/WINCONS.C [new file with mode: 0644]
putty/WINDOWS/WINCTRLS.C [new file with mode: 0644]
putty/WINDOWS/WINDEFS.C [new file with mode: 0644]
putty/WINDOWS/WINDLG.C [new file with mode: 0644]
putty/WINDOWS/WINDOW.C [new file with mode: 0644]
putty/WINDOWS/WINGSS.C [new file with mode: 0644]
putty/WINDOWS/WINHANDL.C [new file with mode: 0644]
putty/WINDOWS/WINHELP.C [new file with mode: 0644]
putty/WINDOWS/WINHELP.H [new file with mode: 0644]
putty/WINDOWS/WINJUMP.C [new file with mode: 0644]
putty/WINDOWS/WINMISC.C [new file with mode: 0644]
putty/WINDOWS/WINNET.C [new file with mode: 0644]
putty/WINDOWS/WINNOISE.C [new file with mode: 0644]
putty/WINDOWS/WINNOJMP.C [new file with mode: 0644]
putty/WINDOWS/WINPGEN.C [new file with mode: 0644]
putty/WINDOWS/WINPGNT.C [new file with mode: 0644]
putty/WINDOWS/WINPGNTC.C [new file with mode: 0644]
putty/WINDOWS/WINPLINK.C [new file with mode: 0644]
putty/WINDOWS/WINPRINT.C [new file with mode: 0644]
putty/WINDOWS/WINPROXY.C [new file with mode: 0644]
putty/WINDOWS/WINSER.C [new file with mode: 0644]
putty/WINDOWS/WINSFTP.C [new file with mode: 0644]
putty/WINDOWS/WINSTORE.C [new file with mode: 0644]
putty/WINDOWS/WINSTUFF.H [new file with mode: 0644]
putty/WINDOWS/WINTIME.C [new file with mode: 0644]
putty/WINDOWS/WINUCS.C [new file with mode: 0644]
putty/WINDOWS/WINUTILS.C [new file with mode: 0644]
putty/WINDOWS/WINX11.C [new file with mode: 0644]
putty/WINDOWS/WIN_RES.H [new file with mode: 0644]
putty/WINDOWS/WIN_RES.RC2 [new file with mode: 0644]
putty/X11FWD.C [new file with mode: 0644]
putty/dllmain.c [new file with mode: 0644]
putty/stdafx.h [new file with mode: 0644]
putty/targetver.h [new file with mode: 0644]

index 1de1a58..6a7e1a6 100644 (file)
--- a/FFFTP.sln
+++ b/FFFTP.sln
@@ -4,6 +4,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FFFTP", "FFFTP.vcproj", "{5
 EndProject\r
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FFFTP_English", "FFFTP_English.vcproj", "{EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}"\r
 EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PuTTY", "putty\PuTTY.vc90.vcproj", "{AF1981EB-379B-43B8-BE66-298194297B5C}"\r
+EndProject\r
 Global\r
        GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
                Debug|Win32 = Debug|Win32\r
@@ -18,6 +20,10 @@ Global
                {EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}.Debug|Win32.Build.0 = Debug|Win32\r
                {EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}.Release|Win32.ActiveCfg = Release|Win32\r
                {EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}.Release|Win32.Build.0 = Release|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Debug|Win32.Build.0 = Debug|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Release|Win32.ActiveCfg = Release|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Release|Win32.Build.0 = Release|Win32\r
        EndGlobalSection\r
        GlobalSection(SolutionProperties) = preSolution\r
                HideSolutionNode = FALSE\r
index f73d409..34ca1c7 100644 (file)
@@ -4,6 +4,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FFFTP", "FFFTP.vc90.vcproj"
 EndProject\r
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FFFTP_English", "FFFTP_English.vc90.vcproj", "{EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}"\r
 EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PuTTY", "putty\PuTTY.vc90.vcproj", "{AF1981EB-379B-43B8-BE66-298194297B5C}"\r
+EndProject\r
 Global\r
        GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
                Debug|Win32 = Debug|Win32\r
@@ -18,6 +20,10 @@ Global
                {EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}.Debug|Win32.Build.0 = Debug|Win32\r
                {EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}.Release|Win32.ActiveCfg = Release|Win32\r
                {EE7CC1BD-92A9-46D5-8270-386BD7BEEA55}.Release|Win32.Build.0 = Release|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Debug|Win32.Build.0 = Debug|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Release|Win32.ActiveCfg = Release|Win32\r
+               {AF1981EB-379B-43B8-BE66-298194297B5C}.Release|Win32.Build.0 = Release|Win32\r
        EndGlobalSection\r
        GlobalSection(SolutionProperties) = preSolution\r
                HideSolutionNode = FALSE\r
index 53eb2cb..a479903 100644 (file)
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\punycode.c"\r
+                               RelativePath=".\protectprocess.c"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\protectprocess.c"\r
+                               RelativePath=".\punycode.c"\r
                                >\r
                        </File>\r
                        <File\r
index eb47506..d56ef28 100644 (file)
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\punycode.c"\r
+                               RelativePath=".\protectprocess.c"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\protectprocess.c"\r
+                               RelativePath=".\punycode.c"\r
                                >\r
                        </File>\r
                        <File\r
index b46c757..9ab70df 100644 (file)
Binary files a/FFFTP_Eng_Release/FFFTP.exe and b/FFFTP_Eng_Release/FFFTP.exe differ
index 9d26882..e1d0fb4 100644 (file)
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\punycode.c"\r
+                               RelativePath=".\protectprocess.c"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\protectprocess.c"\r
+                               RelativePath=".\punycode.c"\r
                                >\r
                        </File>\r
                        <File\r
index 37259ff..dcdc984 100644 (file)
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\punycode.c"\r
+                               RelativePath=".\protectprocess.c"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\protectprocess.c"\r
+                               RelativePath=".\punycode.c"\r
                                >\r
                        </File>\r
                        <File\r
index fb4deb7..303c723 100644 (file)
Binary files a/Release/FFFTP.exe and b/Release/FFFTP.exe differ
diff --git a/putty/BE_ALL.C b/putty/BE_ALL.C
new file mode 100644 (file)
index 0000000..05ba82d
--- /dev/null
@@ -0,0 +1,31 @@
+/*\r
+ * Linking module for PuTTY proper: list the available backends\r
+ * including ssh.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+/*\r
+ * This appname is not strictly in the right place, since Plink\r
+ * also uses this module. However, Plink doesn't currently use any\r
+ * of the dialog-box sorts of things that make use of appname, so\r
+ * it shouldn't do any harm here. I'm trying to avoid having to\r
+ * have tiny little source modules containing nothing but\r
+ * declarations of appname, for as long as I can...\r
+ */\r
+const char *const appname = "PuTTY";\r
+\r
+#ifdef TELNET_DEFAULT\r
+const int be_default_protocol = PROT_TELNET;\r
+#else\r
+const int be_default_protocol = PROT_SSH;\r
+#endif\r
+\r
+Backend *backends[] = {\r
+    &ssh_backend,\r
+    &telnet_backend,\r
+    &rlogin_backend,\r
+    &raw_backend,\r
+    NULL\r
+};\r
diff --git a/putty/BE_ALL_S.C b/putty/BE_ALL_S.C
new file mode 100644 (file)
index 0000000..95891fb
--- /dev/null
@@ -0,0 +1,32 @@
+/*\r
+ * Linking module for PuTTY proper: list the available backends\r
+ * including ssh, plus the serial backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+/*\r
+ * This appname is not strictly in the right place, since Plink\r
+ * also uses this module. However, Plink doesn't currently use any\r
+ * of the dialog-box sorts of things that make use of appname, so\r
+ * it shouldn't do any harm here. I'm trying to avoid having to\r
+ * have tiny little source modules containing nothing but\r
+ * declarations of appname, for as long as I can...\r
+ */\r
+const char *const appname = "PuTTY";\r
+\r
+#ifdef TELNET_DEFAULT\r
+const int be_default_protocol = PROT_TELNET;\r
+#else\r
+const int be_default_protocol = PROT_SSH;\r
+#endif\r
+\r
+Backend *backends[] = {\r
+    &ssh_backend,\r
+    &telnet_backend,\r
+    &rlogin_backend,\r
+    &raw_backend,\r
+    &serial_backend,\r
+    NULL\r
+};\r
diff --git a/putty/BE_NONE.C b/putty/BE_NONE.C
new file mode 100644 (file)
index 0000000..95ddbd5
--- /dev/null
@@ -0,0 +1,11 @@
+/*\r
+ * Linking module for programs that do not support selection of backend\r
+ * (such as pscp or pterm).\r
+ */\r
+\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+Backend *backends[] = {\r
+    NULL\r
+};\r
diff --git a/putty/BE_NOSSH.C b/putty/BE_NOSSH.C
new file mode 100644 (file)
index 0000000..e127cd9
--- /dev/null
@@ -0,0 +1,33 @@
+/*\r
+ * Linking module for PuTTYtel: list the available backends not\r
+ * including ssh.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+const int be_default_protocol = PROT_TELNET;\r
+\r
+const char *const appname = "PuTTYtel";\r
+\r
+Backend *backends[] = {\r
+    &telnet_backend,\r
+    &rlogin_backend,\r
+    &raw_backend,\r
+    NULL\r
+};\r
+\r
+/*\r
+ * Stub implementations of functions not used in non-ssh versions.\r
+ */\r
+void random_save_seed(void)\r
+{\r
+}\r
+\r
+void random_destroy_seed(void)\r
+{\r
+}\r
+\r
+void noise_ultralight(unsigned long data)\r
+{\r
+}\r
diff --git a/putty/BE_NOS_S.C b/putty/BE_NOS_S.C
new file mode 100644 (file)
index 0000000..32eb88c
--- /dev/null
@@ -0,0 +1,34 @@
+/*\r
+ * Linking module for PuTTYtel: list the available backends not\r
+ * including ssh.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+const int be_default_protocol = PROT_TELNET;\r
+\r
+const char *const appname = "PuTTYtel";\r
+\r
+Backend *backends[] = {\r
+    &telnet_backend,\r
+    &rlogin_backend,\r
+    &raw_backend,\r
+    &serial_backend,\r
+    NULL\r
+};\r
+\r
+/*\r
+ * Stub implementations of functions not used in non-ssh versions.\r
+ */\r
+void random_save_seed(void)\r
+{\r
+}\r
+\r
+void random_destroy_seed(void)\r
+{\r
+}\r
+\r
+void noise_ultralight(unsigned long data)\r
+{\r
+}\r
diff --git a/putty/BUILDSCR b/putty/BUILDSCR
new file mode 100644 (file)
index 0000000..131a734
--- /dev/null
@@ -0,0 +1,118 @@
+# -*- sh -*-\r
+# Build script to construct a full distribution directory of PuTTY.\r
+\r
+module putty\r
+\r
+# Set up the arguments for the main make command.\r
+set Makever -DSVN_REV=$(revision)\r
+ifneq "$(!numeric $(revision))" "yes" set Makever $(Makever) -DMODIFIED\r
+ifneq "$(RELEASE)" "" set Makever $(Makever) -DRELEASE=$(RELEASE)\r
+ifneq "$(date)" "" set Makever $(Makever) -DSNAPSHOT=$(date)\r
+set Makeargs VER="$(Makever)"\r
+ifneq "$(XFLAGS)" "" set Makeargs $(Makeargs) XFLAGS="$(XFLAGS)"\r
+ifneq "$(MAKEARGS)" "" set Makeargs $(Makeargs) $(MAKEARGS)\r
+\r
+# Set up the version string for the docs build.\r
+set Docmakeargs VERSION="PuTTY revision $(revision)"\r
+ifneq "$(RELEASE)" "" set Docmakeargs VERSION="PuTTY release $(RELEASE)"\r
+ifneq "$(date)" "" set Docmakeargs VERSION="PuTTY development snapshot $(date)"\r
+\r
+# Set up the version string for the Unix source archive.\r
+set Unxver r$(revision)\r
+ifneq "$(RELEASE)" "" set Unxver $(RELEASE)\r
+ifneq "$(date)" "" set Unxver $(date)\r
+\r
+# Set up the various version strings for the installer.\r
+set Iversion r$(revision)\r
+set Iname PuTTY revision $(revision)\r
+set Ivertext Revision $(revision)\r
+set Irev $(revision)\r
+set Ifilename putty-$(Iversion)-installer.exe\r
+ifneq "$(RELEASE)" "" set Iversion $(RELEASE)\r
+ifneq "$(RELEASE)" "" set Iname PuTTY version $(RELEASE)\r
+ifneq "$(RELEASE)" "" set Ivertext Release $(RELEASE)\r
+ifneq "$(RELEASE)" "" set Irev 0\r
+ifneq "$(RELEASE)" "" set Ifilename putty-$(RELEASE)-installer.exe\r
+ifneq "$(date)" "" set Iversion $(date):r$(revision)\r
+ifneq "$(date)" "" set Iname PuTTY development snapshot $(date):r$(revision)\r
+ifneq "$(date)" "" set Ivertext Development snapshot $(date):r$(revision)\r
+ifneq "$(date)" "" set Ifilename putty-$(date)-installer.exe\r
+\r
+# Set up the version string for the installer.\r
+set Iversion r$(revision)\r
+ifneq "$(RELEASE)" "" set Iversion $(RELEASE)\r
+ifneq "$(date)" "" set Iversion $(date):r$(revision)\r
+\r
+in putty do ./mksrcarc.sh\r
+in putty do ./mkunxarc.sh $(Unxver)\r
+in putty do perl mkfiles.pl\r
+in putty/doc do make $(Docmakeargs) putty.hlp\r
+in putty/doc do make $(Docmakeargs) chm\r
+\r
+# Munge the installer script locally so that it reports the version\r
+# we're really building.\r
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(AppVerName=).*$$/$$1$$a/' '$(Iname)' putty.iss\r
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(VersionInfoTextVersion=).*$$/$$1$$a/' '$(Ivertext)' putty.iss\r
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(AppVersion=).*$$/$$1$$a/' '$(Iversion)' putty.iss\r
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;$$a=~s/M//;}s/^(VersionInfoVersion=\d+\.\d+\.)\d+(\.\d+)\r?$$/$$1$$a$$2/' '$(Irev)' putty.iss\r
+\r
+# Windowsify LICENCE, since it's going in the Windows installer.\r
+in putty do perl -i~ -pe 'y/\015//d;s/$$/\015/' LICENCE\r
+\r
+delegate windows\r
+  # FIXME: Cygwin alternative?\r
+  in putty/windows do cmd /c vcvars32 \& nmake -f Makefile.vc $(Makeargs)\r
+  # Ignore exit code from hhc, in favour of seeing whether the .chm\r
+  # file was created. (Yuck; but hhc appears to return non-zero\r
+  # exit codes on whim.)\r
+  in putty/doc do hhc putty.hhp; test -f putty.chm\r
+  in putty/windows do iscc putty.iss\r
+  return putty/windows/*.exe\r
+  return putty/windows/*.map\r
+  return putty/doc/putty.chm\r
+  return putty/windows/Output/setup.exe\r
+enddelegate\r
+in putty/doc do make mostlyclean\r
+in putty/doc do make $(Docmakeargs)\r
+in putty/windows do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../doc/putty.chm ../doc/putty.hlp ../doc/putty.cnt\r
+in putty/doc do zip puttydoc.zip *.html\r
+\r
+# Deliver the actual PuTTY release directory into a subdir `putty'.\r
+deliver putty/windows/*.exe putty/x86/$@\r
+deliver putty/windows/putty.zip putty/x86/$@\r
+deliver putty/windows/Output/setup.exe putty/x86/$(Ifilename)\r
+deliver putty/doc/puttydoc.zip putty/$@\r
+deliver putty/doc/putty.chm putty/$@\r
+deliver putty/doc/putty.hlp putty/$@\r
+deliver putty/doc/putty.cnt putty/$@\r
+deliver putty/doc/puttydoc.txt putty/$@\r
+deliver putty/doc/*.html putty/htmldoc/$@\r
+deliver putty/putty-src.zip putty/$@\r
+deliver putty/*.tar.gz putty/$@\r
+\r
+# Deliver the map files alongside the `proper' release deliverables.\r
+deliver putty/windows/*.map maps-x86/$@\r
+\r
+# Deliver sign.sh, so that whoever has just built PuTTY (the\r
+# snapshot scripts or me, depending) can conveniently sign it with\r
+# whatever key they want.\r
+deliver putty/sign.sh $@\r
+\r
+# Create files of cryptographic checksums, which will be signed along\r
+# with the files they verify. We've provided MD5 checksums for a\r
+# while, but now MD5 is looking iffy, we're expanding our selection.\r
+#\r
+# Creating these files is most easily done in the destination\r
+# directory, where all the files we're delivering are already in their\r
+# final relative layout.\r
+in-dest putty do a=`\find * -type f -print`; md5sum $$a > md5sums && sha1sum $$a > sha1sums && sha256sum $$a > sha256sums && sha512sum $$a > sha512sums\r
+\r
+# And construct .htaccess files. One in the top-level directory,\r
+# setting the MIME types for Windows help files and providing an\r
+# appropriate link to the source archive:\r
+in-dest putty do echo "AddType application/octet-stream .chm" >> .htaccess\r
+in-dest putty do echo "AddType application/octet-stream .hlp" >> .htaccess\r
+in-dest putty do echo "AddType application/octet-stream .cnt" >> .htaccess\r
+in-dest putty do set -- putty*.tar.gz; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k\$$ '$$1'"$$1$$k" >> .htaccess; done\r
+# And one in the x86 directory, providing a link for the installer.\r
+in-dest putty/x86 do set -- putty*installer.exe; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty-installer.exe$$k\$$ '$$1'"$$1$$k" >> .htaccess; done\r
diff --git a/putty/CHARSET/CHARSET.H b/putty/CHARSET/CHARSET.H
new file mode 100644 (file)
index 0000000..3f7eb34
--- /dev/null
@@ -0,0 +1,154 @@
+/*\r
+ * charset.h - header file for general character set conversion\r
+ * routines.\r
+ */\r
+\r
+#ifndef charset_charset_h\r
+#define charset_charset_h\r
+\r
+#include <stddef.h>\r
+\r
+/*\r
+ * Enumeration that lists all the multibyte or single-byte\r
+ * character sets known to this library.\r
+ */\r
+typedef enum {\r
+    CS_NONE,                          /* used for reporting errors, etc */\r
+    CS_ISO8859_1,\r
+    CS_ISO8859_1_X11,                 /* X font encoding with VT100 glyphs */\r
+    CS_ISO8859_2,\r
+    CS_ISO8859_3,\r
+    CS_ISO8859_4,\r
+    CS_ISO8859_5,\r
+    CS_ISO8859_6,\r
+    CS_ISO8859_7,\r
+    CS_ISO8859_8,\r
+    CS_ISO8859_9,\r
+    CS_ISO8859_10,\r
+    CS_ISO8859_11,\r
+    CS_ISO8859_13,\r
+    CS_ISO8859_14,\r
+    CS_ISO8859_15,\r
+    CS_ISO8859_16,\r
+    CS_CP437,\r
+    CS_CP850,\r
+    CS_CP866,\r
+    CS_CP1250,\r
+    CS_CP1251,\r
+    CS_CP1252,\r
+    CS_CP1253,\r
+    CS_CP1254,\r
+    CS_CP1255,\r
+    CS_CP1256,\r
+    CS_CP1257,\r
+    CS_CP1258,\r
+    CS_KOI8_R,\r
+    CS_KOI8_U,\r
+    CS_MAC_ROMAN,\r
+    CS_MAC_TURKISH,\r
+    CS_MAC_CROATIAN,\r
+    CS_MAC_ICELAND,\r
+    CS_MAC_ROMANIAN,\r
+    CS_MAC_GREEK,\r
+    CS_MAC_CYRILLIC,\r
+    CS_MAC_THAI,\r
+    CS_MAC_CENTEURO,\r
+    CS_MAC_SYMBOL,\r
+    CS_MAC_DINGBATS,\r
+    CS_MAC_ROMAN_OLD,\r
+    CS_MAC_CROATIAN_OLD,\r
+    CS_MAC_ICELAND_OLD,\r
+    CS_MAC_ROMANIAN_OLD,\r
+    CS_MAC_GREEK_OLD,\r
+    CS_MAC_CYRILLIC_OLD,\r
+    CS_MAC_UKRAINE,\r
+    CS_MAC_VT100,\r
+    CS_MAC_VT100_OLD,\r
+    CS_VISCII,\r
+    CS_HP_ROMAN8,\r
+    CS_DEC_MCS,\r
+    CS_UTF8\r
+} charset_t;\r
+\r
+typedef struct {\r
+    unsigned long s0;\r
+} charset_state;\r
+\r
+/*\r
+ * Routine to convert a MB/SB character set to Unicode.\r
+ * \r
+ * This routine accepts some number of bytes, updates a state\r
+ * variable, and outputs some number of Unicode characters. There\r
+ * are no guarantees. You can't even guarantee that at most one\r
+ * Unicode character will be output per byte you feed in; for\r
+ * example, suppose you're reading UTF-8, you've seen E1 80, and\r
+ * then you suddenly see FE. Now you need to output _two_ error\r
+ * characters - one for the incomplete sequence E1 80, and one for\r
+ * the completely invalid UTF-8 byte FE.\r
+ * \r
+ * Returns the number of wide characters output; will never output\r
+ * more than the size of the buffer (as specified on input).\r
+ * Advances the `input' pointer and decrements `inlen', to indicate\r
+ * how far along the input string it got.\r
+ * \r
+ * The sequence of `errlen' wide characters pointed to by `errstr'\r
+ * will be used to indicate a conversion error. If `errstr' is\r
+ * NULL, `errlen' will be ignored, and the library will choose\r
+ * something sensible to do on its own. For Unicode, this will be\r
+ * U+FFFD (REPLACEMENT CHARACTER).\r
+ */\r
+\r
+int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen,\r
+                      int charset, charset_state *state,\r
+                      const wchar_t *errstr, int errlen);\r
+\r
+/*\r
+ * Routine to convert Unicode to an MB/SB character set.\r
+ * \r
+ * This routine accepts some number of Unicode characters, updates\r
+ * a state variable, and outputs some number of bytes.\r
+ * \r
+ * Returns the number of bytes characters output; will never output\r
+ * more than the size of the buffer (as specified on input), and\r
+ * will never output a partial MB character. Advances the `input'\r
+ * pointer and decrements `inlen', to indicate how far along the\r
+ * input string it got.\r
+ * \r
+ * The sequence of `errlen' characters pointed to by `errstr' will\r
+ * be used to indicate a conversion error. If `errstr' is NULL,\r
+ * `errlen' will be ignored, and the library will choose something\r
+ * sensible to do on its own (which will vary depending on the\r
+ * output charset).\r
+ */\r
+\r
+int charset_from_unicode(wchar_t **input, int *inlen, char *output, int outlen,\r
+                        int charset, charset_state *state,\r
+                        const char *errstr, int errlen);\r
+\r
+/*\r
+ * Convert X11 encoding names to and from our charset identifiers.\r
+ */\r
+const char *charset_to_xenc(int charset);\r
+int charset_from_xenc(const char *name);\r
+\r
+/*\r
+ * Convert MIME encoding names to and from our charset identifiers.\r
+ */\r
+const char *charset_to_mimeenc(int charset);\r
+int charset_from_mimeenc(const char *name);\r
+\r
+/*\r
+ * Convert our own encoding names to and from our charset\r
+ * identifiers.\r
+ */\r
+const char *charset_to_localenc(int charset);\r
+int charset_from_localenc(const char *name);\r
+int charset_localenc_nth(int n);\r
+\r
+/*\r
+ * Convert Mac OS script/region/font to our charset identifiers.\r
+ */\r
+int charset_from_macenc(int script, int region, int sysvers,\r
+                       const char *fontname);\r
+\r
+#endif /* charset_charset_h */\r
diff --git a/putty/CHARSET/ENUM.C b/putty/CHARSET/ENUM.C
new file mode 100644 (file)
index 0000000..4c559be
--- /dev/null
@@ -0,0 +1,19 @@
+/*\r
+ * enum.c - enumerate all charsets defined by the library.\r
+ * \r
+ * This file maintains a list of every other source file which\r
+ * contains ENUM_CHARSET definitions. It #includes each one with\r
+ * ENUM_CHARSETS defined, which causes those source files to do\r
+ * nothing at all except call the ENUM_CHARSET macro on each\r
+ * charset they define.\r
+ * \r
+ * This file in turn is included from various other places, with\r
+ * the ENUM_CHARSET macro defined to various different things. This\r
+ * allows us to have multiple implementations of the master charset\r
+ * lookup table (a static one and a dynamic one).\r
+ */\r
+\r
+#define ENUM_CHARSETS\r
+#include "sbcsdat.c"\r
+#include "utf8.c"\r
+#undef ENUM_CHARSETS\r
diff --git a/putty/CHARSET/FROMUCS.C b/putty/CHARSET/FROMUCS.C
new file mode 100644 (file)
index 0000000..ce69cd7
--- /dev/null
@@ -0,0 +1,91 @@
+/*\r
+ * fromucs.c - convert Unicode to other character sets.\r
+ */\r
+\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+struct charset_emit_param {\r
+    char *output;\r
+    int outlen;\r
+    const char *errstr;\r
+    int errlen;\r
+    int stopped;\r
+};\r
+\r
+static void charset_emit(void *ctx, long int output)\r
+{\r
+    struct charset_emit_param *param = (struct charset_emit_param *)ctx;\r
+    char outval;\r
+    char const *p;\r
+    int outlen;\r
+\r
+    if (output == ERROR) {\r
+       p = param->errstr;\r
+       outlen = param->errlen;\r
+    } else {\r
+       outval = output;\r
+       p = &outval;\r
+       outlen = 1;\r
+    }\r
+\r
+    if (param->outlen >= outlen) {\r
+       while (outlen > 0) {\r
+           *param->output++ = *p++;\r
+           param->outlen--;\r
+           outlen--;\r
+       }\r
+    } else {\r
+       param->stopped = 1;\r
+    }\r
+}\r
+\r
+int charset_from_unicode(wchar_t **input, int *inlen, char *output, int outlen,\r
+                        int charset, charset_state *state,\r
+                        const char *errstr, int errlen)\r
+{\r
+    charset_spec const *spec = charset_find_spec(charset);\r
+    charset_state localstate;\r
+    struct charset_emit_param param;\r
+\r
+    param.output = output;\r
+    param.outlen = outlen;\r
+    param.stopped = 0;\r
+\r
+    /*\r
+     * charset_emit will expect a valid errstr.\r
+     */\r
+    if (!errstr) {\r
+       /* *shrug* this is good enough, and consistent across all SBCS... */\r
+       param.errstr = ".";\r
+       param.errlen = 1;\r
+    }\r
+    param.errstr = errstr;\r
+    param.errlen = errlen;\r
+\r
+    if (!state) {\r
+       localstate.s0 = 0;\r
+    } else {\r
+       localstate = *state;           /* structure copy */\r
+    }\r
+    state = &localstate;\r
+\r
+    while (*inlen > 0) {\r
+       int lenbefore = param.output - output;\r
+       spec->write(spec, **input, &localstate, charset_emit, &param);\r
+       if (param.stopped) {\r
+           /*\r
+            * The emit function has _tried_ to output some\r
+            * characters, but ran up against the end of the\r
+            * buffer. Leave immediately, and return what happened\r
+            * _before_ attempting to process this character.\r
+            */\r
+           return lenbefore;\r
+       }\r
+       if (state)\r
+           *state = localstate;       /* structure copy */\r
+       (*input)++;\r
+       (*inlen)--;\r
+    }\r
+    return param.output - output;\r
+}\r
diff --git a/putty/CHARSET/INTERNAL.H b/putty/CHARSET/INTERNAL.H
new file mode 100644 (file)
index 0000000..683b8a6
--- /dev/null
@@ -0,0 +1,89 @@
+/*\r
+ * internal.h - internal header stuff for the charset library.\r
+ */\r
+\r
+#ifndef charset_internal_h\r
+#define charset_internal_h\r
+\r
+/* This invariably comes in handy */\r
+#define lenof(x) ( sizeof((x)) / sizeof(*(x)) )\r
+\r
+/* This is an invalid Unicode value used to indicate an error. */\r
+#define ERROR 0xFFFFL                 /* Unicode value representing error */\r
+\r
+typedef struct charset_spec charset_spec;\r
+typedef struct sbcs_data sbcs_data;\r
+\r
+struct charset_spec {\r
+    int charset;                      /* numeric identifier */\r
+\r
+    /*\r
+     * A function to read the character set and output Unicode\r
+     * characters. The `emit' function expects to get Unicode chars\r
+     * passed to it; it should be sent ERROR for any encoding error\r
+     * on the input.\r
+     */\r
+    void (*read)(charset_spec const *charset, long int input_chr,\r
+                charset_state *state,\r
+                void (*emit)(void *ctx, long int output), void *emitctx);\r
+    /*\r
+     * A function to read Unicode characters and output in this\r
+     * character set. The `emit' function expects to get byte\r
+     * values passed to it; it should be sent ERROR for any\r
+     * non-representable characters on the input.\r
+     */\r
+    void (*write)(charset_spec const *charset, long int input_chr,\r
+                 charset_state *state,\r
+                 void (*emit)(void *ctx, long int output), void *emitctx);\r
+    void const *data;\r
+};\r
+\r
+/*\r
+ * This is the format of `data' used by the SBCS read and write\r
+ * functions; so it's the format used in all SBCS definitions.\r
+ */\r
+struct sbcs_data {\r
+    /*\r
+     * This is a simple mapping table converting each SBCS position\r
+     * to a Unicode code point. Some positions may contain ERROR,\r
+     * indicating that that byte value is not defined in the SBCS\r
+     * in question and its occurrence in input is an error.\r
+     */\r
+    unsigned long sbcs2ucs[256];\r
+\r
+    /*\r
+     * This lookup table is used to convert Unicode back to the\r
+     * SBCS. It consists of the valid byte values in the SBCS,\r
+     * sorted in order of their Unicode translation. So given a\r
+     * Unicode value U, you can do a binary search on this table\r
+     * using the above table as a lookup: when testing the Xth\r
+     * position in this table, you branch according to whether\r
+     * sbcs2ucs[ucs2sbcs[X]] is less than, greater than, or equal\r
+     * to U.\r
+     * \r
+     * Note that since there may be fewer than 256 valid byte\r
+     * values in a particular SBCS, we must supply the length of\r
+     * this table as well as the contents.\r
+     */\r
+    unsigned char ucs2sbcs[256];\r
+    int nvalid;\r
+};\r
+\r
+/*\r
+ * Prototypes for internal library functions.\r
+ */\r
+charset_spec const *charset_find_spec(int charset);\r
+void read_sbcs(charset_spec const *charset, long int input_chr,\r
+              charset_state *state,\r
+              void (*emit)(void *ctx, long int output), void *emitctx);\r
+void write_sbcs(charset_spec const *charset, long int input_chr,\r
+               charset_state *state,\r
+               void (*emit)(void *ctx, long int output), void *emitctx);\r
+\r
+/*\r
+ * Placate compiler warning about unused parameters, of which we\r
+ * expect to have some in this library.\r
+ */\r
+#define UNUSEDARG(x) ( (x) = (x) )\r
+\r
+#endif /* charset_internal_h */\r
diff --git a/putty/CHARSET/LOCALENC.C b/putty/CHARSET/LOCALENC.C
new file mode 100644 (file)
index 0000000..9e51f72
--- /dev/null
@@ -0,0 +1,125 @@
+/*\r
+ * local.c - translate our internal character set codes to and from\r
+ * our own set of plausibly legible character-set names. Also\r
+ * provides a canonical name for each encoding (useful for software\r
+ * announcing what character set it will be using), and a set of\r
+ * enumeration functions which return a list of supported\r
+ * encodings one by one.\r
+ * \r
+ * charset_from_localenc will attempt all other text translations\r
+ * as well as this table, to maximise the number of different ways\r
+ * you can select a supported charset.\r
+ */\r
+\r
+#include <ctype.h>\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+static const struct {\r
+    const char *name;\r
+    int charset;\r
+    int return_in_enum;   /* enumeration misses some charsets */\r
+} localencs[] = {\r
+    { "<UNKNOWN>", CS_NONE, 0 },\r
+    { "ISO-8859-1", CS_ISO8859_1, 1 },\r
+    { "ISO-8859-1 with X11 line drawing", CS_ISO8859_1_X11, 0 },\r
+    { "ISO-8859-2", CS_ISO8859_2, 1 },\r
+    { "ISO-8859-3", CS_ISO8859_3, 1 },\r
+    { "ISO-8859-4", CS_ISO8859_4, 1 },\r
+    { "ISO-8859-5", CS_ISO8859_5, 1 },\r
+    { "ISO-8859-6", CS_ISO8859_6, 1 },\r
+    { "ISO-8859-7", CS_ISO8859_7, 1 },\r
+    { "ISO-8859-8", CS_ISO8859_8, 1 },\r
+    { "ISO-8859-9", CS_ISO8859_9, 1 },\r
+    { "ISO-8859-10", CS_ISO8859_10, 1 },\r
+    { "ISO-8859-11", CS_ISO8859_11, 1 },\r
+    { "ISO-8859-13", CS_ISO8859_13, 1 },\r
+    { "ISO-8859-14", CS_ISO8859_14, 1 },\r
+    { "ISO-8859-15", CS_ISO8859_15, 1 },\r
+    { "ISO-8859-16", CS_ISO8859_16, 1 },\r
+    { "CP437", CS_CP437, 1 },\r
+    { "CP850", CS_CP850, 1 },\r
+    { "CP866", CS_CP866, 1 },\r
+    { "CP1250", CS_CP1250, 1 },\r
+    { "CP1251", CS_CP1251, 1 },\r
+    { "CP1252", CS_CP1252, 1 },\r
+    { "CP1253", CS_CP1253, 1 },\r
+    { "CP1254", CS_CP1254, 1 },\r
+    { "CP1255", CS_CP1255, 1 },\r
+    { "CP1256", CS_CP1256, 1 },\r
+    { "CP1257", CS_CP1257, 1 },\r
+    { "CP1258", CS_CP1258, 1 },\r
+    { "KOI8-R", CS_KOI8_R, 1 },\r
+    { "KOI8-U", CS_KOI8_U, 1 },\r
+    { "Mac Roman", CS_MAC_ROMAN, 1 },\r
+    { "Mac Turkish", CS_MAC_TURKISH, 1 },\r
+    { "Mac Croatian", CS_MAC_CROATIAN, 1 },\r
+    { "Mac Iceland", CS_MAC_ICELAND, 1 },\r
+    { "Mac Romanian", CS_MAC_ROMANIAN, 1 },\r
+    { "Mac Greek", CS_MAC_GREEK, 1 },\r
+    { "Mac Cyrillic", CS_MAC_CYRILLIC, 1 },\r
+    { "Mac Thai", CS_MAC_THAI, 1 },\r
+    { "Mac Centeuro", CS_MAC_CENTEURO, 1 },\r
+    { "Mac Symbol", CS_MAC_SYMBOL, 1 },\r
+    { "Mac Dingbats", CS_MAC_DINGBATS, 1 },\r
+    { "Mac Roman (old)", CS_MAC_ROMAN_OLD, 0 },\r
+    { "Mac Croatian (old)", CS_MAC_CROATIAN_OLD, 0 },\r
+    { "Mac Iceland (old)", CS_MAC_ICELAND_OLD, 0 },\r
+    { "Mac Romanian (old)", CS_MAC_ROMANIAN_OLD, 0 },\r
+    { "Mac Greek (old)", CS_MAC_GREEK_OLD, 0 },\r
+    { "Mac Cyrillic (old)", CS_MAC_CYRILLIC_OLD, 0 },\r
+    { "Mac Ukraine", CS_MAC_UKRAINE, 1 },\r
+    { "Mac VT100", CS_MAC_VT100, 1 },\r
+    { "Mac VT100 (old)", CS_MAC_VT100_OLD, 0 },\r
+    { "VISCII", CS_VISCII, 1 },\r
+    { "HP ROMAN8", CS_HP_ROMAN8, 1 },\r
+    { "DEC MCS", CS_DEC_MCS, 1 },\r
+    { "UTF-8", CS_UTF8, 1 },\r
+};\r
+\r
+const char *charset_to_localenc(int charset)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(localencs); i++)\r
+       if (charset == localencs[i].charset)\r
+           return localencs[i].name;\r
+\r
+    return NULL;                      /* not found */\r
+}\r
+\r
+int charset_from_localenc(const char *name)\r
+{\r
+    int i;\r
+\r
+    if ( (i = charset_from_mimeenc(name)) != CS_NONE)\r
+       return i;\r
+    if ( (i = charset_from_xenc(name)) != CS_NONE)\r
+       return i;\r
+\r
+    for (i = 0; i < (int)lenof(localencs); i++) {\r
+       const char *p, *q;\r
+       p = name;\r
+       q = localencs[i].name;\r
+       while (*p || *q) {\r
+               if (tolower((unsigned char)*p) != tolower((unsigned char)*q))\r
+               break;\r
+           p++; q++;\r
+       }\r
+       if (!*p && !*q)\r
+           return localencs[i].charset;\r
+    }\r
+\r
+    return CS_NONE;                   /* not found */\r
+}\r
+\r
+int charset_localenc_nth(int n)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(localencs); i++)\r
+       if (localencs[i].return_in_enum && !n--)\r
+           return localencs[i].charset;\r
+\r
+    return CS_NONE;                   /* end of list */\r
+}\r
diff --git a/putty/CHARSET/MACENC.C b/putty/CHARSET/MACENC.C
new file mode 100644 (file)
index 0000000..a6d9aab
--- /dev/null
@@ -0,0 +1,169 @@
+/* $Id: macenc.c 8037 2008-06-04 23:05:48Z simon $ */\r
+/*\r
+ * Copyright (c) 2003 Ben Harris\r
+ * All rights reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person\r
+ * obtaining a copy of this software and associated documentation\r
+ * files (the "Software"), to deal in the Software without\r
+ * restriction, including without limitation the rights to use,\r
+ * copy, modify, merge, publish, distribute, sublicense, and/or\r
+ * sell copies of the Software, and to permit persons to whom the\r
+ * Software is furnished to do so, subject to the following\r
+ * conditions:\r
+ * \r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ * \r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+/*\r
+ * macenc.c -- Convert a Mac OS script/region/font combination to our\r
+ * internal charset code.\r
+ */\r
+\r
+#include <string.h>\r
+\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+/*\r
+ * These are defined by Mac OS's <Script.h>, but we'd like to be\r
+ * independent of that.\r
+ */\r
+\r
+#define smRoman                        0\r
+#define smJapanese             1\r
+#define smTradChinese          2\r
+#define smKorean               3\r
+#define smArabic               4\r
+#define smHebrew               5\r
+#define smCyrillic             7\r
+#define smDevenagari           9\r
+#define smGurmukhi             10\r
+#define smGujurati             11\r
+#define smThai                 21\r
+#define smSimpChinese          25\r
+#define smTibetan              26\r
+#define smEthiopic             28\r
+#define smCentralEuroRoman     29\r
+\r
+#define verGreece              20\r
+#define verIceland             21\r
+#define verTurkey              24\r
+#define verYugoCroatian                25\r
+#define verRomania             39\r
+#define verFaroeIsl            47\r
+#define verIran                        48\r
+#define verRussia              49\r
+#define verSlovenian           66\r
+#define verCroatia             68\r
+#define verBulgaria            72\r
+#define verScottishGaelic      75\r
+#define verManxGaelic          76\r
+#define verBreton              77\r
+#define verNunavut             78\r
+#define verWelsh               79\r
+#define verIrishGaelicScript   81\r
+\r
+static const struct {\r
+    int script;\r
+    int region;\r
+    int sysvermin;\r
+    char const *fontname;\r
+    int charset;\r
+} macencs[] = {\r
+    { smRoman, -1,                   0x850, "VT100", CS_MAC_VT100 },\r
+    { smRoman, -1,                   0,     "VT100", CS_MAC_VT100_OLD },\r
+    /*\r
+     * From here on, this table is largely derived from\r
+     * <http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/README.TXT>,\r
+     * with _OLD version added based on the comments in individual\r
+     * mapping files.\r
+     */\r
+    { smRoman, -1,                   0,     "Symbol", CS_MAC_SYMBOL },\r
+    { smRoman, -1,                   0,     "Zapf Dingbats", CS_MAC_DINGBATS },\r
+    { smRoman, verTurkey,            0,     NULL,    CS_MAC_TURKISH },\r
+    { smRoman, verYugoCroatian,      0x850, NULL,    CS_MAC_CROATIAN },\r
+    { smRoman, verYugoCroatian,      0,     NULL,    CS_MAC_CROATIAN_OLD },\r
+    { smRoman, verSlovenian,         0x850, NULL,    CS_MAC_CROATIAN },\r
+    { smRoman, verSlovenian,         0,     NULL,    CS_MAC_CROATIAN_OLD },\r
+    { smRoman, verCroatia,           0x850, NULL,    CS_MAC_CROATIAN },\r
+    { smRoman, verCroatia,           0,     NULL,    CS_MAC_CROATIAN_OLD },\r
+    { smRoman, verIceland,           0x850, NULL,    CS_MAC_ICELAND },\r
+    { smRoman, verIceland,           0,     NULL,    CS_MAC_ICELAND_OLD },\r
+    { smRoman, verFaroeIsl,          0x850, NULL,    CS_MAC_ICELAND },\r
+    { smRoman, verFaroeIsl,          0,     NULL,    CS_MAC_ICELAND_OLD },\r
+    { smRoman, verRomania,           0x850, NULL,    CS_MAC_ROMANIAN },\r
+    { smRoman, verRomania,           0,     NULL,    CS_MAC_ROMANIAN_OLD },\r
+#if 0 /* No mapping table on ftp.unicode.org */\r
+    { smRoman, verIreland,           0x850, NULL,    CS_MAC_CELTIC },\r
+    { smRoman, verIreland,           0,     NULL,    CS_MAC_CELTIC_OLD },\r
+    { smRoman, verScottishGaelic,    0x850, NULL,    CS_MAC_CELTIC },\r
+    { smRoman, verScottishGaelic,    0,     NULL,    CS_MAC_CELTIC_OLD },\r
+    { smRoman, verManxGaelic,        0x850, NULL,    CS_MAC_CELTIC },\r
+    { smRoman, verManxGaelic,        0,     NULL,    CS_MAC_CELTIC_OLD },\r
+    { smRoman, verBreton,            0x850, NULL,    CS_MAC_CELTIC },\r
+    { smRoman, verBreton,            0,     NULL,    CS_MAC_CELTIC_OLD },\r
+    { smRoman, verWelsh,             0x850, NULL,    CS_MAC_CELTIC },\r
+    { smRoman, verWelsh,             0,     NULL,    CS_MAC_CELTIC_OLD },\r
+    { smRoman, verIrishGaelicScript, 0x850, NULL,    CS_MAC_GAELIC },\r
+    { smRoman, verIrishGaelicScript, 0,     NULL,    CS_MAC_GAELIC_OLD },\r
+#endif\r
+    { smRoman, verGreece,            0x922, NULL,    CS_MAC_GREEK },\r
+    { smRoman, verGreece,            0,     NULL,    CS_MAC_GREEK_OLD },\r
+    { smRoman, -1,                   0x850, NULL,    CS_MAC_ROMAN },\r
+    { smRoman, -1,                   0,     NULL,    CS_MAC_ROMAN_OLD },\r
+#if 0 /* Multi-byte encodings, not yet supported */\r
+    { smJapanese,    -1,             0,     NULL,    CS_MAC_JAPANESE },\r
+    { smTradChinese, -1,             0,     NULL,    CS_MAC_CHINTRAD },\r
+    { smKorean,      -1,             0,     NULL,    CS_MAC_KOREAN },\r
+#endif\r
+#if 0 /* Bidirectional encodings, not yet supported */\r
+    { smArabic, verIran,             0,     NULL,    CS_MAC_FARSI },\r
+    { smArabic, -1,                  0,     NULL,    CS_MAC_ARABIC },\r
+    { smHebrew, -1,                  0,     NULL,    CS_MAC_HEBREW },\r
+#endif\r
+    { smCyrillic, -1,                0x900, NULL,    CS_MAC_CYRILLIC },\r
+    { smCyrillic, verRussia,         0,     NULL,    CS_MAC_CYRILLIC_OLD },\r
+    { smCyrillic, verBulgaria,       0,     NULL,    CS_MAC_CYRILLIC_OLD },\r
+    { smCyrillic, -1,                0,     NULL,    CS_MAC_UKRAINE },\r
+#if 0 /* Complex Indic scripts, not yet supported */\r
+    { smDevanagari, -1,              0,     NULL,    CS_MAC_DEVENAGA },\r
+    { smGurmukhi, -1,                0,     NULL,    CS_MAC_GURMUKHI },\r
+    { smGujurati, -1,                0,     NULL,    CS_MAC_GUJURATI },\r
+#endif\r
+    { smThai,  -1,                   0,     NULL,    CS_MAC_THAI },\r
+#if 0 /* Multi-byte encoding, not yet supported */\r
+    { smSimpChinese, -1,             0,     NULL,    CS_MAC_CHINSIMP },\r
+#endif\r
+#if 0 /* No mapping table on ftp.unicode.org */\r
+    { smTibetan, -1,                 0,     NULL,    CS_MAC_TIBETAN },\r
+    { smEthiopic, -1,                0,     NULL,    CS_MAC_ETHIOPIC },\r
+    { smEthiopic, verNanavut,        0,     NULL,    CS_MAC_INUIT },\r
+#endif\r
+    { smCentralEuroRoman, -1,        0,     NULL,    CS_MAC_CENTEURO },\r
+};\r
+\r
+int charset_from_macenc(int script, int region, int sysvers,\r
+                       char const *fontname)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(macencs); i++)\r
+       if ((macencs[i].script == script) &&\r
+           (macencs[i].region < 0 || macencs[i].region == region) &&\r
+           (macencs[i].sysvermin <= sysvers) &&\r
+           (macencs[i].fontname == NULL ||\r
+            (fontname != NULL && strcmp(macencs[i].fontname, fontname) == 0)))\r
+           return macencs[i].charset;\r
+\r
+    return CS_NONE;\r
+}\r
diff --git a/putty/CHARSET/MIMEENC.C b/putty/CHARSET/MIMEENC.C
new file mode 100644 (file)
index 0000000..27b860a
--- /dev/null
@@ -0,0 +1,214 @@
+/*\r
+ * mimeenc.c - translate our internal character set codes to and\r
+ * from MIME standard character-set names.\r
+ * \r
+ */\r
+\r
+#include <ctype.h>\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+static const struct {\r
+    const char *name;\r
+    int charset;\r
+} mimeencs[] = {\r
+    /*\r
+     * These names are taken from\r
+     * \r
+     *   http://www.iana.org/assignments/character-sets\r
+     * \r
+     * Where multiple encoding names map to the same encoding id\r
+     * (such as the variety of aliases for ISO-8859-1), the first\r
+     * is considered canonical and will be returned when\r
+     * translating the id to a string.\r
+     */\r
+    { "ISO-8859-1", CS_ISO8859_1 },\r
+    { "iso-ir-100", CS_ISO8859_1 },\r
+    { "ISO_8859-1", CS_ISO8859_1 },\r
+    { "ISO_8859-1:1987", CS_ISO8859_1 },\r
+    { "latin1", CS_ISO8859_1 },\r
+    { "l1", CS_ISO8859_1 },\r
+    { "IBM819", CS_ISO8859_1 },\r
+    { "CP819", CS_ISO8859_1 },\r
+    { "csISOLatin1", CS_ISO8859_1 },\r
+\r
+    { "ISO-8859-2", CS_ISO8859_2 },\r
+    { "ISO_8859-2:1987", CS_ISO8859_2 },\r
+    { "iso-ir-101", CS_ISO8859_2 },\r
+    { "ISO_8859-2", CS_ISO8859_2 },\r
+    { "latin2", CS_ISO8859_2 },\r
+    { "l2", CS_ISO8859_2 },\r
+    { "csISOLatin2", CS_ISO8859_2 },\r
+\r
+    { "ISO-8859-3", CS_ISO8859_3 },\r
+    { "ISO_8859-3:1988", CS_ISO8859_3 },\r
+    { "iso-ir-109", CS_ISO8859_3 },\r
+    { "ISO_8859-3", CS_ISO8859_3 },\r
+    { "latin3", CS_ISO8859_3 },\r
+    { "l3", CS_ISO8859_3 },\r
+    { "csISOLatin3", CS_ISO8859_3 },\r
+\r
+    { "ISO-8859-4", CS_ISO8859_4 },\r
+    { "ISO_8859-4:1988", CS_ISO8859_4 },\r
+    { "iso-ir-110", CS_ISO8859_4 },\r
+    { "ISO_8859-4", CS_ISO8859_4 },\r
+    { "latin4", CS_ISO8859_4 },\r
+    { "l4", CS_ISO8859_4 },\r
+    { "csISOLatin4", CS_ISO8859_4 },\r
+\r
+    { "ISO-8859-5", CS_ISO8859_5 },\r
+    { "ISO_8859-5:1988", CS_ISO8859_5 },\r
+    { "iso-ir-144", CS_ISO8859_5 },\r
+    { "ISO_8859-5", CS_ISO8859_5 },\r
+    { "cyrillic", CS_ISO8859_5 },\r
+    { "csISOLatinCyrillic", CS_ISO8859_5 },\r
+\r
+    { "ISO-8859-6", CS_ISO8859_6 },\r
+    { "ISO_8859-6:1987", CS_ISO8859_6 },\r
+    { "iso-ir-127", CS_ISO8859_6 },\r
+    { "ISO_8859-6", CS_ISO8859_6 },\r
+    { "ECMA-114", CS_ISO8859_6 },\r
+    { "ASMO-708", CS_ISO8859_6 },\r
+    { "arabic", CS_ISO8859_6 },\r
+    { "csISOLatinArabic", CS_ISO8859_6 },\r
+\r
+    { "ISO-8859-7", CS_ISO8859_7 },\r
+    { "ISO_8859-7:1987", CS_ISO8859_7 },\r
+    { "iso-ir-126", CS_ISO8859_7 },\r
+    { "ISO_8859-7", CS_ISO8859_7 },\r
+    { "ELOT_928", CS_ISO8859_7 },\r
+    { "ECMA-118", CS_ISO8859_7 },\r
+    { "greek", CS_ISO8859_7 },\r
+    { "greek8", CS_ISO8859_7 },\r
+    { "csISOLatinGreek", CS_ISO8859_7 },\r
+\r
+    { "ISO-8859-8", CS_ISO8859_8 },\r
+    { "ISO_8859-8:1988", CS_ISO8859_8 },\r
+    { "iso-ir-138", CS_ISO8859_8 },\r
+    { "ISO_8859-8", CS_ISO8859_8 },\r
+    { "hebrew", CS_ISO8859_8 },\r
+    { "csISOLatinHebrew", CS_ISO8859_8 },\r
+\r
+    { "ISO-8859-9", CS_ISO8859_9 },\r
+    { "ISO_8859-9:1989", CS_ISO8859_9 },\r
+    { "iso-ir-148", CS_ISO8859_9 },\r
+    { "ISO_8859-9", CS_ISO8859_9 },\r
+    { "latin5", CS_ISO8859_9 },\r
+    { "l5", CS_ISO8859_9 },\r
+    { "csISOLatin5", CS_ISO8859_9 },\r
+\r
+    { "ISO-8859-10", CS_ISO8859_10 },\r
+    { "iso-ir-157", CS_ISO8859_10 },\r
+    { "l6", CS_ISO8859_10 },\r
+    { "ISO_8859-10:1992", CS_ISO8859_10 },\r
+    { "csISOLatin6", CS_ISO8859_10 },\r
+    { "latin6", CS_ISO8859_10 },\r
+\r
+    { "ISO-8859-13", CS_ISO8859_13 },\r
+\r
+    { "ISO-8859-14", CS_ISO8859_14 },\r
+    { "iso-ir-199", CS_ISO8859_14 },\r
+    { "ISO_8859-14:1998", CS_ISO8859_14 },\r
+    { "ISO_8859-14", CS_ISO8859_14 },\r
+    { "latin8", CS_ISO8859_14 },\r
+    { "iso-celtic", CS_ISO8859_14 },\r
+    { "l8", CS_ISO8859_14 },\r
+\r
+    { "ISO-8859-15", CS_ISO8859_15 },\r
+    { "ISO_8859-15", CS_ISO8859_15 },\r
+    { "Latin-9", CS_ISO8859_15 },\r
+\r
+    { "ISO-8859-16", CS_ISO8859_16 },\r
+    { "iso-ir-226", CS_ISO8859_16 },\r
+    { "ISO_8859-16", CS_ISO8859_16 },\r
+    { "ISO_8859-16:2001", CS_ISO8859_16 },\r
+    { "latin10", CS_ISO8859_16 },\r
+    { "l10", CS_ISO8859_16 },\r
+\r
+    { "IBM437", CS_CP437 },\r
+    { "cp437", CS_CP437 },\r
+    { "437", CS_CP437 },\r
+    { "csPC8CodePage437", CS_CP437 },\r
+\r
+    { "IBM850", CS_CP850 },\r
+    { "cp850", CS_CP850 },\r
+    { "850", CS_CP850 },\r
+    { "csPC850Multilingual", CS_CP850 },\r
+\r
+    { "IBM866", CS_CP866 },\r
+    { "cp866", CS_CP866 },\r
+    { "866", CS_CP866 },\r
+    { "csIBM866", CS_CP866 },\r
+\r
+    { "windows-1250", CS_CP1250 },\r
+\r
+    { "windows-1251", CS_CP1251 },\r
+\r
+    { "windows-1252", CS_CP1252 },\r
+\r
+    { "windows-1253", CS_CP1253 },\r
+\r
+    { "windows-1254", CS_CP1254 },\r
+\r
+    { "windows-1255", CS_CP1255 },\r
+\r
+    { "windows-1256", CS_CP1256 },\r
+\r
+    { "windows-1257", CS_CP1257 },\r
+\r
+    { "windows-1258", CS_CP1258 },\r
+\r
+    { "KOI8-R", CS_KOI8_R },\r
+    { "csKOI8R", CS_KOI8_R },\r
+\r
+    { "KOI8-U", CS_KOI8_U },\r
+\r
+    { "macintosh", CS_MAC_ROMAN_OLD },\r
+    { "mac", CS_MAC_ROMAN_OLD },\r
+    { "csMacintosh", CS_MAC_ROMAN_OLD },\r
+\r
+    { "VISCII", CS_VISCII },\r
+    { "csVISCII", CS_VISCII },\r
+\r
+    { "hp-roman8", CS_HP_ROMAN8 },\r
+    { "roman8", CS_HP_ROMAN8 },\r
+    { "r8", CS_HP_ROMAN8 },\r
+    { "csHPRoman8", CS_HP_ROMAN8 },\r
+\r
+    { "DEC-MCS", CS_DEC_MCS },\r
+    { "dec", CS_DEC_MCS },\r
+    { "csDECMCS", CS_DEC_MCS },\r
+\r
+    { "UTF-8", CS_UTF8 },\r
+};\r
+\r
+const char *charset_to_mimeenc(int charset)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(mimeencs); i++)\r
+       if (charset == mimeencs[i].charset)\r
+           return mimeencs[i].name;\r
+\r
+    return NULL;                      /* not found */\r
+}\r
+\r
+int charset_from_mimeenc(const char *name)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(mimeencs); i++) {\r
+       const char *p, *q;\r
+       p = name;\r
+       q = mimeencs[i].name;\r
+       while (*p || *q) {\r
+               if (tolower((unsigned char)*p) != tolower((unsigned char)*q))\r
+               break;\r
+           p++; q++;\r
+       }\r
+       if (!*p && !*q)\r
+           return mimeencs[i].charset;\r
+    }\r
+\r
+    return CS_NONE;                   /* not found */\r
+}\r
diff --git a/putty/CHARSET/README b/putty/CHARSET/README
new file mode 100644 (file)
index 0000000..2d08b36
--- /dev/null
@@ -0,0 +1,15 @@
+This subdirectory contains a general character-set conversion\r
+library, used in the Unix port of PuTTY, and available for use in\r
+other ports if it should happen to be useful.\r
+\r
+This is a variant of a library that's currently used in some other\r
+programs such as Timber and Halibut. At some future date, we would\r
+like to merge the two libraries, so that all programs use the same\r
+libcharset.\r
+\r
+It is therefore a _strong_ design goal that this library should remain\r
+perfectly general, and not tied to particulars of PuTTY. It must not\r
+reference any code outside its own subdirectory; it should not have\r
+PuTTY-specific helper routines added to it unless they can be\r
+documented in a general manner which might make them useful in other\r
+circumstances as well.\r
diff --git a/putty/CHARSET/SBCS.C b/putty/CHARSET/SBCS.C
new file mode 100644 (file)
index 0000000..f5ea523
--- /dev/null
@@ -0,0 +1,53 @@
+/*\r
+ * sbcs.c - routines to handle single-byte character sets.\r
+ */\r
+\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+/*\r
+ * The charset_spec for any single-byte character set should\r
+ * provide read_sbcs() as its read function, and its `data' field\r
+ * should be a wchar_t string constant containing the 256 entries\r
+ * of the translation table.\r
+ */\r
+\r
+void read_sbcs(charset_spec const *charset, long int input_chr,\r
+              charset_state *state,\r
+              void (*emit)(void *ctx, long int output), void *emitctx)\r
+{\r
+    const struct sbcs_data *sd = charset->data;\r
+\r
+    UNUSEDARG(state);\r
+\r
+    emit(emitctx, sd->sbcs2ucs[input_chr]);\r
+}\r
+\r
+void write_sbcs(charset_spec const *charset, long int input_chr,\r
+               charset_state *state,\r
+               void (*emit)(void *ctx, long int output), void *emitctx)\r
+{\r
+    const struct sbcs_data *sd = charset->data;\r
+    int i, j, k, c;\r
+\r
+    UNUSEDARG(state);\r
+\r
+    /*\r
+     * Binary-search in the ucs2sbcs table.\r
+     */\r
+    i = -1;\r
+    j = sd->nvalid;\r
+    while (i+1 < j) {\r
+       k = (i+j)/2;\r
+       c = sd->ucs2sbcs[k];\r
+       if (input_chr < sd->sbcs2ucs[c])\r
+           j = k;\r
+       else if (input_chr > sd->sbcs2ucs[c])\r
+           i = k;\r
+       else {\r
+           emit(emitctx, c);\r
+           return;\r
+       }\r
+    }\r
+    emit(emitctx, ERROR);\r
+}\r
diff --git a/putty/CHARSET/SBCS.DAT b/putty/CHARSET/SBCS.DAT
new file mode 100644 (file)
index 0000000..2b919a4
--- /dev/null
@@ -0,0 +1,1117 @@
+  Data file defining single-byte character sets.\r
\r
+  All lines which begin with whitespace are considered comments.\r
+\r
+  To generate an SBCS table from a unicode.org mapping table:\r
+\r
+  gensbcs() {\r
+  wget -q -O - "$1" | tr '\r' '\n' | \\r
+  perl -ne '/^(0x.*)\s+(0x.*)\s+/ and $a[hex $1]=sprintf "%04x", hex $2;' \\r
+   -e 'BEGIN{for($i=0;$i<256;$i++){$a[$i]="XXXX";' \\r
+   -e '      if ($i < 32 or $i == 127) {$a[$i]=sprintf "%04x", $i}}}' \\r
+   -e 'END{for($i=0;$i<256;$i++){printf"%s%s",$a[$i],$i%16==15?"\n":" "}}'\r
+  }\r
+\r
+  (A couple of noteworthy ickinesses here. For a start, any\r
+  undefined characters in the control-code regions (00-1F and 7F)\r
+  are assumed to be the Unicode code point corresponding to their\r
+  index, since the Mac Roman mapping table declines to define them\r
+  but realistically you don't want to be messing with that sort of\r
+  thing. Secondly, the Mac mapping tables are shipped with Mac line\r
+  endings, so note the `tr' to turn them into something legible to\r
+  Perl...)\r
+\r
+  Here are the ISO-8859-x tables, generated by this piece of Bourne\r
+  shell:\r
+\r
+  for i in 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16; do\r
+    echo charset CS_ISO8859_$i\r
+    gensbcs http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-$i.TXT\r
+    echo\r
+  done\r
+\r
+charset CS_ISO8859_1\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff\r
+\r
+charset CS_ISO8859_2\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 0104 02d8 0141 00a4 013d 015a 00a7 00a8 0160 015e 0164 0179 00ad 017d 017b\r
+00b0 0105 02db 0142 00b4 013e 015b 02c7 00b8 0161 015f 0165 017a 02dd 017e 017c\r
+0154 00c1 00c2 0102 00c4 0139 0106 00c7 010c 00c9 0118 00cb 011a 00cd 00ce 010e\r
+0110 0143 0147 00d3 00d4 0150 00d6 00d7 0158 016e 00da 0170 00dc 00dd 0162 00df\r
+0155 00e1 00e2 0103 00e4 013a 0107 00e7 010d 00e9 0119 00eb 011b 00ed 00ee 010f\r
+0111 0144 0148 00f3 00f4 0151 00f6 00f7 0159 016f 00fa 0171 00fc 00fd 0163 02d9\r
+\r
+charset CS_ISO8859_3\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 0126 02d8 00a3 00a4 XXXX 0124 00a7 00a8 0130 015e 011e 0134 00ad XXXX 017b\r
+00b0 0127 00b2 00b3 00b4 00b5 0125 00b7 00b8 0131 015f 011f 0135 00bd XXXX 017c\r
+00c0 00c1 00c2 XXXX 00c4 010a 0108 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+XXXX 00d1 00d2 00d3 00d4 0120 00d6 00d7 011c 00d9 00da 00db 00dc 016c 015c 00df\r
+00e0 00e1 00e2 XXXX 00e4 010b 0109 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+XXXX 00f1 00f2 00f3 00f4 0121 00f6 00f7 011d 00f9 00fa 00fb 00fc 016d 015d 02d9\r
+\r
+charset CS_ISO8859_4\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 0104 0138 0156 00a4 0128 013b 00a7 00a8 0160 0112 0122 0166 00ad 017d 00af\r
+00b0 0105 02db 0157 00b4 0129 013c 02c7 00b8 0161 0113 0123 0167 014a 017e 014b\r
+0100 00c1 00c2 00c3 00c4 00c5 00c6 012e 010c 00c9 0118 00cb 0116 00cd 00ce 012a\r
+0110 0145 014c 0136 00d4 00d5 00d6 00d7 00d8 0172 00da 00db 00dc 0168 016a 00df\r
+0101 00e1 00e2 00e3 00e4 00e5 00e6 012f 010d 00e9 0119 00eb 0117 00ed 00ee 012b\r
+0111 0146 014d 0137 00f4 00f5 00f6 00f7 00f8 0173 00fa 00fb 00fc 0169 016b 02d9\r
+\r
+charset CS_ISO8859_5\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 0401 0402 0403 0404 0405 0406 0407 0408 0409 040a 040b 040c 00ad 040e 040f\r
+0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f\r
+0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f\r
+0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f\r
+0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f\r
+2116 0451 0452 0453 0454 0455 0456 0457 0458 0459 045a 045b 045c 00a7 045e 045f\r
+\r
+charset CS_ISO8859_6\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 XXXX XXXX XXXX 00a4 XXXX XXXX XXXX XXXX XXXX XXXX XXXX 060c 00ad XXXX XXXX\r
+XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 061b XXXX XXXX XXXX 061f\r
+XXXX 0621 0622 0623 0624 0625 0626 0627 0628 0629 062a 062b 062c 062d 062e 062f\r
+0630 0631 0632 0633 0634 0635 0636 0637 0638 0639 063a XXXX XXXX XXXX XXXX XXXX\r
+0640 0641 0642 0643 0644 0645 0646 0647 0648 0649 064a 064b 064c 064d 064e 064f\r
+0650 0651 0652 XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX\r
+\r
+charset CS_ISO8859_7\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 2018 2019 00a3 XXXX XXXX 00a6 00a7 00a8 00a9 XXXX 00ab 00ac 00ad XXXX 2015\r
+00b0 00b1 00b2 00b3 0384 0385 0386 00b7 0388 0389 038a 00bb 038c 00bd 038e 038f\r
+0390 0391 0392 0393 0394 0395 0396 0397 0398 0399 039a 039b 039c 039d 039e 039f\r
+03a0 03a1 XXXX 03a3 03a4 03a5 03a6 03a7 03a8 03a9 03aa 03ab 03ac 03ad 03ae 03af\r
+03b0 03b1 03b2 03b3 03b4 03b5 03b6 03b7 03b8 03b9 03ba 03bb 03bc 03bd 03be 03bf\r
+03c0 03c1 03c2 03c3 03c4 03c5 03c6 03c7 03c8 03c9 03ca 03cb 03cc 03cd 03ce XXXX\r
+\r
+charset CS_ISO8859_8\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 XXXX 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00d7 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00f7 00bb 00bc 00bd 00be XXXX\r
+XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX\r
+XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 2017\r
+05d0 05d1 05d2 05d3 05d4 05d5 05d6 05d7 05d8 05d9 05da 05db 05dc 05dd 05de 05df\r
+05e0 05e1 05e2 05e3 05e4 05e5 05e6 05e7 05e8 05e9 05ea XXXX XXXX 200e 200f XXXX\r
+\r
+charset CS_ISO8859_9\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+011e 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 0130 015e 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+011f 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 0131 015f 00ff\r
+\r
+charset CS_ISO8859_10\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 0104 0112 0122 012a 0128 0136 00a7 013b 0110 0160 0166 017d 00ad 016a 014a\r
+00b0 0105 0113 0123 012b 0129 0137 00b7 013c 0111 0161 0167 017e 2015 016b 014b\r
+0100 00c1 00c2 00c3 00c4 00c5 00c6 012e 010c 00c9 0118 00cb 0116 00cd 00ce 00cf\r
+00d0 0145 014c 00d3 00d4 00d5 00d6 0168 00d8 0172 00da 00db 00dc 00dd 00de 00df\r
+0101 00e1 00e2 00e3 00e4 00e5 00e6 012f 010d 00e9 0119 00eb 0117 00ed 00ee 00ef\r
+00f0 0146 014d 00f3 00f4 00f5 00f6 0169 00f8 0173 00fa 00fb 00fc 00fd 00fe 0138\r
+\r
+charset CS_ISO8859_11\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 0e01 0e02 0e03 0e04 0e05 0e06 0e07 0e08 0e09 0e0a 0e0b 0e0c 0e0d 0e0e 0e0f\r
+0e10 0e11 0e12 0e13 0e14 0e15 0e16 0e17 0e18 0e19 0e1a 0e1b 0e1c 0e1d 0e1e 0e1f\r
+0e20 0e21 0e22 0e23 0e24 0e25 0e26 0e27 0e28 0e29 0e2a 0e2b 0e2c 0e2d 0e2e 0e2f\r
+0e30 0e31 0e32 0e33 0e34 0e35 0e36 0e37 0e38 0e39 0e3a XXXX XXXX XXXX XXXX 0e3f\r
+0e40 0e41 0e42 0e43 0e44 0e45 0e46 0e47 0e48 0e49 0e4a 0e4b 0e4c 0e4d 0e4e 0e4f\r
+0e50 0e51 0e52 0e53 0e54 0e55 0e56 0e57 0e58 0e59 0e5a 0e5b XXXX XXXX XXXX XXXX\r
+\r
+charset CS_ISO8859_13\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 201d 00a2 00a3 00a4 201e 00a6 00a7 00d8 00a9 0156 00ab 00ac 00ad 00ae 00c6\r
+00b0 00b1 00b2 00b3 201c 00b5 00b6 00b7 00f8 00b9 0157 00bb 00bc 00bd 00be 00e6\r
+0104 012e 0100 0106 00c4 00c5 0118 0112 010c 00c9 0179 0116 0122 0136 012a 013b\r
+0160 0143 0145 00d3 014c 00d5 00d6 00d7 0172 0141 015a 016a 00dc 017b 017d 00df\r
+0105 012f 0101 0107 00e4 00e5 0119 0113 010d 00e9 017a 0117 0123 0137 012b 013c\r
+0161 0144 0146 00f3 014d 00f5 00f6 00f7 0173 0142 015b 016b 00fc 017c 017e 2019\r
+\r
+charset CS_ISO8859_14\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 1e02 1e03 00a3 010a 010b 1e0a 00a7 1e80 00a9 1e82 1e0b 1ef2 00ad 00ae 0178\r
+1e1e 1e1f 0120 0121 1e40 1e41 00b6 1e56 1e81 1e57 1e83 1e60 1ef3 1e84 1e85 1e61\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+0174 00d1 00d2 00d3 00d4 00d5 00d6 1e6a 00d8 00d9 00da 00db 00dc 00dd 0176 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+0175 00f1 00f2 00f3 00f4 00f5 00f6 1e6b 00f8 00f9 00fa 00fb 00fc 00fd 0177 00ff\r
+\r
+charset CS_ISO8859_15\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 00a1 00a2 00a3 20ac 00a5 0160 00a7 0161 00a9 00aa 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 017d 00b5 00b6 00b7 017e 00b9 00ba 00bb 0152 0153 0178 00bf\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff\r
+\r
+charset CS_ISO8859_16\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 0104 0105 0141 20ac 201e 0160 00a7 0161 00a9 0218 00ab 0179 00ad 017a 017b\r
+00b0 00b1 010c 0142 017d 201d 00b6 00b7 017e 010d 0219 00bb 0152 0153 0178 017c\r
+00c0 00c1 00c2 0102 00c4 0106 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+0110 0143 00d2 00d3 00d4 0150 00d6 015a 0170 00d9 00da 00db 00dc 0118 021a 00df\r
+00e0 00e1 00e2 0103 00e4 0107 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+0111 0144 00f2 00f3 00f4 0151 00f6 015b 0171 00f9 00fa 00fb 00fc 0119 021b 00ff\r
+\r
+  Some X fonts are encoded in a variant form of ISO8859-1:\r
+  everything above 0x20 (space) is as normal, but the first 32\r
+  characters contain the VT100 line drawing glyphs as they would\r
+  appear from positions 0x5F to 0x7E inclusive. Here is the modified\r
+  ISO8859-1 code table.\r
+\r
+  Since this table contains a few duplicated positions, we use the\r
+  `sortpriority' hint to indicate that things in the main part of\r
+  the code table (0x20-0xFF) should be generated preferentially when\r
+  converting _from_ Unicode. Hence, U+00b0 (for example) will yield\r
+  0xb0 rather than 0x07.\r
+\r
+charset CS_ISO8859_1_X11\r
+sortpriority 00-1F -1\r
+0020 2666 2592 2409 240c 240d 240a 00b0 00b1 2424 240b 2518 2510 250c 2514 253c\r
+23ba 23bb 2500 23bc 23bd 251c 2524 2534 252c 2502 2264 2265 03c0 2260 00a3 00b7\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff\r
+\r
+  Here are some PC (old DOS) code pages, generated by this piece of\r
+  Bourne shell:\r
+\r
+  for i in 437 850 866; do\r
+    echo charset CS_CP$i\r
+    gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP$i.TXT\r
+    echo\r
+  done\r
+\r
+charset CS_CP437\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c7 00fc 00e9 00e2 00e4 00e0 00e5 00e7 00ea 00eb 00e8 00ef 00ee 00ec 00c4 00c5\r
+00c9 00e6 00c6 00f4 00f6 00f2 00fb 00f9 00ff 00d6 00dc 00a2 00a3 00a5 20a7 0192\r
+00e1 00ed 00f3 00fa 00f1 00d1 00aa 00ba 00bf 2310 00ac 00bd 00bc 00a1 00ab 00bb\r
+2591 2592 2593 2502 2524 2561 2562 2556 2555 2563 2551 2557 255d 255c 255b 2510\r
+2514 2534 252c 251c 2500 253c 255e 255f 255a 2554 2569 2566 2560 2550 256c 2567\r
+2568 2564 2565 2559 2558 2552 2553 256b 256a 2518 250c 2588 2584 258c 2590 2580\r
+03b1 00df 0393 03c0 03a3 03c3 00b5 03c4 03a6 0398 03a9 03b4 221e 03c6 03b5 2229\r
+2261 00b1 2265 2264 2320 2321 00f7 2248 00b0 2219 00b7 221a 207f 00b2 25a0 00a0\r
+\r
+charset CS_CP850\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c7 00fc 00e9 00e2 00e4 00e0 00e5 00e7 00ea 00eb 00e8 00ef 00ee 00ec 00c4 00c5\r
+00c9 00e6 00c6 00f4 00f6 00f2 00fb 00f9 00ff 00d6 00dc 00f8 00a3 00d8 00d7 0192\r
+00e1 00ed 00f3 00fa 00f1 00d1 00aa 00ba 00bf 00ae 00ac 00bd 00bc 00a1 00ab 00bb\r
+2591 2592 2593 2502 2524 00c1 00c2 00c0 00a9 2563 2551 2557 255d 00a2 00a5 2510\r
+2514 2534 252c 251c 2500 253c 00e3 00c3 255a 2554 2569 2566 2560 2550 256c 00a4\r
+00f0 00d0 00ca 00cb 00c8 0131 00cd 00ce 00cf 2518 250c 2588 2584 00a6 00cc 2580\r
+00d3 00df 00d4 00d2 00f5 00d5 00b5 00fe 00de 00da 00db 00d9 00fd 00dd 00af 00b4\r
+00ad 00b1 2017 00be 00b6 00a7 00f7 00b8 00b0 00a8 00b7 00b9 00b3 00b2 25a0 00a0\r
+\r
+charset CS_CP866\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f\r
+0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f\r
+0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f\r
+2591 2592 2593 2502 2524 2561 2562 2556 2555 2563 2551 2557 255d 255c 255b 2510\r
+2514 2534 252c 251c 2500 253c 255e 255f 255a 2554 2569 2566 2560 2550 256c 2567\r
+2568 2564 2565 2559 2558 2552 2553 256b 256a 2518 250c 2588 2584 258c 2590 2580\r
+0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f\r
+0401 0451 0404 0454 0407 0457 040e 045e 00b0 2219 00b7 221a 2116 00a4 25a0 00a0\r
+\r
+  Here are some Windows code pages, generated by this piece of\r
+  Bourne shell:\r
+\r
+  for i in 1250 1251 1252 1253 1254 1255 1256 1257 1258; do\r
+    echo charset CS_CP$i\r
+    gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP$i.TXT\r
+    echo\r
+  done\r
+\r
+charset CS_CP1250\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac XXXX 201a XXXX 201e 2026 2020 2021 XXXX 2030 0160 2039 015a 0164 017d 0179\r
+XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 0161 203a 015b 0165 017e 017a\r
+00a0 02c7 02d8 0141 00a4 0104 00a6 00a7 00a8 00a9 015e 00ab 00ac 00ad 00ae 017b\r
+00b0 00b1 02db 0142 00b4 00b5 00b6 00b7 00b8 0105 015f 00bb 013d 02dd 013e 017c\r
+0154 00c1 00c2 0102 00c4 0139 0106 00c7 010c 00c9 0118 00cb 011a 00cd 00ce 010e\r
+0110 0143 0147 00d3 00d4 0150 00d6 00d7 0158 016e 00da 0170 00dc 00dd 0162 00df\r
+0155 00e1 00e2 0103 00e4 013a 0107 00e7 010d 00e9 0119 00eb 011b 00ed 00ee 010f\r
+0111 0144 0148 00f3 00f4 0151 00f6 00f7 0159 016f 00fa 0171 00fc 00fd 0163 02d9\r
+\r
+charset CS_CP1251\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0402 0403 201a 0453 201e 2026 2020 2021 20ac 2030 0409 2039 040a 040c 040b 040f\r
+0452 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 0459 203a 045a 045c 045b 045f\r
+00a0 040e 045e 0408 00a4 0490 00a6 00a7 0401 00a9 0404 00ab 00ac 00ad 00ae 0407\r
+00b0 00b1 0406 0456 0491 00b5 00b6 00b7 0451 2116 0454 00bb 0458 0405 0455 0457\r
+0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f\r
+0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f\r
+0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f\r
+0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f\r
+\r
+charset CS_CP1252\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 0160 2039 0152 XXXX 017d XXXX\r
+XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 0161 203a 0153 XXXX 017e 0178\r
+00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff\r
+\r
+charset CS_CP1253\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac XXXX 201a 0192 201e 2026 2020 2021 XXXX 2030 XXXX 2039 XXXX XXXX XXXX XXXX\r
+XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 XXXX 203a XXXX XXXX XXXX XXXX\r
+00a0 0385 0386 00a3 00a4 00a5 00a6 00a7 00a8 00a9 XXXX 00ab 00ac 00ad 00ae 2015\r
+00b0 00b1 00b2 00b3 0384 00b5 00b6 00b7 0388 0389 038a 00bb 038c 00bd 038e 038f\r
+0390 0391 0392 0393 0394 0395 0396 0397 0398 0399 039a 039b 039c 039d 039e 039f\r
+03a0 03a1 XXXX 03a3 03a4 03a5 03a6 03a7 03a8 03a9 03aa 03ab 03ac 03ad 03ae 03af\r
+03b0 03b1 03b2 03b3 03b4 03b5 03b6 03b7 03b8 03b9 03ba 03bb 03bc 03bd 03be 03bf\r
+03c0 03c1 03c2 03c3 03c4 03c5 03c6 03c7 03c8 03c9 03ca 03cb 03cc 03cd 03ce XXXX\r
+\r
+charset CS_CP1254\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 0160 2039 0152 XXXX XXXX XXXX\r
+XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 0161 203a 0153 XXXX XXXX 0178\r
+00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+011e 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 0130 015e 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+011f 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 0131 015f 00ff\r
+\r
+charset CS_CP1255\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 XXXX 2039 XXXX XXXX XXXX XXXX\r
+XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 XXXX 203a XXXX XXXX XXXX XXXX\r
+00a0 00a1 00a2 00a3 20aa 00a5 00a6 00a7 00a8 00a9 00d7 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00f7 00bb 00bc 00bd 00be 00bf\r
+05b0 05b1 05b2 05b3 05b4 05b5 05b6 05b7 05b8 05b9 XXXX 05bb 05bc 05bd 05be 05bf\r
+05c0 05c1 05c2 05c3 05f0 05f1 05f2 05f3 05f4 XXXX XXXX XXXX XXXX XXXX XXXX XXXX\r
+05d0 05d1 05d2 05d3 05d4 05d5 05d6 05d7 05d8 05d9 05da 05db 05dc 05dd 05de 05df\r
+05e0 05e1 05e2 05e3 05e4 05e5 05e6 05e7 05e8 05e9 05ea XXXX XXXX 200e 200f XXXX\r
+\r
+charset CS_CP1256\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac 067e 201a 0192 201e 2026 2020 2021 02c6 2030 0679 2039 0152 0686 0698 0688\r
+06af 2018 2019 201c 201d 2022 2013 2014 06a9 2122 0691 203a 0153 200c 200d 06ba\r
+00a0 060c 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 06be 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 061b 00bb 00bc 00bd 00be 061f\r
+06c1 0621 0622 0623 0624 0625 0626 0627 0628 0629 062a 062b 062c 062d 062e 062f\r
+0630 0631 0632 0633 0634 0635 0636 00d7 0637 0638 0639 063a 0640 0641 0642 0643\r
+00e0 0644 00e2 0645 0646 0647 0648 00e7 00e8 00e9 00ea 00eb 0649 064a 00ee 00ef\r
+064b 064c 064d 064e 00f4 064f 0650 00f7 0651 00f9 0652 00fb 00fc 200e 200f 06d2\r
+\r
+charset CS_CP1257\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac XXXX 201a XXXX 201e 2026 2020 2021 XXXX 2030 XXXX 2039 XXXX 00a8 02c7 00b8\r
+XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 XXXX 203a XXXX 00af 02db XXXX\r
+00a0 XXXX 00a2 00a3 00a4 XXXX 00a6 00a7 00d8 00a9 0156 00ab 00ac 00ad 00ae 00c6\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00f8 00b9 0157 00bb 00bc 00bd 00be 00e6\r
+0104 012e 0100 0106 00c4 00c5 0118 0112 010c 00c9 0179 0116 0122 0136 012a 013b\r
+0160 0143 0145 00d3 014c 00d5 00d6 00d7 0172 0141 015a 016a 00dc 017b 017d 00df\r
+0105 012f 0101 0107 00e4 00e5 0119 0113 010d 00e9 017a 0117 0123 0137 012b 013c\r
+0161 0144 0146 00f3 014d 00f5 00f6 00f7 0173 0142 015b 016b 00fc 017c 017e 02d9\r
+\r
+charset CS_CP1258\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 XXXX 2039 0152 XXXX XXXX XXXX\r
+XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 XXXX 203a 0153 XXXX XXXX 0178\r
+00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af\r
+00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf\r
+00c0 00c1 00c2 0102 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 0300 00cd 00ce 00cf\r
+0110 00d1 0309 00d3 00d4 01a0 00d6 00d7 00d8 00d9 00da 00db 00dc 01af 0303 00df\r
+00e0 00e1 00e2 0103 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 0301 00ed 00ee 00ef\r
+0111 00f1 0323 00f3 00f4 01a1 00f6 00f7 00f8 00f9 00fa 00fb 00fc 01b0 20ab 00ff\r
+\r
+  KOI8-R, generated by this code:\r
+  \r
+  { echo charset CS_KOI8_R;\r
+    gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT; }\r
+\r
+charset CS_KOI8_R\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+2500 2502 250c 2510 2514 2518 251c 2524 252c 2534 253c 2580 2584 2588 258c 2590\r
+2591 2592 2593 2320 25a0 2219 221a 2248 2264 2265 00a0 2321 00b0 00b2 00b7 00f7\r
+2550 2551 2552 0451 2553 2554 2555 2556 2557 2558 2559 255a 255b 255c 255d 255e\r
+255f 2560 2561 0401 2562 2563 2564 2565 2566 2567 2568 2569 256a 256b 256c 00a9\r
+044e 0430 0431 0446 0434 0435 0444 0433 0445 0438 0439 043a 043b 043c 043d 043e\r
+043f 044f 0440 0441 0442 0443 0436 0432 044c 044b 0437 0448 044d 0449 0447 044a\r
+042e 0410 0411 0426 0414 0415 0424 0413 0425 0418 0419 041a 041b 041c 041d 041e\r
+041f 042f 0420 0421 0422 0423 0416 0412 042c 042b 0417 0428 042d 0429 0427 042a\r
+\r
+  KOI8-U: I can't find an easily machine-processable mapping table\r
+  for this one, so I've created it by hand-editing the KOI8-R\r
+  mapping table in accordance with the list of differences specified\r
+  in RFC2319. Note that RFC2319 has an apparent error: position B4\r
+  is listed as U+0404 in the main character set list, but as U+0403\r
+  in Appendix A (differences from KOI8-R). Both agree that it should\r
+  be CYRILLIC CAPITAL LETTER UKRAINIAN IE, however, and the Unicode\r
+  character database says that therefore U+0404 is the correct value.\r
+\r
+charset CS_KOI8_U\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+2500 2502 250c 2510 2514 2518 251c 2524 252c 2534 253c 2580 2584 2588 258c 2590\r
+2591 2592 2593 2320 25a0 2219 221a 2248 2264 2265 00a0 2321 00b0 00b2 00b7 00f7\r
+2550 2551 2552 0451 0454 2554 0456 0457 2557 2558 2559 255a 255b 0491 255d 255e\r
+255f 2560 2561 0401 0404 2563 0406 0407 2566 2567 2568 2569 256a 0490 256c 00a9\r
+044e 0430 0431 0446 0434 0435 0444 0433 0445 0438 0439 043a 043b 043c 043d 043e\r
+043f 044f 0440 0441 0442 0443 0436 0432 044c 044b 0437 0448 044d 0449 0447 044a\r
+042e 0410 0411 0426 0414 0415 0424 0413 0425 0418 0419 041a 041b 041c 041d 041e\r
+041f 042f 0420 0421 0422 0423 0416 0412 042c 042b 0417 0428 042d 0429 0427 042a\r
+\r
+  Various Mac character sets, generated by:\r
+\r
+  for i in ROMAN TURKISH CROATIAN ICELAND ROMANIAN GREEK CYRILLIC THAI \\r
+        CENTEURO SYMBOL DINGBATS; do\r
+    echo charset CS_MAC_$i\r
+    gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/$i.TXT | \\r
+        sed s/f8a0/XXXX/\r
+    echo\r
+  done\r
+\r
+  The code point F8FF at position F0 in Mac OS Roman an interesting\r
+  one. In Unicode, it's the last of the Private Use section. The\r
+  mapping table states that it should be an Apple logo. I suppose we\r
+  should just leave it as it is; there's bound to be some software out\r
+  there that understands U+F8FF to be an Apple logo!\r
+\r
+  Code point F8A0 at position F5 in Mac OS Turkish is actually just an\r
+  undefined character, so we make it properly undefined.\r
+\r
+  Many of the positions 80-9F in Mac OS Thai are for presentation\r
+  forms of other characters. When converting from Unicode, we use\r
+  `sortpriority' to avoid them.\r
+\r
+  Positions E2-E4 in Mac OS Symbol are for sans-serif variants of\r
+  other characters. Similarly, we avoid them.\r
+\r
+charset CS_MAC_ROMAN\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8\r
+221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8\r
+00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 2039 203a fb01 fb02\r
+2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7\r
+\r
+charset CS_MAC_TURKISH\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8\r
+221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8\r
+00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 011e 011f 0130 0131 015e 015f\r
+2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+f8ff 00d2 00da 00db 00d9 XXXX 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7\r
+\r
+charset CS_MAC_CROATIAN\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 0160 2122 00b4 00a8 2260 017d 00d8\r
+221e 00b1 2264 2265 2206 00b5 2202 2211 220f 0161 222b 00aa 00ba 03a9 017e 00f8\r
+00bf 00a1 00ac 221a 0192 2248 0106 00ab 010c 2026 00a0 00c0 00c3 00d5 0152 0153\r
+0110 2014 201c 201d 2018 2019 00f7 25ca f8ff 00a9 2044 20ac 2039 203a 00c6 00bb\r
+2013 00b7 201a 201e 2030 00c2 0107 00c1 010d 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+0111 00d2 00da 00db 00d9 0131 02c6 02dc 00af 03c0 00cb 02da 00b8 00ca 00e6 02c7\r
+\r
+charset CS_MAC_ICELAND\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+00dd 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8\r
+221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8\r
+00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 00d0 00f0 00de 00fe\r
+00fd 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7\r
+\r
+charset CS_MAC_ROMANIAN\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 0102 0218\r
+221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 0103 0219\r
+00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 2039 203a 021a 021b\r
+2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7\r
+\r
+charset CS_MAC_GREEK\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00b9 00b2 00c9 00b3 00d6 00dc 0385 00e0 00e2 00e4 0384 00a8 00e7 00e9 00e8\r
+00ea 00eb 00a3 2122 00ee 00ef 2022 00bd 2030 00f4 00f6 00a6 20ac 00f9 00fb 00fc\r
+2020 0393 0394 0398 039b 039e 03a0 00df 00ae 00a9 03a3 03aa 00a7 2260 00b0 00b7\r
+0391 00b1 2264 2265 00a5 0392 0395 0396 0397 0399 039a 039c 03a6 03ab 03a8 03a9\r
+03ac 039d 00ac 039f 03a1 2248 03a4 00ab 00bb 2026 00a0 03a5 03a7 0386 0388 0153\r
+2013 2015 201c 201d 2018 2019 00f7 0389 038a 038c 038e 03ad 03ae 03af 03cc 038f\r
+03cd 03b1 03b2 03c8 03b4 03b5 03c6 03b3 03b7 03b9 03be 03ba 03bb 03bc 03bd 03bf\r
+03c0 03ce 03c1 03c3 03c4 03b8 03c9 03c2 03c7 03c5 03b6 03ca 03cb 0390 03b0 00ad\r
+\r
+charset CS_MAC_CYRILLIC\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f\r
+0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f\r
+2020 00b0 0490 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453\r
+221e 00b1 2264 2265 0456 00b5 0491 0408 0404 0454 0407 0457 0409 0459 040a 045a\r
+0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455\r
+2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f\r
+0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f\r
+0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 20ac\r
+\r
+charset CS_MAC_THAI\r
+sortpriority 83-8C -1\r
+sortpriority 8F-8F -1\r
+sortpriority 92-9C -1\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00ab 00bb 2026 0e48 0e49 0e4a 0e4b 0e4c 0e48 0e49 0e4a 0e4b 0e4c 201c 201d 0e4d\r
+XXXX 2022 0e31 0e47 0e34 0e35 0e36 0e37 0e48 0e49 0e4a 0e4b 0e4c 2018 2019 XXXX\r
+00a0 0e01 0e02 0e03 0e04 0e05 0e06 0e07 0e08 0e09 0e0a 0e0b 0e0c 0e0d 0e0e 0e0f\r
+0e10 0e11 0e12 0e13 0e14 0e15 0e16 0e17 0e18 0e19 0e1a 0e1b 0e1c 0e1d 0e1e 0e1f\r
+0e20 0e21 0e22 0e23 0e24 0e25 0e26 0e27 0e28 0e29 0e2a 0e2b 0e2c 0e2d 0e2e 0e2f\r
+0e30 0e31 0e32 0e33 0e34 0e35 0e36 0e37 0e38 0e39 0e3a 2060 200b 2013 2014 0e3f\r
+0e40 0e41 0e42 0e43 0e44 0e45 0e46 0e47 0e48 0e49 0e4a 0e4b 0e4c 0e4d 2122 0e4f\r
+0e50 0e51 0e52 0e53 0e54 0e55 0e56 0e57 0e58 0e59 00ae 00a9 XXXX XXXX XXXX XXXX\r
+\r
+charset CS_MAC_CENTEURO\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 0100 0101 00c9 0104 00d6 00dc 00e1 0105 010c 00e4 010d 0106 0107 00e9 0179\r
+017a 010e 00ed 010f 0112 0113 0116 00f3 0117 00f4 00f6 00f5 00fa 011a 011b 00fc\r
+2020 00b0 0118 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 0119 00a8 2260 0123 012e\r
+012f 012a 2264 2265 012b 0136 2202 2211 0142 013b 013c 013d 013e 0139 013a 0145\r
+0146 0143 00ac 221a 0144 0147 2206 00ab 00bb 2026 00a0 0148 0150 00d5 0151 014c\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 014d 0154 0155 0158 2039 203a 0159 0156\r
+0157 0160 201a 201e 0161 015a 015b 00c1 0164 0165 00cd 017d 017e 016a 00d3 00d4\r
+016b 016e 00da 016f 0170 0171 0172 0173 00dd 00fd 0137 017b 0141 017c 0122 02c7\r
+\r
+charset CS_MAC_SYMBOL\r
+sortpriority E2-E4 -1\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 2200 0023 2203 0025 0026 220d 0028 0029 2217 002b 002c 2212 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+2245 0391 0392 03a7 0394 0395 03a6 0393 0397 0399 03d1 039a 039b 039c 039d 039f\r
+03a0 0398 03a1 03a3 03a4 03a5 03c2 03a9 039e 03a8 0396 005b 2234 005d 22a5 005f\r
+f8e5 03b1 03b2 03c7 03b4 03b5 03c6 03b3 03b7 03b9 03d5 03ba 03bb 03bc 03bd 03bf\r
+03c0 03b8 03c1 03c3 03c4 03c5 03d6 03c9 03be 03c8 03b6 007b 007c 007d 223c 007f\r
+XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX\r
+XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX\r
+20ac 03d2 2032 2264 2044 221e 0192 2663 2666 2665 2660 2194 2190 2191 2192 2193\r
+00b0 00b1 2033 2265 00d7 221d 2202 2022 00f7 2260 2261 2248 2026 f8e6 23af 21b5\r
+2135 2111 211c 2118 2297 2295 2205 2229 222a 2283 2287 2284 2282 2286 2208 2209\r
+2220 2207 00ae 00a9 2122 220f 221a 22c5 00ac 2227 2228 21d4 21d0 21d1 21d2 21d3\r
+22c4 3008 00ae 00a9 2122 2211 239b 239c 239d 23a1 23a2 23a3 23a7 23a8 23a9 23aa\r
+f8ff 3009 222b 2320 23ae 2321 239e 239f 23a0 23a4 23a5 23a6 23ab 23ac 23ad XXXX\r
+\r
+charset CS_MAC_DINGBATS\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 2701 2702 2703 2704 260e 2706 2707 2708 2709 261b 261e 270c 270d 270e 270f\r
+2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 271a 271b 271c 271d 271e 271f\r
+2720 2721 2722 2723 2724 2725 2726 2727 2605 2729 272a 272b 272c 272d 272e 272f\r
+2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 273a 273b 273c 273d 273e 273f\r
+2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 274a 274b 25cf 274d 25a0 274f\r
+2750 2751 2752 25b2 25bc 25c6 2756 25d7 2758 2759 275a 275b 275c 275d 275e 007f\r
+2768 2769 276a 276b 276c 276d 276e 276f 2770 2771 2772 2773 2774 2775 XXXX XXXX\r
+XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX\r
+XXXX 2761 2762 2763 2764 2765 2766 2767 2663 2666 2665 2660 2460 2461 2462 2463\r
+2464 2465 2466 2467 2468 2469 2776 2777 2778 2779 277a 277b 277c 277d 277e 277f\r
+2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 278a 278b 278c 278d 278e 278f\r
+2790 2791 2792 2793 2794 2192 2194 2195 2798 2799 279a 279b 279c 279d 279e 279f\r
+27a0 27a1 27a2 27a3 27a4 27a5 27a6 27a7 27a8 27a9 27aa 27ab 27ac 27ad 27ae 27af\r
+XXXX 27b1 27b2 27b3 27b4 27b5 27b6 27b7 27b8 27b9 27ba 27bb 27bc 27bd 27be XXXX\r
+\r
+  Various Mac character sets have older (usually pre-Euro) variants\r
+  which are documented in the comments in their mapping tables.  I've\r
+  manually applied these changes below.\r
+\r
+  Mac OS Roman variants before Mac OS 8.5 (CURRENCY SIGN rather than\r
+  EURO SIGN):\r
+\r
+charset CS_MAC_ROMAN_OLD\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8\r
+221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8\r
+00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 2039 203a fb01 fb02\r
+2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7\r
+\r
+charset CS_MAC_CROATIAN_OLD\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 0160 2122 00b4 00a8 2260 017d 00d8\r
+221e 00b1 2264 2265 2206 00b5 2202 2211 220f 0161 222b 00aa 00ba 03a9 017e 00f8\r
+00bf 00a1 00ac 221a 0192 2248 0106 00ab 010c 2026 00a0 00c0 00c3 00d5 0152 0153\r
+0110 2014 201c 201d 2018 2019 00f7 25ca f8ff 00a9 2044 00a4 2039 203a 00c6 00bb\r
+2013 00b7 201a 201e 2030 00c2 0107 00c1 010d 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+0111 00d2 00da 00db 00d9 0131 02c6 02dc 00af 03c0 00cb 02da 00b8 00ca 00e6 02c7\r
+\r
+charset CS_MAC_ICELAND_OLD\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+00dd 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8\r
+221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8\r
+00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 00d0 00f0 00de 00fe\r
+00fd 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7\r
+\r
+charset CS_MAC_ROMANIAN_OLD\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 0102 0218\r
+221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 0103 0219\r
+00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153\r
+2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 2039 203a 021a 021b\r
+2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7\r
+\r
+  Mac OS Greek before Mac OS 9.2.2 (SOFT HYPHEN instead of EURO SIGN,\r
+  and undefined instead of SOFT HYPHEN).\r
+\r
+charset CS_MAC_GREEK_OLD\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+00c4 00b9 00b2 00c9 00b3 00d6 00dc 0385 00e0 00e2 00e4 0384 00a8 00e7 00e9 00e8\r
+00ea 00eb 00a3 2122 00ee 00ef 2022 00bd 2030 00f4 00f6 00a6 00ad 00f9 00fb 00fc\r
+2020 0393 0394 0398 039b 039e 03a0 00df 00ae 00a9 03a3 03aa 00a7 2260 00b0 00b7\r
+0391 00b1 2264 2265 00a5 0392 0395 0396 0397 0399 039a 039c 03a6 03ab 03a8 03a9\r
+03ac 039d 00ac 039f 03a1 2248 03a4 00ab 00bb 2026 00a0 03a5 03a7 0386 0388 0153\r
+2013 2015 201c 201d 2018 2019 00f7 0389 038a 038c 038e 03ad 03ae 03af 03cc 038f\r
+03cd 03b1 03b2 03c8 03b4 03b5 03c6 03b3 03b7 03b9 03be 03ba 03bb 03bc 03bd 03bf\r
+03c0 03ce 03c1 03c3 03c4 03b8 03c9 03c2 03c7 03c5 03b6 03ca 03cb 0390 03b0 XXXX\r
+\r
+  Mac OS Cyrillic before Mac OS 9.0 (CENT SIGN instead of CYRILLIC\r
+  CAPITAL LETTER GHE WITH UPTURN, PARTIAL DIFFERENTIAL instead of\r
+  CYRILLIC SMALL LETTER GHE WITH UPTURN, CURRENCY SIGN instead of EURO\r
+  SIGN):\r
+\r
+charset CS_MAC_CYRILLIC_OLD\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f\r
+0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f\r
+2020 00b0 00a2 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453\r
+221e 00b1 2264 2265 0456 00b5 2022 0408 0404 0454 0407 0457 0409 0459 040a 045a\r
+0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455\r
+2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f\r
+0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f\r
+0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 00a4\r
+\r
+  Mac OS Ukrainian (now Cyrillic) before Mac OS 9.0 (CURRENCY SIGN\r
+  instead of EURO SIGN):\r
+\r
+charset CS_MAC_UKRAINE\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f\r
+0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f\r
+2020 00b0 0490 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453\r
+221e 00b1 2264 2265 0456 00b5 0491 0408 0404 0454 0407 0457 0409 0459 040a 045a\r
+0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455\r
+2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f\r
+0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f\r
+0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 00a4\r
+\r
+  Mac OS VT100 character set, as used by the "VT100" font.  Basically\r
+  Mac OS Roman hacked about to give it an almost-Latin1 repertoire and\r
+  most of the VT100 line-drawing set too.\r
+\r
+  Point CA is the backward question-mark used for silo overflows.\r
+\r
+  This table was derived by pasting the relevant part of 'utom' 140\r
+  from the "Western Language Encodings" file shipped with TEC 1.5 and\r
+  then manually fixing up the scan line characters to use the Unicode\r
+  3.2 HORIZONTAL SCAN LINE characters rather than UPPER ONE EIGHTH\r
+  BLOCK and LOWER ONE EIGHTH BLOCK with transcoding hints.\r
+\r
+charset CS_MAC_VT100\r
+2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 240a 240b 240c 240d 240e 240f\r
+2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 241a 241b 241c 241d 241e 241f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 2421\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+00dd 00b0 00a2 00a3 00a7 00b8 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8\r
+00d7 00b1 2264 2265 00a5 00b5 00b9 00b2 00b3 03c0 00a6 00aa 00ba 2592 00e6 00f8\r
+00bf 00a1 00ac 00bd 0192 00bc 00be 00ab 00bb 2026 XXXX 00c0 00c3 00d5 0152 0153\r
+2013 2014 2518 2510 250c 2514 00f7 2022 00ff 0178 253c 20ac 00d0 00f0 00fe 00de\r
+00fd 00b7 23ba 23bb 2500 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+XXXX 00d2 00da 00db 00d9 23bc 23bd 251c 2524 2534 252c 2502 XXXX XXXX XXXX XXXX\r
+\r
+  As with so many others, before Mac OS 8.5 this font had CURRENCY\r
+  SIGN rather than EURO SIGN.\r
+\r
+charset CS_MAC_VT100_OLD\r
+2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 240a 240b 240c 240d 240e 240f\r
+2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 241a 241b 241c 241d 241e 241f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 2421\r
+00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8\r
+00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc\r
+00dd 00b0 00a2 00a3 00a7 00b8 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8\r
+00d7 00b1 2264 2265 00a5 00b5 00b9 00b2 00b3 03c0 00a6 00aa 00ba 2592 00e6 00f8\r
+00bf 00a1 00ac 00bd 0192 00bc 00be 00ab 00bb 2026 XXXX 00c0 00c3 00d5 0152 0153\r
+2013 2014 2518 2510 250c 2514 00f7 2022 00ff 0178 253c 00a4 00d0 00f0 00fe 00de\r
+00fd 00b7 23ba 23bb 2500 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4\r
+XXXX 00d2 00da 00db 00d9 23bc 23bd 251c 2524 2534 252c 2502 XXXX XXXX XXXX XXXX\r
+\r
+  Roman Czyborra's web site (http://czyborra.com/) has a variety of\r
+  other useful mapping tables, in a slightly different format (and\r
+  gzipped). Here's a shell/Perl function to generate an SBCS table\r
+  from a Czyborra mapping table:\r
+\r
+  gensbcs_c() {\r
+  wget -q -O - "$1" | gzip -d | \\r
+  perl -ne '/^=(.*)\s+U\+(.*)\s+/ and $a[hex $1]=sprintf "%04x", hex $2;' \\r
+   -e 'BEGIN{for($i=0;$i<256;$i++){$a[$i]="XXXX";' \\r
+   -e 'if ($i < 32 or ($i >=127 and $i < 160)) {$a[$i]=sprintf "%04x", $i}}}' \\r
+   -e 'END{for($i=0;$i<256;$i++){printf"%s%s",$a[$i],$i%16==15?"\n":" "}}'\r
+  }\r
+\r
+  So here we have some character sets generated from Czyborra\r
+  mapping tables: VISCII, HP-Roman8, and the DEC Multinational\r
+  Character Set.\r
+\r
+  { echo charset CS_VISCII;\r
+    gensbcs_c http://czyborra.com/charsets/viscii.txt.gz; echo;\r
+    echo charset CS_HP_ROMAN8;\r
+    gensbcs_c http://czyborra.com/charsets/hp-roman8.txt.gz; echo;\r
+    echo charset CS_DEC_MCS;\r
+    gensbcs_c http://czyborra.com/charsets/dec-mcs.txt.gz; echo; }\r
+\r
+charset CS_VISCII\r
+0000 0001 1eb2 0003 0004 1eb4 1eaa 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 1ef6 0015 0016 0017 0018 1ef8 001a 001b 001c 001d 1ef4 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+1ea0 1eae 1eb0 1eb6 1ea4 1ea6 1ea8 1eac 1ebc 1eb8 1ebe 1ec0 1ec2 1ec4 1ec6 1ed0\r
+1ed2 1ed4 1ed6 1ed8 1ee2 1eda 1edc 1ede 1eca 1ece 1ecc 1ec8 1ee6 0168 1ee4 1ef2\r
+00d5 1eaf 1eb1 1eb7 1ea5 1ea7 1ea8 1ead 1ebd 1eb9 1ebf 1ec1 1ec3 1ec5 1ec7 1ed1\r
+1ed3 1ed5 1ed7 1ee0 01a0 1ed9 1edd 1edf 1ecb 1ef0 1ee8 1eea 1eec 01a1 1edb 01af\r
+00c0 00c1 00c2 00c3 1ea2 0102 1eb3 1eb5 00c8 00c9 00ca 1eba 00cc 00cd 0128 1ef3\r
+0110 1ee9 00d2 00d3 00d4 1ea1 1ef7 1eeb 1eed 00d9 00da 1ef9 1ef5 00dd 1ee1 01b0\r
+00e0 00e1 00e2 00e3 1ea3 0103 1eef 1eab 00e8 00e9 00ea 1ebb 00ec 00ed 0129 1ec9\r
+0111 1ef1 00f2 00f3 00f4 00f5 1ecf 1ecd 1ee5 00f9 00fa 0169 1ee7 00fd 1ee3 1eee\r
+\r
+charset CS_HP_ROMAN8\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+00a0 00c0 00c2 00c8 00ca 00cb 00ce 00cf 00b4 02cb 02c6 00a8 02dc 00d9 00db 20a4\r
+00af 00dd 00fd 00b0 00c7 00e7 00d1 00f1 00a1 00bf 00a4 00a3 00a5 00a7 0192 00a2\r
+00e2 00ea 00f4 00fb 00e1 00e9 00f3 00fa 00e0 00e8 00f2 00f9 00e4 00eb 00f6 00fc\r
+00c5 00ee 00d8 00c6 00e5 00ed 00f8 00e6 00c4 00ec 00d6 00dc 00c9 00ef 00df 00d4\r
+00c1 00c3 00e3 00d0 00f0 00cd 00cc 00d3 00d2 00d5 00f5 0160 0161 00da 0178 00ff\r
+00de 00fe 00b7 00b5 00b6 00be 2014 00bc 00bd 00aa 00ba 00ab 25a0 00bb 00b1 XXXX\r
+\r
+charset CS_DEC_MCS\r
+0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f\r
+0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f\r
+0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f\r
+0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f\r
+0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f\r
+0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f\r
+0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f\r
+0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f\r
+0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f\r
+0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f\r
+XXXX 00a1 00a2 00a3 XXXX 00a5 XXXX 00a7 00a4 00a9 00aa 00ab XXXX XXXX XXXX XXXX\r
+00b0 00b1 00b2 00b3 XXXX 00b5 00b6 00b7 XXXX 00b9 00ba 00bb 00bc 00bd XXXX 00bf\r
+00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf\r
+XXXX 00d1 00d2 00d3 00d4 00d5 00d6 0152 00d8 00d9 00da 00db 00dc 0178 XXXX 00df\r
+00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef\r
+XXXX 00f1 00f2 00f3 00f4 00f5 00f6 0153 00f8 00f9 00fa 00fb 00fc 00ff XXXX XXXX\r
diff --git a/putty/CHARSET/SBCSDAT.C b/putty/CHARSET/SBCSDAT.C
new file mode 100644 (file)
index 0000000..664bcd5
--- /dev/null
@@ -0,0 +1,4018 @@
+/*\r
+ * sbcsdat.c - data definitions for single-byte character sets.\r
+ *\r
+ * Generated by sbcsgen.pl from sbcs.dat.\r
+ * You should edit those files rather than editing this one.\r
+ */\r
+\r
+#ifndef ENUM_CHARSETS\r
+\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+static const sbcs_data data_CS_ISO8859_1 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,\r
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\r
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,\r
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_1 = {\r
+    CS_ISO8859_1, read_sbcs, write_sbcs, &data_CS_ISO8859_1\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_2 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7,\r
+    0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b,\r
+    0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7,\r
+    0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c,\r
+    0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,\r
+    0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,\r
+    0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,\r
+    0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,\r
+    0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,\r
+    0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,\r
+    0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,\r
+    0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa4, 0xa7, 0xa8, 0xad, 0xb0, 0xb4, 0xb8,\r
+    0xc1, 0xc2, 0xc4, 0xc7, 0xc9, 0xcb, 0xcd, 0xce,\r
+    0xd3, 0xd4, 0xd6, 0xd7, 0xda, 0xdc, 0xdd, 0xdf,\r
+    0xe1, 0xe2, 0xe4, 0xe7, 0xe9, 0xeb, 0xed, 0xee,\r
+    0xf3, 0xf4, 0xf6, 0xf7, 0xfa, 0xfc, 0xfd, 0xc3,\r
+    0xe3, 0xa1, 0xb1, 0xc6, 0xe6, 0xc8, 0xe8, 0xcf,\r
+    0xef, 0xd0, 0xf0, 0xca, 0xea, 0xcc, 0xec, 0xc5,\r
+    0xe5, 0xa5, 0xb5, 0xa3, 0xb3, 0xd1, 0xf1, 0xd2,\r
+    0xf2, 0xd5, 0xf5, 0xc0, 0xe0, 0xd8, 0xf8, 0xa6,\r
+    0xb6, 0xaa, 0xba, 0xa9, 0xb9, 0xde, 0xfe, 0xab,\r
+    0xbb, 0xd9, 0xf9, 0xdb, 0xfb, 0xac, 0xbc, 0xaf,\r
+    0xbf, 0xae, 0xbe, 0xb7, 0xa2, 0xff, 0xb2, 0xbd\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_2 = {\r
+    CS_ISO8859_2, read_sbcs, write_sbcs, &data_CS_ISO8859_2\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_3 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x0126, 0x02d8, 0x00a3, 0x00a4, ERROR , 0x0124, 0x00a7,\r
+    0x00a8, 0x0130, 0x015e, 0x011e, 0x0134, 0x00ad, ERROR , 0x017b,\r
+    0x00b0, 0x0127, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x0125, 0x00b7,\r
+    0x00b8, 0x0131, 0x015f, 0x011f, 0x0135, 0x00bd, ERROR , 0x017c,\r
+    0x00c0, 0x00c1, 0x00c2, ERROR , 0x00c4, 0x010a, 0x0108, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    ERROR , 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x0120, 0x00d6, 0x00d7,\r
+    0x011c, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x016c, 0x015c, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, ERROR , 0x00e4, 0x010b, 0x0109, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    ERROR , 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x0121, 0x00f6, 0x00f7,\r
+    0x011d, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016d, 0x015d, 0x02d9\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xb0, 0xb2,\r
+    0xb3, 0xb4, 0xb5, 0xb7, 0xb8, 0xbd, 0xc0, 0xc1,\r
+    0xc2, 0xc4, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc,\r
+    0xcd, 0xce, 0xcf, 0xd1, 0xd2, 0xd3, 0xd4, 0xd6,\r
+    0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1,\r
+    0xe2, 0xe4, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec,\r
+    0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf6,\r
+    0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xc6, 0xe6, 0xc5,\r
+    0xe5, 0xd8, 0xf8, 0xab, 0xbb, 0xd5, 0xf5, 0xa6,\r
+    0xb6, 0xa1, 0xb1, 0xa9, 0xb9, 0xac, 0xbc, 0xde,\r
+    0xfe, 0xaa, 0xba, 0xdd, 0xfd, 0xaf, 0xbf, 0xa2,\r
+    0xff\r
+    },\r
+    249\r
+};\r
+const charset_spec charset_CS_ISO8859_3 = {\r
+    CS_ISO8859_3, read_sbcs, write_sbcs, &data_CS_ISO8859_3\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_4 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7,\r
+    0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af,\r
+    0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7,\r
+    0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b,\r
+    0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e,\r
+    0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a,\r
+    0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\r
+    0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df,\r
+    0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f,\r
+    0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b,\r
+    0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\r
+    0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa4, 0xa7, 0xa8, 0xad, 0xaf, 0xb0, 0xb4,\r
+    0xb8, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc9,\r
+    0xcb, 0xcd, 0xce, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,\r
+    0xda, 0xdb, 0xdc, 0xdf, 0xe1, 0xe2, 0xe3, 0xe4,\r
+    0xe5, 0xe6, 0xe9, 0xeb, 0xed, 0xee, 0xf4, 0xf5,\r
+    0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xc0, 0xe0,\r
+    0xa1, 0xb1, 0xc8, 0xe8, 0xd0, 0xf0, 0xaa, 0xba,\r
+    0xcc, 0xec, 0xca, 0xea, 0xab, 0xbb, 0xa5, 0xb5,\r
+    0xcf, 0xef, 0xc7, 0xe7, 0xd3, 0xf3, 0xa2, 0xa6,\r
+    0xb6, 0xd1, 0xf1, 0xbd, 0xbf, 0xd2, 0xf2, 0xa3,\r
+    0xb3, 0xa9, 0xb9, 0xac, 0xbc, 0xdd, 0xfd, 0xde,\r
+    0xfe, 0xd9, 0xf9, 0xae, 0xbe, 0xb7, 0xff, 0xb2\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_4 = {\r
+    CS_ISO8859_4, read_sbcs, write_sbcs, &data_CS_ISO8859_4\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_5 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,\r
+    0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f,\r
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+    0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,\r
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+    0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,\r
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+    0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,\r
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+    0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,\r
+    0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,\r
+    0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xfd, 0xad, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,\r
+    0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xae,\r
+    0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,\r
+    0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe,\r
+    0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,\r
+    0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce,\r
+    0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,\r
+    0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,\r
+    0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,\r
+    0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee,\r
+    0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,\r
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfe, 0xff, 0xf0\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_5 = {\r
+    CS_ISO8859_5, read_sbcs, write_sbcs, &data_CS_ISO8859_5\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_6 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, ERROR , ERROR , ERROR , 0x00a4, ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , 0x060c, 0x00ad, ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , 0x061b, ERROR , ERROR , ERROR , 0x061f,\r
+    ERROR , 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,\r
+    0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f,\r
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637,\r
+    0x0638, 0x0639, 0x063a, ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647,\r
+    0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f,\r
+    0x0650, 0x0651, 0x0652, ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa4, 0xad, 0xac, 0xbb, 0xbf, 0xc1, 0xc2,\r
+    0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,\r
+    0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2,\r
+    0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\r
+    0xf0, 0xf1, 0xf2\r
+    },\r
+    211\r
+};\r
+const charset_spec charset_CS_ISO8859_6 = {\r
+    CS_ISO8859_6, read_sbcs, write_sbcs, &data_CS_ISO8859_6\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_7 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x2018, 0x2019, 0x00a3, ERROR , ERROR , 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, ERROR , 0x00ab, 0x00ac, 0x00ad, ERROR , 0x2015,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7,\r
+    0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f,\r
+    0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,\r
+    0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,\r
+    0x03a0, 0x03a1, ERROR , 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,\r
+    0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af,\r
+    0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,\r
+    0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,\r
+    0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,\r
+    0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa3, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac,\r
+    0xad, 0xb0, 0xb1, 0xb2, 0xb3, 0xb7, 0xbb, 0xbd,\r
+    0xb4, 0xb5, 0xb6, 0xb8, 0xb9, 0xba, 0xbc, 0xbe,\r
+    0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,\r
+    0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce,\r
+    0xcf, 0xd0, 0xd1, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,\r
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\r
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,\r
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaf,\r
+    0xa1, 0xa2\r
+    },\r
+    250\r
+};\r
+const charset_spec charset_CS_ISO8859_7 = {\r
+    CS_ISO8859_7, read_sbcs, write_sbcs, &data_CS_ISO8859_7\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_8 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, ERROR , 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , 0x2017,\r
+    0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7,\r
+    0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df,\r
+    0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7,\r
+    0x05e8, 0x05e9, 0x05ea, ERROR , ERROR , 0x200e, 0x200f, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,\r
+    0xa9, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,\r
+    0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,\r
+    0xbb, 0xbc, 0xbd, 0xbe, 0xaa, 0xba, 0xe0, 0xe1,\r
+    0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,\r
+    0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,\r
+    0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,\r
+    0xfa, 0xfd, 0xfe, 0xdf\r
+    },\r
+    220\r
+};\r
+const charset_spec charset_CS_ISO8859_8 = {\r
+    CS_ISO8859_8, read_sbcs, write_sbcs, &data_CS_ISO8859_8\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_9 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,\r
+    0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2,\r
+    0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,\r
+    0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3,\r
+    0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,\r
+    0xfc, 0xff, 0xd0, 0xf0, 0xdd, 0xfd, 0xde, 0xfe\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_9 = {\r
+    CS_ISO8859_9, read_sbcs, write_sbcs, &data_CS_ISO8859_9\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_10 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x0104, 0x0112, 0x0122, 0x012a, 0x0128, 0x0136, 0x00a7,\r
+    0x013b, 0x0110, 0x0160, 0x0166, 0x017d, 0x00ad, 0x016a, 0x014a,\r
+    0x00b0, 0x0105, 0x0113, 0x0123, 0x012b, 0x0129, 0x0137, 0x00b7,\r
+    0x013c, 0x0111, 0x0161, 0x0167, 0x017e, 0x2015, 0x016b, 0x014b,\r
+    0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e,\r
+    0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x00cf,\r
+    0x00d0, 0x0145, 0x014c, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0168,\r
+    0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,\r
+    0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f,\r
+    0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x00ef,\r
+    0x00f0, 0x0146, 0x014d, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x0169,\r
+    0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x0138\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa7, 0xad, 0xb0, 0xb7, 0xc1, 0xc2, 0xc3,\r
+    0xc4, 0xc5, 0xc6, 0xc9, 0xcb, 0xcd, 0xce, 0xcf,\r
+    0xd0, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xda, 0xdb,\r
+    0xdc, 0xdd, 0xde, 0xdf, 0xe1, 0xe2, 0xe3, 0xe4,\r
+    0xe5, 0xe6, 0xe9, 0xeb, 0xed, 0xee, 0xef, 0xf0,\r
+    0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc,\r
+    0xfd, 0xfe, 0xc0, 0xe0, 0xa1, 0xb1, 0xc8, 0xe8,\r
+    0xa9, 0xb9, 0xa2, 0xb2, 0xcc, 0xec, 0xca, 0xea,\r
+    0xa3, 0xb3, 0xa5, 0xb5, 0xa4, 0xb4, 0xc7, 0xe7,\r
+    0xa6, 0xb6, 0xff, 0xa8, 0xb8, 0xd1, 0xf1, 0xaf,\r
+    0xbf, 0xd2, 0xf2, 0xaa, 0xba, 0xab, 0xbb, 0xd7,\r
+    0xf7, 0xae, 0xbe, 0xd9, 0xf9, 0xac, 0xbc, 0xbd\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_10 = {\r
+    CS_ISO8859_10, read_sbcs, write_sbcs, &data_CS_ISO8859_10\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_11 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07,\r
+    0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f,\r
+    0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17,\r
+    0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f,\r
+    0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27,\r
+    0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f,\r
+    0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37,\r
+    0x0e38, 0x0e39, 0x0e3a, ERROR , ERROR , ERROR , ERROR , 0x0e3f,\r
+    0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47,\r
+    0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0e4e, 0x0e4f,\r
+    0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57,\r
+    0x0e58, 0x0e59, 0x0e5a, 0x0e5b, ERROR , ERROR , ERROR , ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,\r
+    0xd8, 0xd9, 0xda, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,\r
+    0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb,\r
+    0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,\r
+    0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb\r
+    },\r
+    248\r
+};\r
+const charset_spec charset_CS_ISO8859_11 = {\r
+    CS_ISO8859_11, read_sbcs, write_sbcs, &data_CS_ISO8859_11\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_13 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x201d, 0x00a2, 0x00a3, 0x00a4, 0x201e, 0x00a6, 0x00a7,\r
+    0x00d8, 0x00a9, 0x0156, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00c6,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x201c, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00f8, 0x00b9, 0x0157, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00e6,\r
+    0x0104, 0x012e, 0x0100, 0x0106, 0x00c4, 0x00c5, 0x0118, 0x0112,\r
+    0x010c, 0x00c9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012a, 0x013b,\r
+    0x0160, 0x0143, 0x0145, 0x00d3, 0x014c, 0x00d5, 0x00d6, 0x00d7,\r
+    0x0172, 0x0141, 0x015a, 0x016a, 0x00dc, 0x017b, 0x017d, 0x00df,\r
+    0x0105, 0x012f, 0x0101, 0x0107, 0x00e4, 0x00e5, 0x0119, 0x0113,\r
+    0x010d, 0x00e9, 0x017a, 0x0117, 0x0123, 0x0137, 0x012b, 0x013c,\r
+    0x0161, 0x0144, 0x0146, 0x00f3, 0x014d, 0x00f5, 0x00f6, 0x00f7,\r
+    0x0173, 0x0142, 0x015b, 0x016b, 0x00fc, 0x017c, 0x017e, 0x2019\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa2, 0xa3, 0xa4, 0xa6, 0xa7, 0xa9, 0xab,\r
+    0xac, 0xad, 0xae, 0xb0, 0xb1, 0xb2, 0xb3, 0xb5,\r
+    0xb6, 0xb7, 0xb9, 0xbb, 0xbc, 0xbd, 0xbe, 0xc4,\r
+    0xc5, 0xaf, 0xc9, 0xd3, 0xd5, 0xd6, 0xd7, 0xa8,\r
+    0xdc, 0xdf, 0xe4, 0xe5, 0xbf, 0xe9, 0xf3, 0xf5,\r
+    0xf6, 0xf7, 0xb8, 0xfc, 0xc2, 0xe2, 0xc0, 0xe0,\r
+    0xc3, 0xe3, 0xc8, 0xe8, 0xc7, 0xe7, 0xcb, 0xeb,\r
+    0xc6, 0xe6, 0xcc, 0xec, 0xce, 0xee, 0xc1, 0xe1,\r
+    0xcd, 0xed, 0xcf, 0xef, 0xd9, 0xf9, 0xd1, 0xf1,\r
+    0xd2, 0xf2, 0xd4, 0xf4, 0xaa, 0xba, 0xda, 0xfa,\r
+    0xd0, 0xf0, 0xdb, 0xfb, 0xd8, 0xf8, 0xca, 0xea,\r
+    0xdd, 0xfd, 0xde, 0xfe, 0xff, 0xb4, 0xa1, 0xa5\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_13 = {\r
+    CS_ISO8859_13, read_sbcs, write_sbcs, &data_CS_ISO8859_13\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_14 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x1e02, 0x1e03, 0x00a3, 0x010a, 0x010b, 0x1e0a, 0x00a7,\r
+    0x1e80, 0x00a9, 0x1e82, 0x1e0b, 0x1ef2, 0x00ad, 0x00ae, 0x0178,\r
+    0x1e1e, 0x1e1f, 0x0120, 0x0121, 0x1e40, 0x1e41, 0x00b6, 0x1e56,\r
+    0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x0174, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x1e6a,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x0176, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x0175, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x1e6b,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x0177, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa3, 0xa7, 0xa9, 0xad, 0xae, 0xb6, 0xc0,\r
+    0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,\r
+    0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1,\r
+    0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda,\r
+    0xdb, 0xdc, 0xdd, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,\r
+    0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb,\r
+    0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4,\r
+    0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,\r
+    0xff, 0xa4, 0xa5, 0xb2, 0xb3, 0xd0, 0xf0, 0xde,\r
+    0xfe, 0xaf, 0xa1, 0xa2, 0xa6, 0xab, 0xb0, 0xb1,\r
+    0xb4, 0xb5, 0xb7, 0xb9, 0xbb, 0xbf, 0xd7, 0xf7,\r
+    0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0xac, 0xbc\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_14 = {\r
+    CS_ISO8859_14, read_sbcs, write_sbcs, &data_CS_ISO8859_14\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_15 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0160, 0x00a7,\r
+    0x0161, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x017d, 0x00b5, 0x00b6, 0x00b7,\r
+    0x017e, 0x00b9, 0x00ba, 0x00bb, 0x0152, 0x0153, 0x0178, 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa,\r
+    0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2,\r
+    0xb3, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,\r
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\r
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,\r
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,\r
+    0xbc, 0xbd, 0xa6, 0xa8, 0xbe, 0xb4, 0xb8, 0xa4\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_15 = {\r
+    CS_ISO8859_15, read_sbcs, write_sbcs, &data_CS_ISO8859_15\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_16 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x0104, 0x0105, 0x0141, 0x20ac, 0x201e, 0x0160, 0x00a7,\r
+    0x0161, 0x00a9, 0x0218, 0x00ab, 0x0179, 0x00ad, 0x017a, 0x017b,\r
+    0x00b0, 0x00b1, 0x010c, 0x0142, 0x017d, 0x201d, 0x00b6, 0x00b7,\r
+    0x017e, 0x010d, 0x0219, 0x00bb, 0x0152, 0x0153, 0x0178, 0x017c,\r
+    0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0106, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x0110, 0x0143, 0x00d2, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x015a,\r
+    0x0170, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0118, 0x021a, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x0107, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x0111, 0x0144, 0x00f2, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x015b,\r
+    0x0171, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0119, 0x021b, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa7, 0xa9, 0xab, 0xad, 0xb0, 0xb1, 0xb6,\r
+    0xb7, 0xbb, 0xc0, 0xc1, 0xc2, 0xc4, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd2, 0xd3, 0xd4, 0xd6, 0xd9, 0xda, 0xdb, 0xdc,\r
+    0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe7, 0xe8,\r
+    0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf2,\r
+    0xf3, 0xf4, 0xf6, 0xf9, 0xfa, 0xfb, 0xfc, 0xff,\r
+    0xc3, 0xe3, 0xa1, 0xa2, 0xc5, 0xe5, 0xb2, 0xb9,\r
+    0xd0, 0xf0, 0xdd, 0xfd, 0xa3, 0xb3, 0xd1, 0xf1,\r
+    0xd5, 0xf5, 0xbc, 0xbd, 0xd7, 0xf7, 0xa6, 0xa8,\r
+    0xd8, 0xf8, 0xbe, 0xac, 0xae, 0xaf, 0xbf, 0xb4,\r
+    0xb8, 0xaa, 0xba, 0xde, 0xfe, 0xb5, 0xa5, 0xa4\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_ISO8859_16 = {\r
+    CS_ISO8859_16, read_sbcs, write_sbcs, &data_CS_ISO8859_16\r
+};\r
+\r
+static const sbcs_data data_CS_ISO8859_1_X11 = {\r
+    {\r
+    0x0020, 0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,\r
+    0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,\r
+    0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534,\r
+    0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff\r
+    },\r
+    {\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,\r
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\r
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,\r
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,\r
+    0x1c, 0x1d, 0x1a, 0x1b, 0x10, 0x11, 0x13, 0x14,\r
+    0x03, 0x06, 0x0a, 0x04, 0x05, 0x09, 0x12, 0x19,\r
+    0x0d, 0x0c, 0x0e, 0x0b, 0x15, 0x16, 0x18, 0x17,\r
+    0x0f, 0x02, 0x01\r
+    },\r
+    251\r
+};\r
+const charset_spec charset_CS_ISO8859_1_X11 = {\r
+    CS_ISO8859_1_X11, read_sbcs, write_sbcs, &data_CS_ISO8859_1_X11\r
+};\r
+\r
+static const sbcs_data data_CS_CP437 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,\r
+    0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,\r
+    0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,\r
+    0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,\r
+    0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,\r
+    0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,\r
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,\r
+    0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,\r
+    0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,\r
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,\r
+    0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,\r
+    0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,\r
+    0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,\r
+    0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,\r
+    0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xff, 0xad, 0x9b, 0x9c, 0x9d, 0xa6, 0xae, 0xaa,\r
+    0xf8, 0xf1, 0xfd, 0xe6, 0xfa, 0xa7, 0xaf, 0xac,\r
+    0xab, 0xa8, 0x8e, 0x8f, 0x92, 0x80, 0x90, 0xa5,\r
+    0x99, 0x9a, 0xe1, 0x85, 0xa0, 0x83, 0x84, 0x86,\r
+    0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1,\r
+    0x8c, 0x8b, 0xa4, 0x95, 0xa2, 0x93, 0x94, 0xf6,\r
+    0x97, 0xa3, 0x96, 0x81, 0x98, 0x9f, 0xe2, 0xe9,\r
+    0xe4, 0xe8, 0xea, 0xe0, 0xeb, 0xee, 0xe3, 0xe5,\r
+    0xe7, 0xed, 0xfc, 0x9e, 0xf9, 0xfb, 0xec, 0xef,\r
+    0xf7, 0xf0, 0xf3, 0xf2, 0xa9, 0xf4, 0xf5, 0xc4,\r
+    0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4, 0xc2,\r
+    0xc1, 0xc5, 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8,\r
+    0xb7, 0xbb, 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc,\r
+    0xc6, 0xc7, 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2,\r
+    0xcb, 0xcf, 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0xdf,\r
+    0xdc, 0xdb, 0xdd, 0xde, 0xb0, 0xb1, 0xb2, 0xfe\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_CP437 = {\r
+    CS_CP437, read_sbcs, write_sbcs, &data_CS_CP437\r
+};\r
+\r
+static const sbcs_data data_CS_CP850 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,\r
+    0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,\r
+    0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,\r
+    0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192,\r
+    0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,\r
+    0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,\r
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x00c0,\r
+    0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510,\r
+    0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3,\r
+    0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4,\r
+    0x00f0, 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x0131, 0x00cd, 0x00ce,\r
+    0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580,\r
+    0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe,\r
+    0x00de, 0x00da, 0x00db, 0x00d9, 0x00fd, 0x00dd, 0x00af, 0x00b4,\r
+    0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8,\r
+    0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x25a0, 0x00a0\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5,\r
+    0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,\r
+    0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa,\r
+    0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,\r
+    0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80,\r
+    0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,\r
+    0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e,\r
+    0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,\r
+    0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87,\r
+    0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,\r
+    0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6,\r
+    0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98,\r
+    0xd5, 0x9f, 0xf2, 0xc4, 0xb3, 0xda, 0xbf, 0xc0,\r
+    0xd9, 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba,\r
+    0xc9, 0xbb, 0xc8, 0xbc, 0xcc, 0xb9, 0xcb, 0xca,\r
+    0xce, 0xdf, 0xdc, 0xdb, 0xb0, 0xb1, 0xb2, 0xfe\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_CP850 = {\r
+    CS_CP850, read_sbcs, write_sbcs, &data_CS_CP850\r
+};\r
+\r
+static const sbcs_data data_CS_CP866 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+    0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,\r
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+    0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,\r
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+    0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,\r
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,\r
+    0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,\r
+    0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,\r
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,\r
+    0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,\r
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+    0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,\r
+    0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040e, 0x045e,\r
+    0x00b0, 0x2219, 0x00b7, 0x221a, 0x2116, 0x00a4, 0x25a0, 0x00a0\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xff, 0xfd, 0xf8, 0xfa, 0xf0, 0xf2, 0xf4, 0xf6,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\r
+    0xf1, 0xf3, 0xf5, 0xf7, 0xfc, 0xf9, 0xfb, 0xc4,\r
+    0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4, 0xc2,\r
+    0xc1, 0xc5, 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8,\r
+    0xb7, 0xbb, 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc,\r
+    0xc6, 0xc7, 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2,\r
+    0xcb, 0xcf, 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0xdf,\r
+    0xdc, 0xdb, 0xdd, 0xde, 0xb0, 0xb1, 0xb2, 0xfe\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_CP866 = {\r
+    CS_CP866, read_sbcs, write_sbcs, &data_CS_CP866\r
+};\r
+\r
+static const sbcs_data data_CS_CP1250 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, ERROR , 0x201a, ERROR , 0x201e, 0x2026, 0x2020, 0x2021,\r
+    ERROR , 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,\r
+    ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    ERROR , 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,\r
+    0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,\r
+    0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,\r
+    0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,\r
+    0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,\r
+    0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,\r
+    0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,\r
+    0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,\r
+    0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,\r
+    0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,\r
+    0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa4, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac,\r
+    0xad, 0xae, 0xb0, 0xb1, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xbb, 0xc1, 0xc2, 0xc4, 0xc7, 0xc9, 0xcb,\r
+    0xcd, 0xce, 0xd3, 0xd4, 0xd6, 0xd7, 0xda, 0xdc,\r
+    0xdd, 0xdf, 0xe1, 0xe2, 0xe4, 0xe7, 0xe9, 0xeb,\r
+    0xed, 0xee, 0xf3, 0xf4, 0xf6, 0xf7, 0xfa, 0xfc,\r
+    0xfd, 0xc3, 0xe3, 0xa5, 0xb9, 0xc6, 0xe6, 0xc8,\r
+    0xe8, 0xcf, 0xef, 0xd0, 0xf0, 0xca, 0xea, 0xcc,\r
+    0xec, 0xc5, 0xe5, 0xbc, 0xbe, 0xa3, 0xb3, 0xd1,\r
+    0xf1, 0xd2, 0xf2, 0xd5, 0xf5, 0xc0, 0xe0, 0xd8,\r
+    0xf8, 0x8c, 0x9c, 0xaa, 0xba, 0x8a, 0x9a, 0xde,\r
+    0xfe, 0x8d, 0x9d, 0xd9, 0xf9, 0xdb, 0xfb, 0x8f,\r
+    0x9f, 0xaf, 0xbf, 0x8e, 0x9e, 0xa1, 0xa2, 0xff,\r
+    0xb2, 0xbd, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93,\r
+    0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b,\r
+    0x9b, 0x80, 0x99\r
+    },\r
+    251\r
+};\r
+const charset_spec charset_CS_CP1250 = {\r
+    CS_CP1250, read_sbcs, write_sbcs, &data_CS_CP1250\r
+};\r
+\r
+static const sbcs_data data_CS_CP1251 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0402, 0x0403, 0x201a, 0x0453, 0x201e, 0x2026, 0x2020, 0x2021,\r
+    0x20ac, 0x2030, 0x0409, 0x2039, 0x040a, 0x040c, 0x040b, 0x040f,\r
+    0x0452, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    ERROR , 0x2122, 0x0459, 0x203a, 0x045a, 0x045c, 0x045b, 0x045f,\r
+    0x00a0, 0x040e, 0x045e, 0x0408, 0x00a4, 0x0490, 0x00a6, 0x00a7,\r
+    0x0401, 0x00a9, 0x0404, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0407,\r
+    0x00b0, 0x00b1, 0x0406, 0x0456, 0x0491, 0x00b5, 0x00b6, 0x00b7,\r
+    0x0451, 0x2116, 0x0454, 0x00bb, 0x0458, 0x0405, 0x0455, 0x0457,\r
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+    0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,\r
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+    0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,\r
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+    0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,\r
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+    0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa4, 0xa6, 0xa7, 0xa9, 0xab, 0xac, 0xad,\r
+    0xae, 0xb0, 0xb1, 0xb5, 0xb6, 0xb7, 0xbb, 0xa8,\r
+    0x80, 0x81, 0xaa, 0xbd, 0xb2, 0xaf, 0xa3, 0x8a,\r
+    0x8c, 0x8e, 0x8d, 0xa1, 0x8f, 0xc0, 0xc1, 0xc2,\r
+    0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,\r
+    0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2,\r
+    0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,\r
+    0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2,\r
+    0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,\r
+    0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2,\r
+    0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,\r
+    0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xb8, 0x90, 0x83,\r
+    0xba, 0xbe, 0xb3, 0xbf, 0xbc, 0x9a, 0x9c, 0x9e,\r
+    0x9d, 0xa2, 0x9f, 0xa5, 0xb4, 0x96, 0x97, 0x91,\r
+    0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95,\r
+    0x85, 0x89, 0x8b, 0x9b, 0x88, 0xb9, 0x99\r
+    },\r
+    255\r
+};\r
+const charset_spec charset_CS_CP1251 = {\r
+    CS_CP1251, read_sbcs, write_sbcs, &data_CS_CP1251\r
+};\r
+\r
+static const sbcs_data data_CS_CP1252 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,\r
+    0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, ERROR , 0x017d, ERROR ,\r
+    ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, ERROR , 0x017e, 0x0178,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,\r
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\r
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,\r
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,\r
+    0x8c, 0x9c, 0x8a, 0x9a, 0x9f, 0x8e, 0x9e, 0x83,\r
+    0x88, 0x98, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93,\r
+    0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b,\r
+    0x9b, 0x80, 0x99\r
+    },\r
+    251\r
+};\r
+const charset_spec charset_CS_CP1252 = {\r
+    CS_CP1252, read_sbcs, write_sbcs, &data_CS_CP1252\r
+};\r
+\r
+static const sbcs_data data_CS_CP1253 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,\r
+    ERROR , 0x2030, ERROR , 0x2039, ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    ERROR , 0x2122, ERROR , 0x203a, ERROR , ERROR , ERROR , ERROR ,\r
+    0x00a0, 0x0385, 0x0386, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, ERROR , 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x2015,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x00b5, 0x00b6, 0x00b7,\r
+    0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f,\r
+    0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,\r
+    0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,\r
+    0x03a0, 0x03a1, ERROR , 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,\r
+    0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af,\r
+    0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,\r
+    0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,\r
+    0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,\r
+    0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,\r
+    0xab, 0xac, 0xad, 0xae, 0xb0, 0xb1, 0xb2, 0xb3,\r
+    0xb5, 0xb6, 0xb7, 0xbb, 0xbd, 0x83, 0xb4, 0xa1,\r
+    0xa2, 0xb8, 0xb9, 0xba, 0xbc, 0xbe, 0xbf, 0xc0,\r
+    0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,\r
+    0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,\r
+    0xd1, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,\r
+    0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1,\r
+    0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,\r
+    0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,\r
+    0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,\r
+    0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x96, 0x97, 0xaf,\r
+    0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87,\r
+    0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99\r
+    },\r
+    239\r
+};\r
+const charset_spec charset_CS_CP1253 = {\r
+    CS_CP1253, read_sbcs, write_sbcs, &data_CS_CP1253\r
+};\r
+\r
+static const sbcs_data data_CS_CP1254 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,\r
+    0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, ERROR , ERROR , ERROR ,\r
+    ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, ERROR , ERROR , 0x0178,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,\r
+    0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2,\r
+    0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,\r
+    0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3,\r
+    0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,\r
+    0xfc, 0xff, 0xd0, 0xf0, 0xdd, 0xfd, 0x8c, 0x9c,\r
+    0xde, 0xfe, 0x8a, 0x9a, 0x9f, 0x83, 0x88, 0x98,\r
+    0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84,\r
+    0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80,\r
+    0x99\r
+    },\r
+    249\r
+};\r
+const charset_spec charset_CS_CP1254 = {\r
+    CS_CP1254, read_sbcs, write_sbcs, &data_CS_CP1254\r
+};\r
+\r
+static const sbcs_data data_CS_CP1255 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,\r
+    0x02c6, 0x2030, ERROR , 0x2039, ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    0x02dc, 0x2122, ERROR , 0x203a, ERROR , ERROR , ERROR , ERROR ,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20aa, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\r
+    0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7,\r
+    0x05b8, 0x05b9, ERROR , 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf,\r
+    0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f0, 0x05f1, 0x05f2, 0x05f3,\r
+    0x05f4, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7,\r
+    0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df,\r
+    0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7,\r
+    0x05e8, 0x05e9, 0x05ea, ERROR , ERROR , 0x200e, 0x200f, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa6, 0xa7, 0xa8,\r
+    0xa9, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,\r
+    0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,\r
+    0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xaa, 0xba, 0x83,\r
+    0x88, 0x98, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,\r
+    0xc6, 0xc7, 0xc8, 0xc9, 0xcb, 0xcc, 0xcd, 0xce,\r
+    0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xe0, 0xe1, 0xe2,\r
+    0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,\r
+    0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2,\r
+    0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,\r
+    0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xfd, 0xfe, 0x96,\r
+    0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86,\r
+    0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0xa4, 0x80,\r
+    0x99\r
+    },\r
+    233\r
+};\r
+const charset_spec charset_CS_CP1255 = {\r
+    CS_CP1255, read_sbcs, write_sbcs, &data_CS_CP1255\r
+};\r
+\r
+static const sbcs_data data_CS_CP1256 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, 0x067e, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,\r
+    0x02c6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,\r
+    0x06af, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    0x06a9, 0x2122, 0x0691, 0x203a, 0x0153, 0x200c, 0x200d, 0x06ba,\r
+    0x00a0, 0x060c, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x06be, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x061b, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x061f,\r
+    0x06c1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,\r
+    0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f,\r
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00d7,\r
+    0x0637, 0x0638, 0x0639, 0x063a, 0x0640, 0x0641, 0x0642, 0x0643,\r
+    0x00e0, 0x0644, 0x00e2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x0649, 0x064a, 0x00ee, 0x00ef,\r
+    0x064b, 0x064c, 0x064d, 0x064e, 0x00f4, 0x064f, 0x0650, 0x00f7,\r
+    0x0651, 0x00f9, 0x0652, 0x00fb, 0x00fc, 0x200e, 0x200f, 0x06d2\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,\r
+    0xa9, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,\r
+    0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,\r
+    0xbb, 0xbc, 0xbd, 0xbe, 0xd7, 0xe0, 0xe2, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xee, 0xef, 0xf4, 0xf7,\r
+    0xf9, 0xfb, 0xfc, 0x8c, 0x9c, 0x83, 0x88, 0xa1,\r
+    0xba, 0xbf, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,\r
+    0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce,\r
+    0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,\r
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,\r
+    0xe1, 0xe3, 0xe4, 0xe5, 0xe6, 0xec, 0xed, 0xf0,\r
+    0xf1, 0xf2, 0xf3, 0xf5, 0xf6, 0xf8, 0xfa, 0x8a,\r
+    0x81, 0x8d, 0x8f, 0x9a, 0x8e, 0x98, 0x90, 0x9f,\r
+    0xaa, 0xc0, 0xff, 0x9d, 0x9e, 0xfd, 0xfe, 0x96,\r
+    0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86,\r
+    0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_CP1256 = {\r
+    CS_CP1256, read_sbcs, write_sbcs, &data_CS_CP1256\r
+};\r
+\r
+static const sbcs_data data_CS_CP1257 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, ERROR , 0x201a, ERROR , 0x201e, 0x2026, 0x2020, 0x2021,\r
+    ERROR , 0x2030, ERROR , 0x2039, ERROR , 0x00a8, 0x02c7, 0x00b8,\r
+    ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    ERROR , 0x2122, ERROR , 0x203a, ERROR , 0x00af, 0x02db, ERROR ,\r
+    0x00a0, ERROR , 0x00a2, 0x00a3, 0x00a4, ERROR , 0x00a6, 0x00a7,\r
+    0x00d8, 0x00a9, 0x0156, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00c6,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00f8, 0x00b9, 0x0157, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00e6,\r
+    0x0104, 0x012e, 0x0100, 0x0106, 0x00c4, 0x00c5, 0x0118, 0x0112,\r
+    0x010c, 0x00c9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012a, 0x013b,\r
+    0x0160, 0x0143, 0x0145, 0x00d3, 0x014c, 0x00d5, 0x00d6, 0x00d7,\r
+    0x0172, 0x0141, 0x015a, 0x016a, 0x00dc, 0x017b, 0x017d, 0x00df,\r
+    0x0105, 0x012f, 0x0101, 0x0107, 0x00e4, 0x00e5, 0x0119, 0x0113,\r
+    0x010d, 0x00e9, 0x017a, 0x0117, 0x0123, 0x0137, 0x012b, 0x013c,\r
+    0x0161, 0x0144, 0x0146, 0x00f3, 0x014d, 0x00f5, 0x00f6, 0x00f7,\r
+    0x0173, 0x0142, 0x015b, 0x016b, 0x00fc, 0x017c, 0x017e, 0x02d9\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa2, 0xa3, 0xa4, 0xa6, 0xa7, 0x8d, 0xa9,\r
+    0xab, 0xac, 0xad, 0xae, 0x9d, 0xb0, 0xb1, 0xb2,\r
+    0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0x8f, 0xb9, 0xbb,\r
+    0xbc, 0xbd, 0xbe, 0xc4, 0xc5, 0xaf, 0xc9, 0xd3,\r
+    0xd5, 0xd6, 0xd7, 0xa8, 0xdc, 0xdf, 0xe4, 0xe5,\r
+    0xbf, 0xe9, 0xf3, 0xf5, 0xf6, 0xf7, 0xb8, 0xfc,\r
+    0xc2, 0xe2, 0xc0, 0xe0, 0xc3, 0xe3, 0xc8, 0xe8,\r
+    0xc7, 0xe7, 0xcb, 0xeb, 0xc6, 0xe6, 0xcc, 0xec,\r
+    0xce, 0xee, 0xc1, 0xe1, 0xcd, 0xed, 0xcf, 0xef,\r
+    0xd9, 0xf9, 0xd1, 0xf1, 0xd2, 0xf2, 0xd4, 0xf4,\r
+    0xaa, 0xba, 0xda, 0xfa, 0xd0, 0xf0, 0xdb, 0xfb,\r
+    0xd8, 0xf8, 0xca, 0xea, 0xdd, 0xfd, 0xde, 0xfe,\r
+    0x8e, 0xff, 0x9e, 0x96, 0x97, 0x91, 0x92, 0x82,\r
+    0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89,\r
+    0x8b, 0x9b, 0x80, 0x99\r
+    },\r
+    244\r
+};\r
+const charset_spec charset_CS_CP1257 = {\r
+    CS_CP1257, read_sbcs, write_sbcs, &data_CS_CP1257\r
+};\r
+\r
+static const sbcs_data data_CS_CP1258 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,\r
+    0x02c6, 0x2030, ERROR , 0x2039, 0x0152, ERROR , ERROR , ERROR ,\r
+    ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,\r
+    0x02dc, 0x2122, ERROR , 0x203a, 0x0153, ERROR , ERROR , 0x0178,\r
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,\r
+    0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,\r
+    0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x0300, 0x00cd, 0x00ce, 0x00cf,\r
+    0x0110, 0x00d1, 0x0309, 0x00d3, 0x00d4, 0x01a0, 0x00d6, 0x00d7,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x01af, 0x0303, 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x0301, 0x00ed, 0x00ee, 0x00ef,\r
+    0x0111, 0x00f1, 0x0323, 0x00f3, 0x00f4, 0x01a1, 0x00f6, 0x00f7,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x01b0, 0x20ab, 0x00ff\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\r
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,\r
+    0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd1, 0xd3,\r
+    0xd4, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc,\r
+    0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xed, 0xee, 0xef, 0xf1,\r
+    0xf3, 0xf4, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,\r
+    0xfc, 0xff, 0xc3, 0xe3, 0xd0, 0xf0, 0x8c, 0x9c,\r
+    0x9f, 0x83, 0xd5, 0xf5, 0xdd, 0xfd, 0x88, 0x98,\r
+    0xcc, 0xec, 0xde, 0xd2, 0xf2, 0x96, 0x97, 0x91,\r
+    0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95,\r
+    0x85, 0x89, 0x8b, 0x9b, 0xfe, 0x80, 0x99\r
+    },\r
+    247\r
+};\r
+const charset_spec charset_CS_CP1258 = {\r
+    CS_CP1258, read_sbcs, write_sbcs, &data_CS_CP1258\r
+};\r
+\r
+static const sbcs_data data_CS_KOI8_R = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x2500, 0x2502, 0x250c, 0x2510, 0x2514, 0x2518, 0x251c, 0x2524,\r
+    0x252c, 0x2534, 0x253c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590,\r
+    0x2591, 0x2592, 0x2593, 0x2320, 0x25a0, 0x2219, 0x221a, 0x2248,\r
+    0x2264, 0x2265, 0x00a0, 0x2321, 0x00b0, 0x00b2, 0x00b7, 0x00f7,\r
+    0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556,\r
+    0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e,\r
+    0x255f, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565,\r
+    0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x00a9,\r
+    0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,\r
+    0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,\r
+    0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,\r
+    0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,\r
+    0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,\r
+    0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,\r
+    0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,\r
+    0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x9a, 0xbf, 0x9c, 0x9d, 0x9e, 0x9f, 0xb3, 0xe1,\r
+    0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa, 0xe9,\r
+    0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf2,\r
+    0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe, 0xfb,\r
+    0xfd, 0xff, 0xf9, 0xf8, 0xfc, 0xe0, 0xf1, 0xc1,\r
+    0xc2, 0xd7, 0xc7, 0xc4, 0xc5, 0xd6, 0xda, 0xc9,\r
+    0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd2,\r
+    0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde, 0xdb,\r
+    0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1, 0xa3,\r
+    0x95, 0x96, 0x97, 0x98, 0x99, 0x93, 0x9b, 0x80,\r
+    0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,\r
+    0x89, 0x8a, 0xa0, 0xa1, 0xa2, 0xa4, 0xa5, 0xa6,\r
+    0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae,\r
+    0xaf, 0xb0, 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0x8b,\r
+    0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x94\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_KOI8_R = {\r
+    CS_KOI8_R, read_sbcs, write_sbcs, &data_CS_KOI8_R\r
+};\r
+\r
+static const sbcs_data data_CS_KOI8_U = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x2500, 0x2502, 0x250c, 0x2510, 0x2514, 0x2518, 0x251c, 0x2524,\r
+    0x252c, 0x2534, 0x253c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590,\r
+    0x2591, 0x2592, 0x2593, 0x2320, 0x25a0, 0x2219, 0x221a, 0x2248,\r
+    0x2264, 0x2265, 0x00a0, 0x2321, 0x00b0, 0x00b2, 0x00b7, 0x00f7,\r
+    0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457,\r
+    0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x0491, 0x255d, 0x255e,\r
+    0x255f, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407,\r
+    0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x0490, 0x256c, 0x00a9,\r
+    0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,\r
+    0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,\r
+    0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,\r
+    0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,\r
+    0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,\r
+    0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,\r
+    0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,\r
+    0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x9a, 0xbf, 0x9c, 0x9d, 0x9e, 0x9f, 0xb3, 0xb4,\r
+    0xb6, 0xb7, 0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5,\r
+    0xf6, 0xfa, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee,\r
+    0xef, 0xf0, 0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8,\r
+    0xe3, 0xfe, 0xfb, 0xfd, 0xff, 0xf9, 0xf8, 0xfc,\r
+    0xe0, 0xf1, 0xc1, 0xc2, 0xd7, 0xc7, 0xc4, 0xc5,\r
+    0xd6, 0xda, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce,\r
+    0xcf, 0xd0, 0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8,\r
+    0xc3, 0xde, 0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc,\r
+    0xc0, 0xd1, 0xa3, 0xa4, 0xa6, 0xa7, 0xbd, 0xad,\r
+    0x95, 0x96, 0x97, 0x98, 0x99, 0x93, 0x9b, 0x80,\r
+    0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,\r
+    0x89, 0x8a, 0xa0, 0xa1, 0xa2, 0xa5, 0xa8, 0xa9,\r
+    0xaa, 0xab, 0xac, 0xae, 0xaf, 0xb0, 0xb1, 0xb2,\r
+    0xb5, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbe, 0x8b,\r
+    0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x94\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_KOI8_U = {\r
+    CS_KOI8_U, read_sbcs, write_sbcs, &data_CS_KOI8_U\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_ROMAN = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x00ff, 0x0178, 0x2044, 0x20ac, 0x2039, 0x203a, 0xfb01, 0xfb02,\r
+    0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9,\r
+    0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab,\r
+    0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb,\r
+    0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9,\r
+    0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84,\r
+    0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2,\r
+    0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a,\r
+    0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93,\r
+    0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b,\r
+    0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xd8,\r
+    0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9,\r
+    0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0,\r
+    0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0,\r
+    0xe0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda, 0xdb,\r
+    0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba,\r
+    0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0, 0xde, 0xdf\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_ROMAN = {\r
+    CS_MAC_ROMAN, read_sbcs, write_sbcs, &data_CS_MAC_ROMAN\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_TURKISH = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x00ff, 0x0178, 0x011e, 0x011f, 0x0130, 0x0131, 0x015e, 0x015f,\r
+    0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, ERROR , 0x02c6, 0x02dc,\r
+    0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9,\r
+    0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab,\r
+    0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb,\r
+    0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9,\r
+    0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84,\r
+    0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2,\r
+    0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a,\r
+    0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93,\r
+    0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b,\r
+    0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xd8,\r
+    0xda, 0xdb, 0xdc, 0xdd, 0xce, 0xcf, 0xde, 0xdf,\r
+    0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe,\r
+    0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5,\r
+    0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xe0, 0xa5, 0xc9,\r
+    0xe4, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0,\r
+    0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0\r
+    },\r
+    255\r
+};\r
+const charset_spec charset_CS_MAC_TURKISH = {\r
+    CS_MAC_TURKISH, read_sbcs, write_sbcs, &data_CS_MAC_TURKISH\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_CROATIAN = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x0160, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x017d, 0x00d8,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x2206, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x0161, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x017e, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x0106, 0x00ab,\r
+    0x010c, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x0110, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0xf8ff, 0x00a9, 0x2044, 0x20ac, 0x2039, 0x203a, 0x00c6, 0x00bb,\r
+    0x2013, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x0107, 0x00c1,\r
+    0x010d, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0x0111, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x03c0, 0x00cb, 0x02da, 0x00b8, 0x00ca, 0x00e6, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xa4, 0xac, 0xd9, 0xbb,\r
+    0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5,\r
+    0xa6, 0xe1, 0xfc, 0xbc, 0xdf, 0xc0, 0xcb, 0xe7,\r
+    0xe5, 0xcc, 0x80, 0x81, 0xde, 0x82, 0xe9, 0x83,\r
+    0xfd, 0xfa, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1,\r
+    0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3,\r
+    0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c,\r
+    0xfe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92,\r
+    0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a,\r
+    0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xc6, 0xe6,\r
+    0xc8, 0xe8, 0xd0, 0xf0, 0xf5, 0xce, 0xcf, 0xa9,\r
+    0xb9, 0xae, 0xbe, 0xc4, 0xf6, 0xff, 0xfb, 0xf7,\r
+    0xbd, 0xf9, 0xe0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2,\r
+    0xd3, 0xe3, 0xa0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd,\r
+    0xda, 0xdb, 0xaa, 0xb6, 0xb4, 0xb8, 0xb7, 0xc3,\r
+    0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xd8\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_CROATIAN = {\r
+    CS_MAC_CROATIAN, read_sbcs, write_sbcs, &data_CS_MAC_CROATIAN\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_ICELAND = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x00ff, 0x0178, 0x2044, 0x20ac, 0x00d0, 0x00f0, 0x00de, 0x00fe,\r
+    0x00fd, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9,\r
+    0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab,\r
+    0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb,\r
+    0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9,\r
+    0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0xdc,\r
+    0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4,\r
+    0xf2, 0xf3, 0x86, 0xa0, 0xde, 0xa7, 0x88, 0x87,\r
+    0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e,\r
+    0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0xdd, 0x96,\r
+    0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d,\r
+    0x9c, 0x9e, 0x9f, 0xe0, 0xdf, 0xd8, 0xf5, 0xce,\r
+    0xcf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa, 0xfb,\r
+    0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4,\r
+    0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa5, 0xc9, 0xe4,\r
+    0xda, 0xdb, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3,\r
+    0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_ICELAND = {\r
+    CS_MAC_ICELAND, read_sbcs, write_sbcs, &data_CS_MAC_ICELAND\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_ROMANIAN = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x0102, 0x0218,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x0103, 0x0219,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x00ff, 0x0178, 0x2044, 0x20ac, 0x2039, 0x203a, 0x021a, 0x021b,\r
+    0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9,\r
+    0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab,\r
+    0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb,\r
+    0xe7, 0xe5, 0xcc, 0x80, 0x81, 0x82, 0xe9, 0x83,\r
+    0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1,\r
+    0xee, 0xef, 0xcd, 0x85, 0xf4, 0xf2, 0xf3, 0x86,\r
+    0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0x8d,\r
+    0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95,\r
+    0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0x9d,\r
+    0x9c, 0x9e, 0x9f, 0xd8, 0xae, 0xbe, 0xf5, 0xce,\r
+    0xcf, 0xd9, 0xc4, 0xaf, 0xbf, 0xde, 0xdf, 0xf6,\r
+    0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd,\r
+    0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3,\r
+    0xe3, 0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd,\r
+    0xda, 0xdb, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3,\r
+    0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_ROMANIAN = {\r
+    CS_MAC_ROMANIAN, read_sbcs, write_sbcs, &data_CS_MAC_ROMANIAN\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_GREEK = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00b9, 0x00b2, 0x00c9, 0x00b3, 0x00d6, 0x00dc, 0x0385,\r
+    0x00e0, 0x00e2, 0x00e4, 0x0384, 0x00a8, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00a3, 0x2122, 0x00ee, 0x00ef, 0x2022, 0x00bd,\r
+    0x2030, 0x00f4, 0x00f6, 0x00a6, 0x20ac, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03a0, 0x00df,\r
+    0x00ae, 0x00a9, 0x03a3, 0x03aa, 0x00a7, 0x2260, 0x00b0, 0x00b7,\r
+    0x0391, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x0392, 0x0395, 0x0396,\r
+    0x0397, 0x0399, 0x039a, 0x039c, 0x03a6, 0x03ab, 0x03a8, 0x03a9,\r
+    0x03ac, 0x039d, 0x00ac, 0x039f, 0x03a1, 0x2248, 0x03a4, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x03a5, 0x03a7, 0x0386, 0x0388, 0x0153,\r
+    0x2013, 0x2015, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x0389,\r
+    0x038a, 0x038c, 0x038e, 0x03ad, 0x03ae, 0x03af, 0x03cc, 0x038f,\r
+    0x03cd, 0x03b1, 0x03b2, 0x03c8, 0x03b4, 0x03b5, 0x03c6, 0x03b3,\r
+    0x03b7, 0x03b9, 0x03be, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf,\r
+    0x03c0, 0x03ce, 0x03c1, 0x03c3, 0x03c4, 0x03b8, 0x03c9, 0x03c2,\r
+    0x03c7, 0x03c5, 0x03b6, 0x03ca, 0x03cb, 0x0390, 0x03b0, 0x00ad\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0x92, 0xb4, 0x9b, 0xac, 0x8c, 0xa9, 0xc7,\r
+    0xc2, 0xff, 0xa8, 0xae, 0xb1, 0x82, 0x84, 0xaf,\r
+    0x81, 0xc8, 0x97, 0x80, 0x83, 0x85, 0x86, 0xa7,\r
+    0x88, 0x89, 0x8a, 0x8d, 0x8f, 0x8e, 0x90, 0x91,\r
+    0x94, 0x95, 0x99, 0x9a, 0xd6, 0x9d, 0x9e, 0x9f,\r
+    0xcf, 0x8b, 0x87, 0xcd, 0xce, 0xd7, 0xd8, 0xd9,\r
+    0xda, 0xdf, 0xfd, 0xb0, 0xb5, 0xa1, 0xa2, 0xb6,\r
+    0xb7, 0xb8, 0xa3, 0xb9, 0xba, 0xa4, 0xbb, 0xc1,\r
+    0xa5, 0xc3, 0xa6, 0xc4, 0xaa, 0xc6, 0xcb, 0xbc,\r
+    0xcc, 0xbe, 0xbf, 0xab, 0xbd, 0xc0, 0xdb, 0xdc,\r
+    0xdd, 0xfe, 0xe1, 0xe2, 0xe7, 0xe4, 0xe5, 0xfa,\r
+    0xe8, 0xf5, 0xe9, 0xeb, 0xec, 0xed, 0xee, 0xea,\r
+    0xef, 0xf0, 0xf2, 0xf7, 0xf3, 0xf4, 0xf9, 0xe6,\r
+    0xf8, 0xe3, 0xf6, 0xfb, 0xfc, 0xde, 0xe0, 0xf1,\r
+    0xd0, 0xd1, 0xd4, 0xd5, 0xd2, 0xd3, 0xa0, 0x96,\r
+    0xc9, 0x98, 0x9c, 0x93, 0xc5, 0xad, 0xb2, 0xb3\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_GREEK = {\r
+    CS_MAC_GREEK, read_sbcs, write_sbcs, &data_CS_MAC_GREEK\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_CYRILLIC = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+    0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,\r
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+    0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,\r
+    0x2020, 0x00b0, 0x0490, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x0406,\r
+    0x00ae, 0x00a9, 0x2122, 0x0402, 0x0452, 0x2260, 0x0403, 0x0453,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x0456, 0x00b5, 0x0491, 0x0408,\r
+    0x0404, 0x0454, 0x0407, 0x0457, 0x0409, 0x0459, 0x040a, 0x045a,\r
+    0x0458, 0x0405, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x040b, 0x045b, 0x040c, 0x045c, 0x0455,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x201e,\r
+    0x040e, 0x045e, 0x040f, 0x045f, 0x2116, 0x0401, 0x0451, 0x044f,\r
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+    0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,\r
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+    0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x20ac\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xa3, 0xa4, 0xa9, 0xc7, 0xc2, 0xa8, 0xa1,\r
+    0xb1, 0xb5, 0xa6, 0xc8, 0xd6, 0xc4, 0xdd, 0xab,\r
+    0xae, 0xb8, 0xc1, 0xa7, 0xba, 0xb7, 0xbc, 0xbe,\r
+    0xcb, 0xcd, 0xd8, 0xda, 0x80, 0x81, 0x82, 0x83,\r
+    0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b,\r
+    0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,\r
+    0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,\r
+    0x9c, 0x9d, 0x9e, 0x9f, 0xe0, 0xe1, 0xe2, 0xe3,\r
+    0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb,\r
+    0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,\r
+    0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,\r
+    0xfc, 0xfd, 0xfe, 0xdf, 0xde, 0xac, 0xaf, 0xb9,\r
+    0xcf, 0xb4, 0xbb, 0xc0, 0xbd, 0xbf, 0xcc, 0xce,\r
+    0xd9, 0xdb, 0xa2, 0xb6, 0xd0, 0xd1, 0xd4, 0xd5,\r
+    0xd2, 0xd3, 0xd7, 0xa0, 0xa5, 0xc9, 0xff, 0xdc,\r
+    0xaa, 0xc6, 0xc3, 0xb0, 0xc5, 0xad, 0xb2, 0xb3\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_CYRILLIC = {\r
+    CS_MAC_CYRILLIC, read_sbcs, write_sbcs, &data_CS_MAC_CYRILLIC\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_THAI = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00ab, 0x00bb, 0x2026, 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c,\r
+    0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x201c, 0x201d, 0x0e4d,\r
+    ERROR , 0x2022, 0x0e31, 0x0e47, 0x0e34, 0x0e35, 0x0e36, 0x0e37,\r
+    0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x2018, 0x2019, ERROR ,\r
+    0x00a0, 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07,\r
+    0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f,\r
+    0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17,\r
+    0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f,\r
+    0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27,\r
+    0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f,\r
+    0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37,\r
+    0x0e38, 0x0e39, 0x0e3a, 0x2060, 0x200b, 0x2013, 0x2014, 0x0e3f,\r
+    0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47,\r
+    0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x2122, 0x0e4f,\r
+    0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57,\r
+    0x0e58, 0x0e59, 0x00ae, 0x00a9, ERROR , ERROR , ERROR , ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xa0, 0xfb, 0x80, 0xfa, 0x81, 0xa1, 0xa2, 0xa3,\r
+    0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab,\r
+    0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,\r
+    0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb,\r
+    0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3,\r
+    0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,\r
+    0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3,\r
+    0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdf,\r
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\r
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf0,\r
+    0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,\r
+    0xf9, 0xdc, 0xdd, 0xde, 0x9d, 0x9e, 0x8d, 0x8e,\r
+    0x91, 0x82, 0xdb, 0xee\r
+    },\r
+    228\r
+};\r
+const charset_spec charset_CS_MAC_THAI = {\r
+    CS_MAC_THAI, read_sbcs, write_sbcs, &data_CS_MAC_THAI\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_CENTEURO = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x0100, 0x0101, 0x00c9, 0x0104, 0x00d6, 0x00dc, 0x00e1,\r
+    0x0105, 0x010c, 0x00e4, 0x010d, 0x0106, 0x0107, 0x00e9, 0x0179,\r
+    0x017a, 0x010e, 0x00ed, 0x010f, 0x0112, 0x0113, 0x0116, 0x00f3,\r
+    0x0117, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x011a, 0x011b, 0x00fc,\r
+    0x2020, 0x00b0, 0x0118, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x0119, 0x00a8, 0x2260, 0x0123, 0x012e,\r
+    0x012f, 0x012a, 0x2264, 0x2265, 0x012b, 0x0136, 0x2202, 0x2211,\r
+    0x0142, 0x013b, 0x013c, 0x013d, 0x013e, 0x0139, 0x013a, 0x0145,\r
+    0x0146, 0x0143, 0x00ac, 0x221a, 0x0144, 0x0147, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x0148, 0x0150, 0x00d5, 0x0151, 0x014c,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x014d, 0x0154, 0x0155, 0x0158, 0x2039, 0x203a, 0x0159, 0x0156,\r
+    0x0157, 0x0160, 0x201a, 0x201e, 0x0161, 0x015a, 0x015b, 0x00c1,\r
+    0x0164, 0x0165, 0x00cd, 0x017d, 0x017e, 0x016a, 0x00d3, 0x00d4,\r
+    0x016b, 0x016e, 0x00da, 0x016f, 0x0170, 0x0171, 0x0172, 0x0173,\r
+    0x00dd, 0x00fd, 0x0137, 0x017b, 0x0141, 0x017c, 0x0122, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xa3, 0xa4, 0xac, 0xa9, 0xc7, 0xc2, 0xa8,\r
+    0xa1, 0xa6, 0xc8, 0xe7, 0x80, 0x83, 0xea, 0xee,\r
+    0xef, 0xcd, 0x85, 0xf2, 0x86, 0xf8, 0xa7, 0x87,\r
+    0x8a, 0x8e, 0x92, 0x97, 0x99, 0x9b, 0x9a, 0xd6,\r
+    0x9c, 0x9f, 0xf9, 0x81, 0x82, 0x84, 0x88, 0x8c,\r
+    0x8d, 0x89, 0x8b, 0x91, 0x93, 0x94, 0x95, 0x96,\r
+    0x98, 0xa2, 0xab, 0x9d, 0x9e, 0xfe, 0xae, 0xb1,\r
+    0xb4, 0xaf, 0xb0, 0xb5, 0xfa, 0xbd, 0xbe, 0xb9,\r
+    0xba, 0xbb, 0xbc, 0xfc, 0xb8, 0xc1, 0xc4, 0xbf,\r
+    0xc0, 0xc5, 0xcb, 0xcf, 0xd8, 0xcc, 0xce, 0xd9,\r
+    0xda, 0xdf, 0xe0, 0xdb, 0xde, 0xe5, 0xe6, 0xe1,\r
+    0xe4, 0xe8, 0xe9, 0xed, 0xf0, 0xf1, 0xf3, 0xf4,\r
+    0xf5, 0xf6, 0xf7, 0x8f, 0x90, 0xfb, 0xfd, 0xeb,\r
+    0xec, 0xff, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2,\r
+    0xd3, 0xe3, 0xa0, 0xa5, 0xc9, 0xdc, 0xdd, 0xaa,\r
+    0xb6, 0xc6, 0xb7, 0xc3, 0xad, 0xb2, 0xb3, 0xd7\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_CENTEURO = {\r
+    CS_MAC_CENTEURO, read_sbcs, write_sbcs, &data_CS_MAC_CENTEURO\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_SYMBOL = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x2200, 0x0023, 0x2203, 0x0025, 0x0026, 0x220d,\r
+    0x0028, 0x0029, 0x2217, 0x002b, 0x002c, 0x2212, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x2245, 0x0391, 0x0392, 0x03a7, 0x0394, 0x0395, 0x03a6, 0x0393,\r
+    0x0397, 0x0399, 0x03d1, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f,\r
+    0x03a0, 0x0398, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03c2, 0x03a9,\r
+    0x039e, 0x03a8, 0x0396, 0x005b, 0x2234, 0x005d, 0x22a5, 0x005f,\r
+    0xf8e5, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3,\r
+    0x03b7, 0x03b9, 0x03d5, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf,\r
+    0x03c0, 0x03b8, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03d6, 0x03c9,\r
+    0x03be, 0x03c8, 0x03b6, 0x007b, 0x007c, 0x007d, 0x223c, 0x007f,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    0x20ac, 0x03d2, 0x2032, 0x2264, 0x2044, 0x221e, 0x0192, 0x2663,\r
+    0x2666, 0x2665, 0x2660, 0x2194, 0x2190, 0x2191, 0x2192, 0x2193,\r
+    0x00b0, 0x00b1, 0x2033, 0x2265, 0x00d7, 0x221d, 0x2202, 0x2022,\r
+    0x00f7, 0x2260, 0x2261, 0x2248, 0x2026, 0xf8e6, 0x23af, 0x21b5,\r
+    0x2135, 0x2111, 0x211c, 0x2118, 0x2297, 0x2295, 0x2205, 0x2229,\r
+    0x222a, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208, 0x2209,\r
+    0x2220, 0x2207, 0x00ae, 0x00a9, 0x2122, 0x220f, 0x221a, 0x22c5,\r
+    0x00ac, 0x2227, 0x2228, 0x21d4, 0x21d0, 0x21d1, 0x21d2, 0x21d3,\r
+    0x22c4, 0x3008, 0x00ae, 0x00a9, 0x2122, 0x2211, 0x239b, 0x239c,\r
+    0x239d, 0x23a1, 0x23a2, 0x23a3, 0x23a7, 0x23a8, 0x23a9, 0x23aa,\r
+    0xf8ff, 0x3009, 0x222b, 0x2320, 0x23ae, 0x2321, 0x239e, 0x239f,\r
+    0x23a0, 0x23a4, 0x23a5, 0x23a6, 0x23ab, 0x23ac, 0x23ad, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b,\r
+    0x2c, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,\r
+    0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c,\r
+    0x3d, 0x3e, 0x3f, 0x5b, 0x5d, 0x5f, 0x7b, 0x7c,\r
+    0x7d, 0x7f, 0xd3, 0xd8, 0xd2, 0xb0, 0xb1, 0xb4,\r
+    0xb8, 0xa6, 0x41, 0x42, 0x47, 0x44, 0x45, 0x5a,\r
+    0x48, 0x51, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x58,\r
+    0x4f, 0x50, 0x52, 0x53, 0x54, 0x55, 0x46, 0x43,\r
+    0x59, 0x57, 0x61, 0x62, 0x67, 0x64, 0x65, 0x7a,\r
+    0x68, 0x71, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x78,\r
+    0x6f, 0x70, 0x72, 0x56, 0x73, 0x74, 0x75, 0x66,\r
+    0x63, 0x79, 0x77, 0x4a, 0xa1, 0x6a, 0x76, 0xb7,\r
+    0xbc, 0xa2, 0xb2, 0xa4, 0xa0, 0xc1, 0xc3, 0xc2,\r
+    0xd4, 0xc0, 0xac, 0xad, 0xae, 0xaf, 0xab, 0xbf,\r
+    0xdc, 0xdd, 0xde, 0xdf, 0xdb, 0x22, 0xb6, 0x24,\r
+    0xc6, 0xd1, 0xce, 0xcf, 0x27, 0xd5, 0xe5, 0x2d,\r
+    0x2a, 0xd6, 0xb5, 0xa5, 0xd0, 0xd9, 0xda, 0xc7,\r
+    0xc8, 0xf2, 0x5c, 0x7e, 0x40, 0xbb, 0xb9, 0xba,\r
+    0xa3, 0xb3, 0xcc, 0xc9, 0xcb, 0xcd, 0xca, 0xc5,\r
+    0xc4, 0x5e, 0xe0, 0xd7, 0xf3, 0xf5, 0xe6, 0xe7,\r
+    0xe8, 0xf6, 0xf7, 0xf8, 0xe9, 0xea, 0xeb, 0xf9,\r
+    0xfa, 0xfb, 0xec, 0xed, 0xee, 0xef, 0xfc, 0xfd,\r
+    0xfe, 0xf4, 0xbe, 0xaa, 0xa7, 0xa9, 0xa8, 0xe1,\r
+    0xf1, 0x60, 0xbd, 0xf0\r
+    },\r
+    220\r
+};\r
+const charset_spec charset_CS_MAC_SYMBOL = {\r
+    CS_MAC_SYMBOL, read_sbcs, write_sbcs, &data_CS_MAC_SYMBOL\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_DINGBATS = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x2701, 0x2702, 0x2703, 0x2704, 0x260e, 0x2706, 0x2707,\r
+    0x2708, 0x2709, 0x261b, 0x261e, 0x270c, 0x270d, 0x270e, 0x270f,\r
+    0x2710, 0x2711, 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, 0x2717,\r
+    0x2718, 0x2719, 0x271a, 0x271b, 0x271c, 0x271d, 0x271e, 0x271f,\r
+    0x2720, 0x2721, 0x2722, 0x2723, 0x2724, 0x2725, 0x2726, 0x2727,\r
+    0x2605, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e, 0x272f,\r
+    0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737,\r
+    0x2738, 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f,\r
+    0x2740, 0x2741, 0x2742, 0x2743, 0x2744, 0x2745, 0x2746, 0x2747,\r
+    0x2748, 0x2749, 0x274a, 0x274b, 0x25cf, 0x274d, 0x25a0, 0x274f,\r
+    0x2750, 0x2751, 0x2752, 0x25b2, 0x25bc, 0x25c6, 0x2756, 0x25d7,\r
+    0x2758, 0x2759, 0x275a, 0x275b, 0x275c, 0x275d, 0x275e, 0x007f,\r
+    0x2768, 0x2769, 0x276a, 0x276b, 0x276c, 0x276d, 0x276e, 0x276f,\r
+    0x2770, 0x2771, 0x2772, 0x2773, 0x2774, 0x2775, ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR ,\r
+    ERROR , 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767,\r
+    0x2663, 0x2666, 0x2665, 0x2660, 0x2460, 0x2461, 0x2462, 0x2463,\r
+    0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x2776, 0x2777,\r
+    0x2778, 0x2779, 0x277a, 0x277b, 0x277c, 0x277d, 0x277e, 0x277f,\r
+    0x2780, 0x2781, 0x2782, 0x2783, 0x2784, 0x2785, 0x2786, 0x2787,\r
+    0x2788, 0x2789, 0x278a, 0x278b, 0x278c, 0x278d, 0x278e, 0x278f,\r
+    0x2790, 0x2791, 0x2792, 0x2793, 0x2794, 0x2192, 0x2194, 0x2195,\r
+    0x2798, 0x2799, 0x279a, 0x279b, 0x279c, 0x279d, 0x279e, 0x279f,\r
+    0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5, 0x27a6, 0x27a7,\r
+    0x27a8, 0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae, 0x27af,\r
+    ERROR , 0x27b1, 0x27b2, 0x27b3, 0x27b4, 0x27b5, 0x27b6, 0x27b7,\r
+    0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd, 0x27be, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x7f, 0xd5, 0xd6, 0xd7, 0xac, 0xad, 0xae,\r
+    0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0x6e,\r
+    0x73, 0x74, 0x75, 0x6c, 0x77, 0x48, 0x25, 0x2a,\r
+    0x2b, 0xab, 0xa8, 0xaa, 0xa9, 0x21, 0x22, 0x23,\r
+    0x24, 0x26, 0x27, 0x28, 0x29, 0x2c, 0x2d, 0x2e,\r
+    0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,\r
+    0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,\r
+    0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,\r
+    0x47, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6f, 0x70, 0x71,\r
+    0x72, 0x76, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d,\r
+    0x7e, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0xb6, 0xb7,\r
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\r
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\r
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\r
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd8, 0xd9, 0xda,\r
+    0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2,\r
+    0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,\r
+    0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3,\r
+    0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,\r
+    0xfc, 0xfd, 0xfe\r
+    },\r
+    235\r
+};\r
+const charset_spec charset_CS_MAC_DINGBATS = {\r
+    CS_MAC_DINGBATS, read_sbcs, write_sbcs, &data_CS_MAC_DINGBATS\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_ROMAN_OLD = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x00ff, 0x0178, 0x2044, 0x00a4, 0x2039, 0x203a, 0xfb01, 0xfb02,\r
+    0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xa4, 0xac,\r
+    0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1,\r
+    0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0,\r
+    0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82,\r
+    0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec,\r
+    0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4,\r
+    0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b,\r
+    0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91,\r
+    0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99,\r
+    0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f,\r
+    0xd8, 0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xf6, 0xff,\r
+    0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9,\r
+    0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3,\r
+    0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda,\r
+    0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba,\r
+    0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0, 0xde, 0xdf\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_ROMAN_OLD = {\r
+    CS_MAC_ROMAN_OLD, read_sbcs, write_sbcs, &data_CS_MAC_ROMAN_OLD\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_CROATIAN_OLD = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x0160, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x017d, 0x00d8,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x2206, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x0161, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x017e, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x0106, 0x00ab,\r
+    0x010c, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x0110, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0xf8ff, 0x00a9, 0x2044, 0x00a4, 0x2039, 0x203a, 0x00c6, 0x00bb,\r
+    0x2013, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x0107, 0x00c1,\r
+    0x010d, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0x0111, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x03c0, 0x00cb, 0x02da, 0x00b8, 0x00ca, 0x00e6, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xa4, 0xac, 0xd9,\r
+    0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab,\r
+    0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xdf, 0xc0, 0xcb,\r
+    0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xde, 0x82, 0xe9,\r
+    0x83, 0xfd, 0xfa, 0xed, 0xea, 0xeb, 0xec, 0x84,\r
+    0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2,\r
+    0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a,\r
+    0x8c, 0xfe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93,\r
+    0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b,\r
+    0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xc6,\r
+    0xe6, 0xc8, 0xe8, 0xd0, 0xf0, 0xf5, 0xce, 0xcf,\r
+    0xa9, 0xb9, 0xae, 0xbe, 0xc4, 0xf6, 0xff, 0xfb,\r
+    0xf7, 0xbd, 0xf9, 0xe0, 0xd1, 0xd4, 0xd5, 0xe2,\r
+    0xd2, 0xd3, 0xe3, 0xa0, 0xa5, 0xc9, 0xe4, 0xdc,\r
+    0xdd, 0xda, 0xaa, 0xb6, 0xb4, 0xb8, 0xb7, 0xc3,\r
+    0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xd8\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_CROATIAN_OLD = {\r
+    CS_MAC_CROATIAN_OLD, read_sbcs, write_sbcs, &data_CS_MAC_CROATIAN_OLD\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_ICELAND_OLD = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x00ff, 0x0178, 0x2044, 0x00a4, 0x00d0, 0x00f0, 0x00de, 0x00fe,\r
+    0x00fd, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xa4, 0xac,\r
+    0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1,\r
+    0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0,\r
+    0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82,\r
+    0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec,\r
+    0xdc, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf,\r
+    0xf4, 0xf2, 0xf3, 0x86, 0xa0, 0xde, 0xa7, 0x88,\r
+    0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f,\r
+    0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0xdd,\r
+    0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf,\r
+    0x9d, 0x9c, 0x9e, 0x9f, 0xe0, 0xdf, 0xd8, 0xf5,\r
+    0xce, 0xcf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa,\r
+    0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1,\r
+    0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa5, 0xc9,\r
+    0xe4, 0xda, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3,\r
+    0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_ICELAND_OLD = {\r
+    CS_MAC_ICELAND_OLD, read_sbcs, write_sbcs, &data_CS_MAC_ICELAND_OLD\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_ROMANIAN_OLD = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x0102, 0x0218,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,\r
+    0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x0103, 0x0219,\r
+    0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,\r
+    0x00ff, 0x0178, 0x2044, 0x00a4, 0x2039, 0x203a, 0x021a, 0x021b,\r
+    0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,\r
+    0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xa4, 0xac,\r
+    0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1,\r
+    0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0,\r
+    0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0x82, 0xe9,\r
+    0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84,\r
+    0xf1, 0xee, 0xef, 0xcd, 0x85, 0xf4, 0xf2, 0xf3,\r
+    0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c,\r
+    0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94,\r
+    0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6,\r
+    0x9d, 0x9c, 0x9e, 0x9f, 0xd8, 0xae, 0xbe, 0xf5,\r
+    0xce, 0xcf, 0xd9, 0xc4, 0xaf, 0xbf, 0xde, 0xdf,\r
+    0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd,\r
+    0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2,\r
+    0xd3, 0xe3, 0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xdc,\r
+    0xdd, 0xda, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3,\r
+    0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_ROMANIAN_OLD = {\r
+    CS_MAC_ROMANIAN_OLD, read_sbcs, write_sbcs, &data_CS_MAC_ROMANIAN_OLD\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_GREEK_OLD = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x00c4, 0x00b9, 0x00b2, 0x00c9, 0x00b3, 0x00d6, 0x00dc, 0x0385,\r
+    0x00e0, 0x00e2, 0x00e4, 0x0384, 0x00a8, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00a3, 0x2122, 0x00ee, 0x00ef, 0x2022, 0x00bd,\r
+    0x2030, 0x00f4, 0x00f6, 0x00a6, 0x00ad, 0x00f9, 0x00fb, 0x00fc,\r
+    0x2020, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03a0, 0x00df,\r
+    0x00ae, 0x00a9, 0x03a3, 0x03aa, 0x00a7, 0x2260, 0x00b0, 0x00b7,\r
+    0x0391, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x0392, 0x0395, 0x0396,\r
+    0x0397, 0x0399, 0x039a, 0x039c, 0x03a6, 0x03ab, 0x03a8, 0x03a9,\r
+    0x03ac, 0x039d, 0x00ac, 0x039f, 0x03a1, 0x2248, 0x03a4, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x03a5, 0x03a7, 0x0386, 0x0388, 0x0153,\r
+    0x2013, 0x2015, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x0389,\r
+    0x038a, 0x038c, 0x038e, 0x03ad, 0x03ae, 0x03af, 0x03cc, 0x038f,\r
+    0x03cd, 0x03b1, 0x03b2, 0x03c8, 0x03b4, 0x03b5, 0x03c6, 0x03b3,\r
+    0x03b7, 0x03b9, 0x03be, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf,\r
+    0x03c0, 0x03ce, 0x03c1, 0x03c3, 0x03c4, 0x03b8, 0x03c9, 0x03c2,\r
+    0x03c7, 0x03c5, 0x03b6, 0x03ca, 0x03cb, 0x0390, 0x03b0, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0x92, 0xb4, 0x9b, 0xac, 0x8c, 0xa9, 0xc7,\r
+    0xc2, 0x9c, 0xa8, 0xae, 0xb1, 0x82, 0x84, 0xaf,\r
+    0x81, 0xc8, 0x97, 0x80, 0x83, 0x85, 0x86, 0xa7,\r
+    0x88, 0x89, 0x8a, 0x8d, 0x8f, 0x8e, 0x90, 0x91,\r
+    0x94, 0x95, 0x99, 0x9a, 0xd6, 0x9d, 0x9e, 0x9f,\r
+    0xcf, 0x8b, 0x87, 0xcd, 0xce, 0xd7, 0xd8, 0xd9,\r
+    0xda, 0xdf, 0xfd, 0xb0, 0xb5, 0xa1, 0xa2, 0xb6,\r
+    0xb7, 0xb8, 0xa3, 0xb9, 0xba, 0xa4, 0xbb, 0xc1,\r
+    0xa5, 0xc3, 0xa6, 0xc4, 0xaa, 0xc6, 0xcb, 0xbc,\r
+    0xcc, 0xbe, 0xbf, 0xab, 0xbd, 0xc0, 0xdb, 0xdc,\r
+    0xdd, 0xfe, 0xe1, 0xe2, 0xe7, 0xe4, 0xe5, 0xfa,\r
+    0xe8, 0xf5, 0xe9, 0xeb, 0xec, 0xed, 0xee, 0xea,\r
+    0xef, 0xf0, 0xf2, 0xf7, 0xf3, 0xf4, 0xf9, 0xe6,\r
+    0xf8, 0xe3, 0xf6, 0xfb, 0xfc, 0xde, 0xe0, 0xf1,\r
+    0xd0, 0xd1, 0xd4, 0xd5, 0xd2, 0xd3, 0xa0, 0x96,\r
+    0xc9, 0x98, 0x93, 0xc5, 0xad, 0xb2, 0xb3\r
+    },\r
+    255\r
+};\r
+const charset_spec charset_CS_MAC_GREEK_OLD = {\r
+    CS_MAC_GREEK_OLD, read_sbcs, write_sbcs, &data_CS_MAC_GREEK_OLD\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_CYRILLIC_OLD = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+    0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,\r
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+    0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,\r
+    0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x0406,\r
+    0x00ae, 0x00a9, 0x2122, 0x0402, 0x0452, 0x2260, 0x0403, 0x0453,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x0456, 0x00b5, 0x2022, 0x0408,\r
+    0x0404, 0x0454, 0x0407, 0x0457, 0x0409, 0x0459, 0x040a, 0x045a,\r
+    0x0458, 0x0405, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x040b, 0x045b, 0x040c, 0x045c, 0x0455,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x201e,\r
+    0x040e, 0x045e, 0x040f, 0x045f, 0x2116, 0x0401, 0x0451, 0x044f,\r
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+    0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,\r
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+    0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x00a4\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xa2, 0xa3, 0xff, 0xa4, 0xa9, 0xc7, 0xc2,\r
+    0xa8, 0xa1, 0xb1, 0xb5, 0xa6, 0xc8, 0xd6, 0xc4,\r
+    0xdd, 0xab, 0xae, 0xb8, 0xc1, 0xa7, 0xba, 0xb7,\r
+    0xbc, 0xbe, 0xcb, 0xcd, 0xd8, 0xda, 0x80, 0x81,\r
+    0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,\r
+    0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91,\r
+    0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,\r
+    0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xe0, 0xe1,\r
+    0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,\r
+    0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,\r
+    0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,\r
+    0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, 0xde, 0xac,\r
+    0xaf, 0xb9, 0xcf, 0xb4, 0xbb, 0xc0, 0xbd, 0xbf,\r
+    0xcc, 0xce, 0xd9, 0xdb, 0xd0, 0xd1, 0xd4, 0xd5,\r
+    0xd2, 0xd3, 0xd7, 0xa0, 0xa5, 0xc9, 0xdc, 0xaa,\r
+    0xc6, 0xc3, 0xb0, 0xc5, 0xad, 0xb2, 0xb3\r
+    },\r
+    255\r
+};\r
+const charset_spec charset_CS_MAC_CYRILLIC_OLD = {\r
+    CS_MAC_CYRILLIC_OLD, read_sbcs, write_sbcs, &data_CS_MAC_CYRILLIC_OLD\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_UKRAINE = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+    0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,\r
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+    0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,\r
+    0x2020, 0x00b0, 0x0490, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x0406,\r
+    0x00ae, 0x00a9, 0x2122, 0x0402, 0x0452, 0x2260, 0x0403, 0x0453,\r
+    0x221e, 0x00b1, 0x2264, 0x2265, 0x0456, 0x00b5, 0x0491, 0x0408,\r
+    0x0404, 0x0454, 0x0407, 0x0457, 0x0409, 0x0459, 0x040a, 0x045a,\r
+    0x0458, 0x0405, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,\r
+    0x00bb, 0x2026, 0x00a0, 0x040b, 0x045b, 0x040c, 0x045c, 0x0455,\r
+    0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x201e,\r
+    0x040e, 0x045e, 0x040f, 0x045f, 0x2116, 0x0401, 0x0451, 0x044f,\r
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+    0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,\r
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+    0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x00a4\r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0xca, 0xa3, 0xff, 0xa4, 0xa9, 0xc7, 0xc2, 0xa8,\r
+    0xa1, 0xb1, 0xb5, 0xa6, 0xc8, 0xd6, 0xc4, 0xdd,\r
+    0xab, 0xae, 0xb8, 0xc1, 0xa7, 0xba, 0xb7, 0xbc,\r
+    0xbe, 0xcb, 0xcd, 0xd8, 0xda, 0x80, 0x81, 0x82,\r
+    0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,\r
+    0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92,\r
+    0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,\r
+    0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xe0, 0xe1, 0xe2,\r
+    0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,\r
+    0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2,\r
+    0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,\r
+    0xfb, 0xfc, 0xfd, 0xfe, 0xdf, 0xde, 0xac, 0xaf,\r
+    0xb9, 0xcf, 0xb4, 0xbb, 0xc0, 0xbd, 0xbf, 0xcc,\r
+    0xce, 0xd9, 0xdb, 0xa2, 0xb6, 0xd0, 0xd1, 0xd4,\r
+    0xd5, 0xd2, 0xd3, 0xd7, 0xa0, 0xa5, 0xc9, 0xdc,\r
+    0xaa, 0xc6, 0xc3, 0xb0, 0xc5, 0xad, 0xb2, 0xb3\r
+    },\r
+    256\r
+};\r
+const charset_spec charset_CS_MAC_UKRAINE = {\r
+    CS_MAC_UKRAINE, read_sbcs, write_sbcs, &data_CS_MAC_UKRAINE\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_VT100 = {\r
+    {\r
+    0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407,\r
+    0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f,\r
+    0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417,\r
+    0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2421,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x00b8, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,\r
+    0x00d7, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x00b9, 0x00b2,\r
+    0x00b3, 0x03c0, 0x00a6, 0x00aa, 0x00ba, 0x2592, 0x00e6, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x00bd, 0x0192, 0x00bc, 0x00be, 0x00ab,\r
+    0x00bb, 0x2026, ERROR , 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x2518, 0x2510, 0x250c, 0x2514, 0x00f7, 0x2022,\r
+    0x00ff, 0x0178, 0x253c, 0x20ac, 0x00d0, 0x00f0, 0x00fe, 0x00de,\r
+    0x00fd, 0x00b7, 0x23ba, 0x23bb, 0x2500, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    ERROR , 0x00d2, 0x00da, 0x00db, 0x00d9, 0x23bc, 0x23bd, 0x251c,\r
+    0x2524, 0x2534, 0x252c, 0x2502, ERROR , ERROR , ERROR , ERROR \r
+    },\r
+    {\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0xc1,\r
+    0xa2, 0xa3, 0xb4, 0xba, 0xa4, 0xac, 0xa9, 0xbb,\r
+    0xc7, 0xc2, 0xa8, 0xa1, 0xb1, 0xb7, 0xb8, 0xab,\r
+    0xb5, 0xa6, 0xe1, 0xa5, 0xb6, 0xbc, 0xc8, 0xc5,\r
+    0xc3, 0xc6, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80,\r
+    0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed,\r
+    0xea, 0xeb, 0xec, 0xdc, 0x84, 0xf1, 0xee, 0xef,\r
+    0xcd, 0x85, 0xb0, 0xaf, 0xf4, 0xf2, 0xf3, 0x86,\r
+    0xa0, 0xdf, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a,\r
+    0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93,\r
+    0x92, 0x94, 0x95, 0xdd, 0x96, 0x98, 0x97, 0x99,\r
+    0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f,\r
+    0xe0, 0xde, 0xd8, 0xce, 0xcf, 0xd9, 0xc4, 0xb9,\r
+    0xd0, 0xd1, 0xd7, 0xc9, 0xdb, 0xaa, 0xad, 0xb2,\r
+    0xb3, 0xe2, 0xe3, 0xf5, 0xf6, 0x00, 0x01, 0x02,\r
+    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,\r
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\r
+    0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,\r
+    0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x7f, 0xe4, 0xfb,\r
+    0xd4, 0xd3, 0xd5, 0xd2, 0xf7, 0xf8, 0xfa, 0xf9,\r
+    0xda, 0xbd\r
+    },\r
+    250\r
+};\r
+const charset_spec charset_CS_MAC_VT100 = {\r
+    CS_MAC_VT100, read_sbcs, write_sbcs, &data_CS_MAC_VT100\r
+};\r
+\r
+static const sbcs_data data_CS_MAC_VT100_OLD = {\r
+    {\r
+    0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407,\r
+    0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f,\r
+    0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417,\r
+    0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2421,\r
+    0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,\r
+    0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,\r
+    0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,\r
+    0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,\r
+    0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x00b8, 0x00b6, 0x00df,\r
+    0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,\r
+    0x00d7, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x00b9, 0x00b2,\r
+    0x00b3, 0x03c0, 0x00a6, 0x00aa, 0x00ba, 0x2592, 0x00e6, 0x00f8,\r
+    0x00bf, 0x00a1, 0x00ac, 0x00bd, 0x0192, 0x00bc, 0x00be, 0x00ab,\r
+    0x00bb, 0x2026, ERROR , 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,\r
+    0x2013, 0x2014, 0x2518, 0x2510, 0x250c, 0x2514, 0x00f7, 0x2022,\r
+    0x00ff, 0x0178, 0x253c, 0x00a4, 0x00d0, 0x00f0, 0x00fe, 0x00de,\r
+    0x00fd, 0x00b7, 0x23ba, 0x23bb, 0x2500, 0x00c2, 0x00ca, 0x00c1,\r
+    0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,\r
+    ERROR , 0x00d2, 0x00da, 0x00db, 0x00d9, 0x23bc, 0x23bd, 0x251c,\r
+    0x2524, 0x2534, 0x252c, 0x2502, ERROR , ERROR , ERROR , ERROR \r
+    },\r
+    {\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0xc1,\r
+    0xa2, 0xa3, 0xdb, 0xb4, 0xba, 0xa4, 0xac, 0xa9,\r
+    0xbb, 0xc7, 0xc2, 0xa8, 0xa1, 0xb1, 0xb7, 0xb8,\r
+    0xab, 0xb5, 0xa6, 0xe1, 0xa5, 0xb6, 0xbc, 0xc8,\r
+    0xc5, 0xc3, 0xc6, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc,\r
+    0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8,\r
+    0xed, 0xea, 0xeb, 0xec, 0xdc, 0x84, 0xf1, 0xee,\r
+    0xef, 0xcd, 0x85, 0xb0, 0xaf, 0xf4, 0xf2, 0xf3,\r
+    0x86, 0xa0, 0xdf, 0xa7, 0x88, 0x87, 0x89, 0x8b,\r
+    0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91,\r
+    0x93, 0x92, 0x94, 0x95, 0xdd, 0x96, 0x98, 0x97,\r
+    0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e,\r
+    0x9f, 0xe0, 0xde, 0xd8, 0xce, 0xcf, 0xd9, 0xc4,\r
+    0xb9, 0xd0, 0xd1, 0xd7, 0xc9, 0xaa, 0xad, 0xb2,\r
+    0xb3, 0xe2, 0xe3, 0xf5, 0xf6, 0x00, 0x01, 0x02,\r
+    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,\r
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\r
+    0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,\r
+    0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x7f, 0xe4, 0xfb,\r
+    0xd4, 0xd3, 0xd5, 0xd2, 0xf7, 0xf8, 0xfa, 0xf9,\r
+    0xda, 0xbd\r
+    },\r
+    250\r
+};\r
+const charset_spec charset_CS_MAC_VT100_OLD = {\r
+    CS_MAC_VT100_OLD, read_sbcs, write_sbcs, &data_CS_MAC_VT100_OLD\r
+};\r
+\r
+static const sbcs_data data_CS_VISCII = {\r
+    {\r
+    0x0000, 0x0001, 0x1eb2, 0x0003, 0x0004, 0x1eb4, 0x1eaa, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x1ef6, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x1ef8, 0x001a, 0x001b, 0x001c, 0x001d, 0x1ef4, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x1ea0, 0x1eae, 0x1eb0, 0x1eb6, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eac,\r
+    0x1ebc, 0x1eb8, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ed0,\r
+    0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1ee2, 0x1eda, 0x1edc, 0x1ede,\r
+    0x1eca, 0x1ece, 0x1ecc, 0x1ec8, 0x1ee6, 0x0168, 0x1ee4, 0x1ef2,\r
+    0x00d5, 0x1eaf, 0x1eb1, 0x1eb7, 0x1ea5, 0x1ea7, 0x1ea8, 0x1ead,\r
+    0x1ebd, 0x1eb9, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ed1,\r
+    0x1ed3, 0x1ed5, 0x1ed7, 0x1ee0, 0x01a0, 0x1ed9, 0x1edd, 0x1edf,\r
+    0x1ecb, 0x1ef0, 0x1ee8, 0x1eea, 0x1eec, 0x01a1, 0x1edb, 0x01af,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x1ea2, 0x0102, 0x1eb3, 0x1eb5,\r
+    0x00c8, 0x00c9, 0x00ca, 0x1eba, 0x00cc, 0x00cd, 0x0128, 0x1ef3,\r
+    0x0110, 0x1ee9, 0x00d2, 0x00d3, 0x00d4, 0x1ea1, 0x1ef7, 0x1eeb,\r
+    0x1eed, 0x00d9, 0x00da, 0x1ef9, 0x1ef5, 0x00dd, 0x1ee1, 0x01b0,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x1ea3, 0x0103, 0x1eef, 0x1eab,\r
+    0x00e8, 0x00e9, 0x00ea, 0x1ebb, 0x00ec, 0x00ed, 0x0129, 0x1ec9,\r
+    0x0111, 0x1ef1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x1ecf, 0x1ecd,\r
+    0x1ee5, 0x00f9, 0x00fa, 0x0169, 0x1ee7, 0x00fd, 0x1ee3, 0x1eee\r
+    },\r
+    {\r
+    0x00, 0x01, 0x03, 0x04, 0x07, 0x08, 0x09, 0x0a,\r
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\r
+    0x13, 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c,\r
+    0x1d, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,\r
+    0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,\r
+    0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,\r
+    0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,\r
+    0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,\r
+    0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,\r
+    0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,\r
+    0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d,\r
+    0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,\r
+    0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,\r
+    0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,\r
+    0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d,\r
+    0x7e, 0x7f, 0xc0, 0xc1, 0xc2, 0xc3, 0xc8, 0xc9,\r
+    0xca, 0xcc, 0xcd, 0xd2, 0xd3, 0xd4, 0xa0, 0xd9,\r
+    0xda, 0xdd, 0xe0, 0xe1, 0xe2, 0xe3, 0xe8, 0xe9,\r
+    0xea, 0xec, 0xed, 0xf2, 0xf3, 0xf4, 0xf5, 0xf9,\r
+    0xfa, 0xfd, 0xc5, 0xe5, 0xd0, 0xf0, 0xce, 0xee,\r
+    0x9d, 0xfb, 0xb4, 0xbd, 0xbf, 0xdf, 0x80, 0xd5,\r
+    0xc4, 0xe4, 0x84, 0xa4, 0x85, 0xa5, 0x86, 0x06,\r
+    0xe7, 0x87, 0xa7, 0x81, 0xa1, 0x82, 0xa2, 0x02,\r
+    0xc6, 0x05, 0xc7, 0x83, 0xa3, 0x89, 0xa9, 0xcb,\r
+    0xeb, 0x88, 0xa8, 0x8a, 0xaa, 0x8b, 0xab, 0x8c,\r
+    0xac, 0x8d, 0xad, 0x8e, 0xae, 0x9b, 0xef, 0x98,\r
+    0xb8, 0x9a, 0xf7, 0x99, 0xf6, 0x8f, 0xaf, 0x90,\r
+    0xb0, 0x91, 0xb1, 0x92, 0xb2, 0x93, 0xb5, 0x95,\r
+    0xbe, 0x96, 0xb6, 0x97, 0xb7, 0xb3, 0xde, 0x94,\r
+    0xfe, 0x9e, 0xf8, 0x9c, 0xfc, 0xba, 0xd1, 0xbb,\r
+    0xd7, 0xbc, 0xd8, 0xff, 0xe6, 0xb9, 0xf1, 0x9f,\r
+    0xcf, 0x1e, 0xdc, 0x14, 0xd6, 0x19, 0xdb\r
+    },\r
+    255\r
+};\r
+const charset_spec charset_CS_VISCII = {\r
+    CS_VISCII, read_sbcs, write_sbcs, &data_CS_VISCII\r
+};\r
+\r
+static const sbcs_data data_CS_HP_ROMAN8 = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    0x00a0, 0x00c0, 0x00c2, 0x00c8, 0x00ca, 0x00cb, 0x00ce, 0x00cf,\r
+    0x00b4, 0x02cb, 0x02c6, 0x00a8, 0x02dc, 0x00d9, 0x00db, 0x20a4,\r
+    0x00af, 0x00dd, 0x00fd, 0x00b0, 0x00c7, 0x00e7, 0x00d1, 0x00f1,\r
+    0x00a1, 0x00bf, 0x00a4, 0x00a3, 0x00a5, 0x00a7, 0x0192, 0x00a2,\r
+    0x00e2, 0x00ea, 0x00f4, 0x00fb, 0x00e1, 0x00e9, 0x00f3, 0x00fa,\r
+    0x00e0, 0x00e8, 0x00f2, 0x00f9, 0x00e4, 0x00eb, 0x00f6, 0x00fc,\r
+    0x00c5, 0x00ee, 0x00d8, 0x00c6, 0x00e5, 0x00ed, 0x00f8, 0x00e6,\r
+    0x00c4, 0x00ec, 0x00d6, 0x00dc, 0x00c9, 0x00ef, 0x00df, 0x00d4,\r
+    0x00c1, 0x00c3, 0x00e3, 0x00d0, 0x00f0, 0x00cd, 0x00cc, 0x00d3,\r
+    0x00d2, 0x00d5, 0x00f5, 0x0160, 0x0161, 0x00da, 0x0178, 0x00ff,\r
+    0x00de, 0x00fe, 0x00b7, 0x00b5, 0x00b6, 0x00be, 0x2014, 0x00bc,\r
+    0x00bd, 0x00aa, 0x00ba, 0x00ab, 0x25a0, 0x00bb, 0x00b1, ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa0, 0xb8, 0xbf, 0xbb, 0xba, 0xbc, 0xbd, 0xab,\r
+    0xf9, 0xfb, 0xb0, 0xb3, 0xfe, 0xa8, 0xf3, 0xf4,\r
+    0xf2, 0xfa, 0xfd, 0xf7, 0xf8, 0xf5, 0xb9, 0xa1,\r
+    0xe0, 0xa2, 0xe1, 0xd8, 0xd0, 0xd3, 0xb4, 0xa3,\r
+    0xdc, 0xa4, 0xa5, 0xe6, 0xe5, 0xa6, 0xa7, 0xe3,\r
+    0xb6, 0xe8, 0xe7, 0xdf, 0xe9, 0xda, 0xd2, 0xad,\r
+    0xed, 0xae, 0xdb, 0xb1, 0xf0, 0xde, 0xc8, 0xc4,\r
+    0xc0, 0xe2, 0xcc, 0xd4, 0xd7, 0xb5, 0xc9, 0xc5,\r
+    0xc1, 0xcd, 0xd9, 0xd5, 0xd1, 0xdd, 0xe4, 0xb7,\r
+    0xca, 0xc6, 0xc2, 0xea, 0xce, 0xd6, 0xcb, 0xc7,\r
+    0xc3, 0xcf, 0xb2, 0xf1, 0xef, 0xeb, 0xec, 0xee,\r
+    0xbe, 0xaa, 0xa9, 0xac, 0xf6, 0xaf, 0xfc\r
+    },\r
+    255\r
+};\r
+const charset_spec charset_CS_HP_ROMAN8 = {\r
+    CS_HP_ROMAN8, read_sbcs, write_sbcs, &data_CS_HP_ROMAN8\r
+};\r
+\r
+static const sbcs_data data_CS_DEC_MCS = {\r
+    {\r
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,\r
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,\r
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,\r
+    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,\r
+    0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,\r
+    ERROR , 0x00a1, 0x00a2, 0x00a3, ERROR , 0x00a5, ERROR , 0x00a7,\r
+    0x00a4, 0x00a9, 0x00aa, 0x00ab, ERROR , ERROR , ERROR , ERROR ,\r
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, ERROR , 0x00b5, 0x00b6, 0x00b7,\r
+    ERROR , 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, ERROR , 0x00bf,\r
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,\r
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,\r
+    ERROR , 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0152,\r
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0178, ERROR , 0x00df,\r
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,\r
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,\r
+    ERROR , 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x0153,\r
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00ff, ERROR , ERROR \r
+    },\r
+    {\r
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\r
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\r
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\r
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\r
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\r
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\r
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\r
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\r
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\r
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\r
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\r
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\r
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\r
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\r
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\r
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\r
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\r
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\r
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\r
+    0xa1, 0xa2, 0xa3, 0xa8, 0xa5, 0xa7, 0xa9, 0xaa,\r
+    0xab, 0xb0, 0xb1, 0xb2, 0xb3, 0xb5, 0xb6, 0xb7,\r
+    0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbf, 0xc0, 0xc1,\r
+    0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,\r
+    0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd2,\r
+    0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb,\r
+    0xdc, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5,\r
+    0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed,\r
+    0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,\r
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xd7, 0xf7,\r
+    0xdd\r
+    },\r
+    241\r
+};\r
+const charset_spec charset_CS_DEC_MCS = {\r
+    CS_DEC_MCS, read_sbcs, write_sbcs, &data_CS_DEC_MCS\r
+};\r
+\r
+#else /* ENUM_CHARSETS */\r
+\r
+ENUM_CHARSET(CS_ISO8859_1)\r
+ENUM_CHARSET(CS_ISO8859_2)\r
+ENUM_CHARSET(CS_ISO8859_3)\r
+ENUM_CHARSET(CS_ISO8859_4)\r
+ENUM_CHARSET(CS_ISO8859_5)\r
+ENUM_CHARSET(CS_ISO8859_6)\r
+ENUM_CHARSET(CS_ISO8859_7)\r
+ENUM_CHARSET(CS_ISO8859_8)\r
+ENUM_CHARSET(CS_ISO8859_9)\r
+ENUM_CHARSET(CS_ISO8859_10)\r
+ENUM_CHARSET(CS_ISO8859_11)\r
+ENUM_CHARSET(CS_ISO8859_13)\r
+ENUM_CHARSET(CS_ISO8859_14)\r
+ENUM_CHARSET(CS_ISO8859_15)\r
+ENUM_CHARSET(CS_ISO8859_16)\r
+ENUM_CHARSET(CS_ISO8859_1_X11)\r
+ENUM_CHARSET(CS_CP437)\r
+ENUM_CHARSET(CS_CP850)\r
+ENUM_CHARSET(CS_CP866)\r
+ENUM_CHARSET(CS_CP1250)\r
+ENUM_CHARSET(CS_CP1251)\r
+ENUM_CHARSET(CS_CP1252)\r
+ENUM_CHARSET(CS_CP1253)\r
+ENUM_CHARSET(CS_CP1254)\r
+ENUM_CHARSET(CS_CP1255)\r
+ENUM_CHARSET(CS_CP1256)\r
+ENUM_CHARSET(CS_CP1257)\r
+ENUM_CHARSET(CS_CP1258)\r
+ENUM_CHARSET(CS_KOI8_R)\r
+ENUM_CHARSET(CS_KOI8_U)\r
+ENUM_CHARSET(CS_MAC_ROMAN)\r
+ENUM_CHARSET(CS_MAC_TURKISH)\r
+ENUM_CHARSET(CS_MAC_CROATIAN)\r
+ENUM_CHARSET(CS_MAC_ICELAND)\r
+ENUM_CHARSET(CS_MAC_ROMANIAN)\r
+ENUM_CHARSET(CS_MAC_GREEK)\r
+ENUM_CHARSET(CS_MAC_CYRILLIC)\r
+ENUM_CHARSET(CS_MAC_THAI)\r
+ENUM_CHARSET(CS_MAC_CENTEURO)\r
+ENUM_CHARSET(CS_MAC_SYMBOL)\r
+ENUM_CHARSET(CS_MAC_DINGBATS)\r
+ENUM_CHARSET(CS_MAC_ROMAN_OLD)\r
+ENUM_CHARSET(CS_MAC_CROATIAN_OLD)\r
+ENUM_CHARSET(CS_MAC_ICELAND_OLD)\r
+ENUM_CHARSET(CS_MAC_ROMANIAN_OLD)\r
+ENUM_CHARSET(CS_MAC_GREEK_OLD)\r
+ENUM_CHARSET(CS_MAC_CYRILLIC_OLD)\r
+ENUM_CHARSET(CS_MAC_UKRAINE)\r
+ENUM_CHARSET(CS_MAC_VT100)\r
+ENUM_CHARSET(CS_MAC_VT100_OLD)\r
+ENUM_CHARSET(CS_VISCII)\r
+ENUM_CHARSET(CS_HP_ROMAN8)\r
+ENUM_CHARSET(CS_DEC_MCS)\r
+\r
+#endif /* ENUM_CHARSETS */\r
diff --git a/putty/CHARSET/SBCSGEN.PL b/putty/CHARSET/SBCSGEN.PL
new file mode 100644 (file)
index 0000000..335c8e0
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env perl -w\r
+\r
+# This script generates sbcsdat.c (the data for all the SBCSes) from its\r
+# source form sbcs.dat.\r
+\r
+$infile = "sbcs.dat";\r
+$outfile = "sbcsdat.c";\r
+\r
+open FOO, $infile;\r
+open BAR, ">$outfile";\r
+select BAR;\r
+\r
+print "/*\n";\r
+print " * sbcsdat.c - data definitions for single-byte character sets.\n";\r
+print " *\n";\r
+print " * Generated by sbcsgen.pl from sbcs.dat.\n";\r
+print " * You should edit those files rather than editing this one.\n";\r
+print " */\n";\r
+print "\n";\r
+print "#ifndef ENUM_CHARSETS\n";\r
+print "\n";\r
+print "#include \"charset.h\"\n";\r
+print "#include \"internal.h\"\n";\r
+print "\n";\r
+\r
+my $charsetname = undef;\r
+my @vals = ();\r
+\r
+my @charsetnames = ();\r
+my @sortpriority = ();\r
+\r
+while (<FOO>) {\r
+    chomp;\r
+    if (/^charset (.*)$/) {\r
+       $charsetname = $1;\r
+       @vals = ();\r
+       @sortpriority = map { 0 } 0..255;\r
+    } elsif (/^sortpriority ([^-]*)-([^-]*) (.*)$/) {\r
+       for ($i = hex $1; $i <= hex $2; $i++) {\r
+           $sortpriority[$i] += $3;\r
+       }\r
+    } elsif (/^[0-9a-fA-FX]/) {\r
+       push @vals, map { $_ eq "XXXX" ? -1 : hex $_ } split / +/, $_;\r
+       if (scalar @vals > 256) {\r
+           die "$infile:$.: charset $charsetname has more than 256 values\n";\r
+       } elsif (scalar @vals == 256) {\r
+           &outcharset($charsetname, \@vals, \@sortpriority);\r
+           push @charsetnames, $charsetname;\r
+           $charsetname = undef;\r
+           @vals = ();\r
+           @sortpriority = map { 0 } 0..255;\r
+       }\r
+    }\r
+}\r
+\r
+print "#else /* ENUM_CHARSETS */\n";\r
+print "\n";\r
+\r
+foreach $i (@charsetnames) {\r
+    print "ENUM_CHARSET($i)\n";\r
+}\r
+\r
+print "\n";\r
+print "#endif /* ENUM_CHARSETS */\n";\r
+\r
+sub outcharset($$$) {\r
+    my ($name, $vals, $sortpriority) = @_;\r
+    my ($prefix, $i, @sorted);\r
+\r
+    print "static const sbcs_data data_$name = {\n";\r
+    print "    {\n";\r
+    $prefix = "    ";\r
+    @sorted = ();\r
+    for ($i = 0; $i < 256; $i++) {\r
+       if ($vals->[$i] < 0) {\r
+           printf "%sERROR ", $prefix;\r
+       } else {\r
+           printf "%s0x%04x", $prefix, $vals->[$i];\r
+           die "ooh? $i\n" unless defined $sortpriority->[$i];\r
+           push @sorted, [$i, $vals->[$i], 0+$sortpriority->[$i]];\r
+       }\r
+       if ($i % 8 == 7) {\r
+           $prefix = ",\n    ";\r
+       } else {\r
+           $prefix = ", ";\r
+       }\r
+    }\r
+    print "\n    },\n    {\n";\r
+    @sorted = sort { ($a->[1] == $b->[1] ?\r
+                     $b->[2] <=> $a->[2] :\r
+                     $a->[1] <=> $b->[1]) ||\r
+                     $a->[0] <=> $b->[0] } @sorted;\r
+    $prefix = "    ";\r
+    $uval = -1;\r
+    for ($i = $j = 0; $i < scalar @sorted; $i++) {\r
+       next if ($uval == $sorted[$i]->[1]); # low-priority alternative\r
+       $uval = $sorted[$i]->[1];\r
+       printf "%s0x%02x", $prefix, $sorted[$i]->[0];\r
+       if ($j % 8 == 7) {\r
+           $prefix = ",\n    ";\r
+       } else {\r
+           $prefix = ", ";\r
+       }\r
+       $j++;\r
+    }\r
+    printf "\n    },\n    %d\n", $j;\r
+    print "};\n";\r
+    print "const charset_spec charset_$name = {\n" .\r
+          "    $name, read_sbcs, write_sbcs, &data_$name\n};\n\n";\r
+}\r
diff --git a/putty/CHARSET/SLOOKUP.C b/putty/CHARSET/SLOOKUP.C
new file mode 100644 (file)
index 0000000..921a174
--- /dev/null
@@ -0,0 +1,29 @@
+/*\r
+ * slookup.c - static lookup of character sets.\r
+ */\r
+\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+#define ENUM_CHARSET(x) extern charset_spec const charset_##x;\r
+#include "enum.c"\r
+#undef ENUM_CHARSET\r
+\r
+static charset_spec const *const cs_table[] = {\r
+\r
+#define ENUM_CHARSET(x) &charset_##x,\r
+#include "enum.c"\r
+#undef ENUM_CHARSET\r
+\r
+};\r
+\r
+charset_spec const *charset_find_spec(int charset)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(cs_table); i++)\r
+       if (cs_table[i]->charset == charset)\r
+           return cs_table[i];\r
+\r
+    return NULL;\r
+}\r
diff --git a/putty/CHARSET/TOUCS.C b/putty/CHARSET/TOUCS.C
new file mode 100644 (file)
index 0000000..658eb73
--- /dev/null
@@ -0,0 +1,89 @@
+/*\r
+ * toucs.c - convert charsets to Unicode.\r
+ */\r
+\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+struct unicode_emit_param {\r
+    wchar_t *output;\r
+    int outlen;\r
+    const wchar_t *errstr;\r
+    int errlen;\r
+    int stopped;\r
+};\r
+\r
+static void unicode_emit(void *ctx, long int output)\r
+{\r
+    struct unicode_emit_param *param = (struct unicode_emit_param *)ctx;\r
+    wchar_t outval;\r
+    wchar_t const *p;\r
+    int outlen;\r
+\r
+    if (output == ERROR) {\r
+       if (param->errstr) {\r
+           p = param->errstr;\r
+           outlen = param->errlen;\r
+       } else {\r
+           outval = 0xFFFD;           /* U+FFFD REPLACEMENT CHARACTER */\r
+           p = &outval;\r
+           outlen = 1;\r
+       }\r
+    } else {\r
+       outval = output;\r
+       p = &outval;\r
+       outlen = 1;\r
+    }\r
+\r
+    if (param->outlen >= outlen) {\r
+       while (outlen > 0) {\r
+           *param->output++ = *p++;\r
+           param->outlen--;\r
+           outlen--;\r
+       }\r
+    } else {\r
+       param->stopped = 1;\r
+    }\r
+}\r
+\r
+int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen,\r
+                      int charset, charset_state *state,\r
+                      const wchar_t *errstr, int errlen)\r
+{\r
+    charset_spec const *spec = charset_find_spec(charset);\r
+    charset_state localstate;\r
+    struct unicode_emit_param param;\r
+\r
+    param.output = output;\r
+    param.outlen = outlen;\r
+    param.errstr = errstr;\r
+    param.errlen = errlen;\r
+    param.stopped = 0;\r
+\r
+    if (!state) {\r
+       localstate.s0 = 0;\r
+    } else {\r
+       localstate = *state;           /* structure copy */\r
+    }\r
+\r
+    while (*inlen > 0) {\r
+       int lenbefore = param.output - output;\r
+       spec->read(spec, (unsigned char)**input, &localstate,\r
+                  unicode_emit, &param);\r
+       if (param.stopped) {\r
+           /*\r
+            * The emit function has _tried_ to output some\r
+            * characters, but ran up against the end of the\r
+            * buffer. Leave immediately, and return what happened\r
+            * _before_ attempting to process this character.\r
+            */\r
+           return lenbefore;\r
+       }\r
+       if (state)\r
+           *state = localstate;   /* structure copy */\r
+       (*input)++;\r
+       (*inlen)--;\r
+    }\r
+\r
+    return param.output - output;\r
+}\r
diff --git a/putty/CHARSET/UTF8.C b/putty/CHARSET/UTF8.C
new file mode 100644 (file)
index 0000000..489ffa2
--- /dev/null
@@ -0,0 +1,882 @@
+/*\r
+ * utf8.c - routines to handle UTF-8.\r
+ */\r
+\r
+#ifndef ENUM_CHARSETS\r
+\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+void read_utf8(charset_spec const *, long int, charset_state *,\r
+              void (*)(void *, long int), void *);\r
+void write_utf8(charset_spec const *, long int,\r
+               charset_state *, void (*)(void *, long int), void *);\r
+\r
+/*\r
+ * UTF-8 has no associated data, so `charset' may be ignored.\r
+ */\r
+\r
+void read_utf8(charset_spec const *charset, long int input_chr,\r
+              charset_state *state,\r
+              void (*emit)(void *ctx, long int output), void *emitctx)\r
+{\r
+    UNUSEDARG(charset);\r
+\r
+    /*\r
+     * For reading UTF-8, the `state' word contains:\r
+     * \r
+     *  - in bits 29-31, the number of bytes expected to be in the\r
+     *    current multibyte character (which we can tell instantly\r
+     *    from the first byte, of course).\r
+     * \r
+     *  - in bits 26-28, the number of bytes _seen so far_ in the\r
+     *    current multibyte character.\r
+     * \r
+     *  - in the remainder of the word, the current value of the\r
+     *    character, which is shifted upwards by 6 bits to\r
+     *    accommodate each new byte.\r
+     * \r
+     * As required, the state is zero when we are not in the middle\r
+     * of a multibyte character at all.\r
+     * \r
+     * For example, when reading E9 8D 8B, starting at state=0:\r
+     * \r
+     *  - after E9, the state is 0x64000009\r
+     *  - after 8D, the state is 0x6800024d\r
+     *  - after 8B, the state conceptually becomes 0x6c00934b, at\r
+     *    which point we notice we've got as many characters as we\r
+     *    were expecting, output U+934B, and reset the state to\r
+     *    zero.\r
+     *\r
+     * Note that the maximum number of bits we might need to store\r
+     * in the character value field is 25 (U+7FFFFFFF contains 31\r
+     * bits, but we will never actually store its full value\r
+     * because when we receive the last 6 bits in the final\r
+     * continuation byte we will output it and revert the state to\r
+     * zero). Hence the character value field never collides with\r
+     * the byte counts.\r
+     */\r
+\r
+    if (input_chr < 0x80) {\r
+       /*\r
+        * Single-byte character. If the state is nonzero before\r
+        * coming here, output an error for an incomplete sequence.\r
+        * Then output the character.\r
+        */\r
+       if (state->s0 != 0) {\r
+           emit(emitctx, ERROR);\r
+           state->s0 = 0;\r
+       }\r
+       emit(emitctx, input_chr);\r
+    } else if (input_chr == 0xFE || input_chr == 0xFF) {\r
+       /*\r
+        * FE and FF bytes should _never_ occur in UTF-8. They are\r
+        * automatic errors; if the state was nonzero to start\r
+        * with, output a further error for an incomplete sequence.\r
+        */\r
+       if (state->s0 != 0) {\r
+           emit(emitctx, ERROR);\r
+           state->s0 = 0;\r
+       }\r
+       emit(emitctx, ERROR);\r
+    } else if (input_chr >= 0x80 && input_chr < 0xC0) {\r
+       /*\r
+        * Continuation byte. Output an error for an unexpected\r
+        * continuation byte, if the state is zero.\r
+        */\r
+       if (state->s0 == 0) {\r
+           emit(emitctx, ERROR);\r
+       } else {\r
+           unsigned long charval;\r
+           unsigned long topstuff;\r
+           int bytes;\r
+\r
+           /*\r
+            * Otherwise, accumulate more of the character value.\r
+            */\r
+           charval = state->s0 & 0x03ffffffL;\r
+           charval = (charval << 6) | (input_chr & 0x3F);\r
+\r
+           /*\r
+            * Check the byte counts; if we have not reached the\r
+            * end of the character, update the state and return.\r
+            */\r
+           topstuff = state->s0 & 0xfc000000L;\r
+           topstuff += 0x04000000L;   /* add one to the byte count */\r
+           if (((topstuff << 3) ^ topstuff) & 0xe0000000L) {\r
+               state->s0 = topstuff | charval;\r
+               return;\r
+           }\r
+\r
+           /*\r
+            * Now we know we've reached the end of the character.\r
+            * `charval' is the Unicode value. We should check for\r
+            * various invalid things, and then either output\r
+            * charval or an error. In all cases we reset the state\r
+            * to zero.\r
+            */\r
+           bytes = topstuff >> 29;\r
+           state->s0 = 0;\r
+\r
+           if (charval >= 0xD800 && charval < 0xE000) {\r
+               /*\r
+                * Surrogates (0xD800-0xDFFF) may never be encoded\r
+                * in UTF-8. A surrogate pair in Unicode should\r
+                * have been encoded as a single UTF-8 character\r
+                * occupying more than three bytes.\r
+                */\r
+               emit(emitctx, ERROR);\r
+           } else if (charval == 0xFFFE || charval == 0xFFFF) {\r
+               /*\r
+                * U+FFFE and U+FFFF are invalid Unicode characters\r
+                * and may never be encoded in UTF-8. (This is one\r
+                * reason why U+FFFF is our way of signalling an\r
+                * error to our `emit' function :-)\r
+                */\r
+               emit(emitctx, ERROR);\r
+           } else if ((charval <= 0x7FL /* && bytes > 1 */) ||\r
+                      (charval <= 0x7FFL && bytes > 2) ||\r
+                      (charval <= 0xFFFFL && bytes > 3) ||\r
+                      (charval <= 0x1FFFFFL && bytes > 4) ||\r
+                      (charval <= 0x3FFFFFFL && bytes > 5)) {\r
+               /*\r
+                * Overlong sequences are not to be tolerated,\r
+                * under any circumstances.\r
+                */\r
+               emit(emitctx, ERROR);\r
+           } else {\r
+               /*\r
+                * Oh, all right. We'll let this one off.\r
+                */\r
+               emit(emitctx, charval);\r
+           }\r
+       }\r
+\r
+    } else {\r
+       /*\r
+        * Lead byte. First output an error for an incomplete\r
+        * sequence, if the state is nonzero.\r
+        */\r
+       if (state->s0 != 0)\r
+           emit(emitctx, ERROR);\r
+\r
+       /*\r
+        * Now deal with the lead byte: work out the number of\r
+        * bytes we expect to see in this character, and extract\r
+        * the initial bits of it too.\r
+        */\r
+       if (input_chr >= 0xC0 && input_chr < 0xE0) {\r
+           state->s0 = 0x44000000L | (input_chr & 0x1F);\r
+       } else if (input_chr >= 0xE0 && input_chr < 0xF0) {\r
+           state->s0 = 0x64000000L | (input_chr & 0x0F);\r
+       } else if (input_chr >= 0xF0 && input_chr < 0xF8) {\r
+           state->s0 = 0x84000000L | (input_chr & 0x07);\r
+       } else if (input_chr >= 0xF8 && input_chr < 0xFC) {\r
+           state->s0 = 0xa4000000L | (input_chr & 0x03);\r
+       } else if (input_chr >= 0xFC && input_chr < 0xFE) {\r
+           state->s0 = 0xc4000000L | (input_chr & 0x01);\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * UTF-8 is a stateless multi-byte encoding (in the sense that just\r
+ * after any character has been completed, the state is always the\r
+ * same); hence when writing it, there is no need to use the\r
+ * charset_state.\r
+ */\r
+\r
+void write_utf8(charset_spec const *charset, long int input_chr,\r
+               charset_state *state,\r
+               void (*emit)(void *ctx, long int output), void *emitctx)\r
+{\r
+    UNUSEDARG(charset);\r
+    UNUSEDARG(state);\r
+\r
+    /*\r
+     * Refuse to output any illegal code points.\r
+     */\r
+    if (input_chr == 0xFFFE || input_chr == 0xFFFF ||\r
+       (input_chr >= 0xD800 && input_chr < 0xE000)) {\r
+       emit(emitctx, ERROR);\r
+    } else if (input_chr < 0x80) {     /* one-byte character */\r
+       emit(emitctx, input_chr);\r
+    } else if (input_chr < 0x800) {    /* two-byte character */\r
+       emit(emitctx, 0xC0 | (0x1F & (input_chr >>  6)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr      )));\r
+    } else if (input_chr < 0x10000) {  /* three-byte character */\r
+       emit(emitctx, 0xE0 | (0x0F & (input_chr >> 12)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >>  6)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr      )));\r
+    } else if (input_chr < 0x200000) { /* four-byte character */\r
+       emit(emitctx, 0xF0 | (0x07 & (input_chr >> 18)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >> 12)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >>  6)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr      )));\r
+    } else if (input_chr < 0x4000000) {/* five-byte character */\r
+       emit(emitctx, 0xF8 | (0x03 & (input_chr >> 24)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >> 18)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >> 12)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >>  6)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr      )));\r
+    } else {                          /* six-byte character */\r
+       emit(emitctx, 0xFC | (0x01 & (input_chr >> 30)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >> 24)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >> 18)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >> 12)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr >>  6)));\r
+       emit(emitctx, 0x80 | (0x3F & (input_chr      )));\r
+    }\r
+}\r
+\r
+#ifdef TESTMODE\r
+\r
+#include <stdio.h>\r
+#include <stdarg.h>\r
+\r
+int total_errs = 0;\r
+\r
+void utf8_emit(void *ctx, long output)\r
+{\r
+    wchar_t **p = (wchar_t **)ctx;\r
+    *(*p)++ = output;\r
+}\r
+\r
+void utf8_read_test(int line, char *input, int inlen, ...)\r
+{\r
+    va_list ap;\r
+    wchar_t *p, str[512];\r
+    int i;\r
+    charset_state state;\r
+    unsigned long l;\r
+\r
+    state.s0 = 0;\r
+    p = str;\r
+\r
+    for (i = 0; i < inlen; i++)\r
+       read_utf8(NULL, input[i] & 0xFF, &state, utf8_emit, &p);\r
+\r
+    va_start(ap, inlen);\r
+    l = 0;\r
+    for (i = 0; i < p - str; i++) {\r
+       l = va_arg(ap, long int);\r
+       if (l == -1) {\r
+           printf("%d: correct string shorter than output\n", line);\r
+           total_errs++;\r
+           break;\r
+       }\r
+       if (l != str[i]) {\r
+           printf("%d: char %d came out as %08x, should be %08x\n",\r
+                   line, i, str[i], l);\r
+           total_errs++;\r
+       }\r
+    }\r
+    if (l != -1) {\r
+       l = va_arg(ap, long int);\r
+       if (l != -1) {\r
+           printf("%d: correct string longer than output\n", line);\r
+           total_errs++;\r
+       }\r
+    }\r
+    va_end(ap);\r
+}\r
+\r
+void utf8_write_test(int line, const long *input, int inlen, ...)\r
+{\r
+    va_list ap;\r
+    wchar_t *p, str[512];\r
+    int i;\r
+    charset_state state;\r
+    unsigned long l;\r
+\r
+    state.s0 = 0;\r
+    p = str;\r
+\r
+    for (i = 0; i < inlen; i++)\r
+       write_utf8(NULL, input[i], &state, utf8_emit, &p);\r
+\r
+    va_start(ap, inlen);\r
+    l = 0;\r
+    for (i = 0; i < p - str; i++) {\r
+       l = va_arg(ap, long int);\r
+       if (l == -1) {\r
+           printf("%d: correct string shorter than output\n", line);\r
+           total_errs++;\r
+           break;\r
+       }\r
+       if (l != str[i]) {\r
+           printf("%d: char %d came out as %08x, should be %08x\n",\r
+                   line, i, str[i], l);\r
+           total_errs++;\r
+       }\r
+    }\r
+    if (l != -1) {\r
+       l = va_arg(ap, long int);\r
+       if (l != -1) {\r
+           printf("%d: correct string longer than output\n", line);\r
+           total_errs++;\r
+       }\r
+    }\r
+    va_end(ap);\r
+}\r
+\r
+/* Macro to concoct the first three parameters of utf8_read_test. */\r
+#define TESTSTR(x) __LINE__, x, lenof(x)\r
+\r
+int main(void)\r
+{\r
+    printf("read tests beginning\n");\r
+    utf8_read_test(TESTSTR("\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5"),\r
+                  0x000003BA, /* GREEK SMALL LETTER KAPPA */\r
+                  0x00001F79, /* GREEK SMALL LETTER OMICRON WITH OXIA */\r
+                  0x000003C3, /* GREEK SMALL LETTER SIGMA */\r
+                  0x000003BC, /* GREEK SMALL LETTER MU */\r
+                  0x000003B5, /* GREEK SMALL LETTER EPSILON */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x00"),\r
+                  0x00000000, /* <control> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xC2\x80"),\r
+                  0x00000080, /* <control> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xE0\xA0\x80"),\r
+                  0x00000800, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF0\x90\x80\x80"),\r
+                  0x00010000, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF8\x88\x80\x80\x80"),\r
+                  0x00200000, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFC\x84\x80\x80\x80\x80"),\r
+                  0x04000000, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x7F"),\r
+                  0x0000007F, /* <control> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xDF\xBF"),\r
+                  0x000007FF, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xEF\xBF\xBD"),\r
+                  0x0000FFFD, /* REPLACEMENT CHARACTER */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xEF\xBF\xBF"),\r
+                  ERROR,      /* <no name available> (invalid char) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF7\xBF\xBF\xBF"),\r
+                  0x001FFFFF, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF\xBF"),\r
+                  0x03FFFFFF, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF\xBF"),\r
+                  0x7FFFFFFF, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\x9F\xBF"),\r
+                  0x0000D7FF, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xEE\x80\x80"),\r
+                  0x0000E000, /* <Private Use, First> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xEF\xBF\xBD"),\r
+                  0x0000FFFD, /* REPLACEMENT CHARACTER */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF4\x8F\xBF\xBF"),\r
+                  0x0010FFFF, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF4\x90\x80\x80"),\r
+                  0x00110000, /* <no name available> */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xBF"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80\xBF"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80\xBF\x80"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80\xBF\x80\xBF"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF\x80"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"),\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  ERROR,      /* (unexpected continuation byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xC0\x20\xC1\x20\xC2\x20\xC3\x20\xC4\x20\xC5\x20\xC6\x20\xC7\x20"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xE0\x20\xE1\x20\xE2\x20\xE3\x20\xE4\x20\xE5\x20\xE6\x20\xE7\x20\xE8\x20\xE9\x20\xEA\x20\xEB\x20\xEC\x20\xED\x20\xEE\x20\xEF\x20"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF0\x20\xF1\x20\xF2\x20\xF3\x20\xF4\x20\xF5\x20\xF6\x20\xF7\x20"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF8\x20\xF9\x20\xFA\x20\xFB\x20"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFC\x20\xFD\x20"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0x00000020, /* SPACE */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xC0"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xE0\x80"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF0\x80\x80"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF8\x80\x80\x80"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xDF"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xEF\xBF"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF7\xBF\xBF"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF"),\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  ERROR,      /* (incomplete sequence) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFE"),\r
+                  ERROR,      /* (invalid UTF-8 byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFF"),\r
+                  ERROR,      /* (invalid UTF-8 byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFE\xFE\xFF\xFF"),\r
+                  ERROR,      /* (invalid UTF-8 byte) */\r
+                  ERROR,      /* (invalid UTF-8 byte) */\r
+                  ERROR,      /* (invalid UTF-8 byte) */\r
+                  ERROR,      /* (invalid UTF-8 byte) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xC0\xAF"),\r
+                  ERROR,      /* SOLIDUS (overlong form of 2F) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xE0\x80\xAF"),\r
+                  ERROR,      /* SOLIDUS (overlong form of 2F) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF0\x80\x80\xAF"),\r
+                  ERROR,      /* SOLIDUS (overlong form of 2F) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF8\x80\x80\x80\xAF"),\r
+                  ERROR,      /* SOLIDUS (overlong form of 2F) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\xAF"),\r
+                  ERROR,      /* SOLIDUS (overlong form of 2F) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xC1\xBF"),\r
+                  ERROR,      /* <control> (overlong form of 7F) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xE0\x9F\xBF"),\r
+                  ERROR,      /* <no name available> (overlong form of DF BF) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF0\x8F\xBF\xBF"),\r
+                  ERROR,      /* <no name available> (overlong form of EF BF BF) (invalid char) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF8\x87\xBF\xBF\xBF"),\r
+                  ERROR,      /* <no name available> (overlong form of F7 BF BF BF) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFC\x83\xBF\xBF\xBF\xBF"),\r
+                  ERROR,      /* <no name available> (overlong form of FB BF BF BF BF) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xC0\x80"),\r
+                  ERROR,      /* <control> (overlong form of 00) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xE0\x80\x80"),\r
+                  ERROR,      /* <control> (overlong form of 00) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF0\x80\x80\x80"),\r
+                  ERROR,      /* <control> (overlong form of 00) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xF8\x80\x80\x80\x80"),\r
+                  ERROR,      /* <control> (overlong form of 00) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\x80"),\r
+                  ERROR,      /* <control> (overlong form of 00) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xA0\x80"),\r
+                  ERROR,      /* <Non Private Use High Surrogate, First> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAD\xBF"),\r
+                  ERROR,      /* <Non Private Use High Surrogate, Last> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAE\x80"),\r
+                  ERROR,      /* <Private Use High Surrogate, First> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAF\xBF"),\r
+                  ERROR,      /* <Private Use High Surrogate, Last> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xB0\x80"),\r
+                  ERROR,      /* <Low Surrogate, First> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xBE\x80"),\r
+                  ERROR,      /* <no name available> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xBF\xBF"),\r
+                  ERROR,      /* <Low Surrogate, Last> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xB0\x80"),\r
+                  ERROR,      /* <Non Private Use High Surrogate, First> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, First> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xBF\xBF"),\r
+                  ERROR,      /* <Non Private Use High Surrogate, First> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, Last> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xB0\x80"),\r
+                  ERROR,      /* <Non Private Use High Surrogate, Last> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, First> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xBF\xBF"),\r
+                  ERROR,      /* <Non Private Use High Surrogate, Last> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, Last> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xB0\x80"),\r
+                  ERROR,      /* <Private Use High Surrogate, First> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, First> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xBF\xBF"),\r
+                  ERROR,      /* <Private Use High Surrogate, First> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, Last> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xB0\x80"),\r
+                  ERROR,      /* <Private Use High Surrogate, Last> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, First> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xBF\xBF"),\r
+                  ERROR,      /* <Private Use High Surrogate, Last> (surrogate) */\r
+                  ERROR,      /* <Low Surrogate, Last> (surrogate) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xEF\xBF\xBE"),\r
+                  ERROR,      /* <no name available> (invalid char) */\r
+                  0, -1);\r
+    utf8_read_test(TESTSTR("\xEF\xBF\xBF"),\r
+                  ERROR,      /* <no name available> (invalid char) */\r
+                  0, -1);\r
+    printf("read tests completed\n");\r
+    printf("write tests beginning\n");\r
+    {\r
+       const static long str[] =\r
+       {0x03BAL, 0x1F79L, 0x03C3L, 0x03BCL, 0x03B5L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xCE, 0xBA,\r
+                       0xE1, 0xBD, 0xB9,\r
+                       0xCF, 0x83,\r
+                       0xCE, 0xBC,\r
+                       0xCE, 0xB5,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x0000L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0x00,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x0080L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xC2, 0x80,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x0800L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xE0, 0xA0, 0x80,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x00010000L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xF0, 0x90, 0x80, 0x80,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x00200000L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xF8, 0x88, 0x80, 0x80, 0x80,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x04000000L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xFC, 0x84, 0x80, 0x80, 0x80, 0x80,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x007FL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0x7F,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x07FFL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xDF, 0xBF,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0xFFFDL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xEF, 0xBF, 0xBD,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0xFFFFL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       ERROR,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x001FFFFFL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xF7, 0xBF, 0xBF, 0xBF,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x03FFFFFFL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xFB, 0xBF, 0xBF, 0xBF, 0xBF,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0x7FFFFFFFL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0xD7FFL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xED, 0x9F, 0xBF,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0xD800L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       ERROR,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0xD800L, 0xDC00L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       ERROR,\r
+                       ERROR,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0xDFFFL, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       ERROR,\r
+                       0, -1);\r
+    }\r
+    {\r
+       const static long str[] = {0xE000L, 0};\r
+       utf8_write_test(TESTSTR(str),\r
+                       0xEE, 0x80, 0x80,\r
+                       0, -1);\r
+    }\r
+    printf("write tests completed\n");\r
+\r
+    printf("total: %d errors\n", total_errs);\r
+    return (total_errs != 0);\r
+}\r
+#endif /* TESTMODE */\r
+\r
+const charset_spec charset_CS_UTF8 = {\r
+    CS_UTF8, read_utf8, write_utf8, NULL\r
+};\r
+\r
+#else /* ENUM_CHARSETS */\r
+\r
+ENUM_CHARSET(CS_UTF8)\r
+\r
+#endif /* ENUM_CHARSETS */\r
diff --git a/putty/CHARSET/XENC.C b/putty/CHARSET/XENC.C
new file mode 100644 (file)
index 0000000..2832f31
--- /dev/null
@@ -0,0 +1,93 @@
+/*\r
+ * xenc.c - translate our internal character set codes to and from\r
+ * X11 character encoding names.\r
+ * \r
+ */\r
+\r
+#include <ctype.h>\r
+#include "charset.h"\r
+#include "internal.h"\r
+\r
+static const struct {\r
+    const char *name;\r
+    int charset;\r
+} xencs[] = {\r
+    /*\r
+     * Officially registered encoding names. This list is derived\r
+     * from the font encodings section of\r
+     * \r
+     *   http://ftp.x.org/pub/DOCS/registry\r
+     * \r
+     * Where multiple encoding names map to the same encoding id\r
+     * (such as iso8859-15 and fcd8859-15), the first is considered\r
+     * canonical and will be returned when translating the id to a\r
+     * string.\r
+     */\r
+    { "iso8859-1", CS_ISO8859_1 },\r
+    { "iso8859-2", CS_ISO8859_2 },\r
+    { "iso8859-3", CS_ISO8859_3 },\r
+    { "iso8859-4", CS_ISO8859_4 },\r
+    { "iso8859-5", CS_ISO8859_5 },\r
+    { "iso8859-6", CS_ISO8859_6 },\r
+    { "iso8859-7", CS_ISO8859_7 },\r
+    { "iso8859-8", CS_ISO8859_8 },\r
+    { "iso8859-9", CS_ISO8859_9 },\r
+    { "iso8859-10", CS_ISO8859_10 },\r
+    { "iso8859-13", CS_ISO8859_13 },\r
+    { "iso8859-14", CS_ISO8859_14 },\r
+    { "iso8859-15", CS_ISO8859_15 },\r
+    { "fcd8859-15", CS_ISO8859_15 },\r
+    { "hp-roman8", CS_HP_ROMAN8 },\r
+    { "koi8-r", CS_KOI8_R },\r
+    /*\r
+     * Unofficial encoding names found in the wild.\r
+     */\r
+    { "iso8859-16", CS_ISO8859_16 },\r
+    { "koi8-u", CS_KOI8_U },\r
+    { "ibm-cp437", CS_CP437 },\r
+    { "ibm-cp850", CS_CP850 },\r
+    { "ibm-cp866", CS_CP866 },\r
+    { "microsoft-cp1250", CS_CP1250 },\r
+    { "microsoft-cp1251", CS_CP1251 },\r
+    { "microsoft-cp1252", CS_CP1252 },\r
+    { "microsoft-cp1253", CS_CP1253 },\r
+    { "microsoft-cp1254", CS_CP1254 },\r
+    { "microsoft-cp1255", CS_CP1255 },\r
+    { "microsoft-cp1256", CS_CP1256 },\r
+    { "microsoft-cp1257", CS_CP1257 },\r
+    { "microsoft-cp1258", CS_CP1258 },\r
+    { "mac-roman", CS_MAC_ROMAN },\r
+    { "viscii1.1-1", CS_VISCII },\r
+    { "viscii1-1", CS_VISCII },\r
+};\r
+\r
+const char *charset_to_xenc(int charset)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(xencs); i++)\r
+       if (charset == xencs[i].charset)\r
+           return xencs[i].name;\r
+\r
+    return NULL;                      /* not found */\r
+}\r
+\r
+int charset_from_xenc(const char *name)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < (int)lenof(xencs); i++) {\r
+       const char *p, *q;\r
+       p = name;\r
+       q = xencs[i].name;\r
+       while (*p || *q) {\r
+               if (tolower((unsigned char)*p) != tolower((unsigned char)*q))\r
+               break;\r
+           p++; q++;\r
+       }\r
+       if (!*p && !*q)\r
+           return xencs[i].charset;\r
+    }\r
+\r
+    return CS_NONE;                   /* not found */\r
+}\r
diff --git a/putty/CMDGEN.C b/putty/CMDGEN.C
new file mode 100644 (file)
index 0000000..5d9efcf
--- /dev/null
@@ -0,0 +1,1598 @@
+/*\r
+ * cmdgen.c - command-line form of PuTTYgen\r
+ */\r
+\r
+#define PUTTY_DO_GLOBALS\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <limits.h>\r
+#include <assert.h>\r
+#include <time.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+\r
+#ifdef TEST_CMDGEN\r
+/*\r
+ * This section overrides some definitions below for test purposes.\r
+ * When compiled with -DTEST_CMDGEN:\r
+ * \r
+ *  - Calls to get_random_data() are replaced with the diagnostic\r
+ *    function below (I #define the name so that I can still link\r
+ *    with the original set of modules without symbol clash), in\r
+ *    order to avoid depleting the test system's /dev/random\r
+ *    unnecessarily.\r
+ * \r
+ *  - Calls to console_get_userpass_input() are replaced with the\r
+ *    diagnostic function below, so that I can run tests in an\r
+ *    automated manner and provide their interactive passphrase\r
+ *    inputs.\r
+ * \r
+ *  - main() is renamed to cmdgen_main(); at the bottom of the file\r
+ *    I define another main() which calls the former repeatedly to\r
+ *    run tests.\r
+ */\r
+#define get_random_data get_random_data_diagnostic\r
+char *get_random_data(int len)\r
+{\r
+    char *buf = snewn(len, char);\r
+    memset(buf, 'x', len);\r
+    return buf;\r
+}\r
+#define console_get_userpass_input console_get_userpass_input_diagnostic\r
+int nprompts, promptsgot;\r
+const char *prompts[3];\r
+int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    size_t i;\r
+    int ret = 1;\r
+    for (i = 0; i < p->n_prompts; i++) {\r
+       if (promptsgot < nprompts) {\r
+           assert(strlen(prompts[promptsgot]) < p->prompts[i]->result_len);\r
+           strcpy(p->prompts[i]->result, prompts[promptsgot++]);\r
+       } else {\r
+           promptsgot++;           /* track number of requests anyway */\r
+           ret = 0;\r
+       }\r
+    }\r
+    return ret;\r
+}\r
+#define main cmdgen_main\r
+#endif\r
+\r
+struct progress {\r
+    int phase, current;\r
+};\r
+\r
+static void progress_update(void *param, int action, int phase, int iprogress)\r
+{\r
+    struct progress *p = (struct progress *)param;\r
+    if (action != PROGFN_PROGRESS)\r
+       return;\r
+    if (phase > p->phase) {\r
+       if (p->phase >= 0)\r
+           fputc('\n', stderr);\r
+       p->phase = phase;\r
+       if (iprogress >= 0)\r
+           p->current = iprogress - 1;\r
+       else\r
+           p->current = iprogress;\r
+    }\r
+    while (p->current < iprogress) {\r
+       fputc('+', stdout);\r
+       p->current++;\r
+    }\r
+    fflush(stdout);\r
+}\r
+\r
+static void no_progress(void *param, int action, int phase, int iprogress)\r
+{\r
+}\r
+\r
+void modalfatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    cleanup_exit(1);\r
+}\r
+\r
+/*\r
+ * Stubs to let everything else link sensibly.\r
+ */\r
+void log_eventlog(void *handle, const char *event)\r
+{\r
+}\r
+char *x_get_default(const char *key)\r
+{\r
+    return NULL;\r
+}\r
+void sk_cleanup(void)\r
+{\r
+}\r
+\r
+void showversion(void)\r
+{\r
+    char *verstr = dupstr(ver);\r
+    verstr[0] = tolower((unsigned char)verstr[0]);\r
+    printf("PuTTYgen %s\n", verstr);\r
+    sfree(verstr);\r
+}\r
+\r
+void usage(int standalone)\r
+{\r
+    fprintf(stderr,\r
+           "Usage: puttygen ( keyfile | -t type [ -b bits ] )\n"\r
+           "                [ -C comment ] [ -P ] [ -q ]\n"\r
+           "                [ -o output-keyfile ] [ -O type | -l | -L"\r
+           " | -p ]\n");\r
+    if (standalone)\r
+       fprintf(stderr,\r
+               "Use \"puttygen --help\" for more detail.\n");\r
+}\r
+\r
+void help(void)\r
+{\r
+    /*\r
+     * Help message is an extended version of the usage message. So\r
+     * start with that, plus a version heading.\r
+     */\r
+    showversion();\r
+    usage(FALSE);\r
+    fprintf(stderr,\r
+           "  -t    specify key type when generating (rsa, dsa, rsa1)\n"\r
+           "  -b    specify number of bits when generating key\n"\r
+           "  -C    change or specify key comment\n"\r
+           "  -P    change key passphrase\n"\r
+           "  -q    quiet: do not display progress bar\n"\r
+           "  -O    specify output type:\n"\r
+           "           private             output PuTTY private key format\n"\r
+           "           private-openssh     export OpenSSH private key\n"\r
+           "           private-sshcom      export ssh.com private key\n"\r
+           "           public              standard / ssh.com public key\n"\r
+           "           public-openssh      OpenSSH public key\n"\r
+           "           fingerprint         output the key fingerprint\n"\r
+           "  -o    specify output file\n"\r
+           "  -l    equivalent to `-O fingerprint'\n"\r
+           "  -L    equivalent to `-O public-openssh'\n"\r
+           "  -p    equivalent to `-O public'\n"\r
+           );\r
+}\r
+\r
+static int save_ssh2_pubkey(char *filename, char *comment,\r
+                           void *v_pub_blob, int pub_len)\r
+{\r
+    unsigned char *pub_blob = (unsigned char *)v_pub_blob;\r
+    char *p;\r
+    int i, column;\r
+    FILE *fp;\r
+\r
+    if (filename) {\r
+       fp = fopen(filename, "wb");\r
+       if (!fp)\r
+           return 0;\r
+    } else\r
+       fp = stdout;\r
+\r
+    fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");\r
+\r
+    if (comment) {\r
+       fprintf(fp, "Comment: \"");\r
+       for (p = comment; *p; p++) {\r
+           if (*p == '\\' || *p == '\"')\r
+               fputc('\\', fp);\r
+           fputc(*p, fp);\r
+       }\r
+       fprintf(fp, "\"\n");\r
+    }\r
+\r
+    i = 0;\r
+    column = 0;\r
+    while (i < pub_len) {\r
+       char buf[5];\r
+       int n = (pub_len - i < 3 ? pub_len - i : 3);\r
+       base64_encode_atom(pub_blob + i, n, buf);\r
+       i += n;\r
+       buf[4] = '\0';\r
+       fputs(buf, fp);\r
+       if (++column >= 16) {\r
+           fputc('\n', fp);\r
+           column = 0;\r
+       }\r
+    }\r
+    if (column > 0)\r
+       fputc('\n', fp);\r
+    \r
+    fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");\r
+    if (filename)\r
+       fclose(fp);\r
+    return 1;\r
+}\r
+\r
+static int move(char *from, char *to)\r
+{\r
+    int ret;\r
+\r
+    ret = rename(from, to);\r
+    if (ret) {\r
+       /*\r
+        * This OS may require us to remove the original file first.\r
+        */\r
+       remove(to);\r
+       ret = rename(from, to);\r
+    }\r
+    if (ret) {\r
+       perror("puttygen: cannot move new file on to old one");\r
+       return FALSE;\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+static char *blobfp(char *alg, int bits, unsigned char *blob, int bloblen)\r
+{\r
+    char buffer[128];\r
+    unsigned char digest[16];\r
+    struct MD5Context md5c;\r
+    int i;\r
+\r
+    MD5Init(&md5c);\r
+    MD5Update(&md5c, blob, bloblen);\r
+    MD5Final(digest, &md5c);\r
+\r
+    sprintf(buffer, "%s ", alg);\r
+    if (bits > 0)\r
+       sprintf(buffer + strlen(buffer), "%d ", bits);\r
+    for (i = 0; i < 16; i++)\r
+       sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",\r
+               digest[i]);\r
+\r
+    return dupstr(buffer);\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    char *infile = NULL;\r
+    Filename infilename;\r
+    enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN;    \r
+    char *outfile = NULL, *outfiletmp = NULL;\r
+    Filename outfilename;\r
+    enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE;\r
+    int bits = 1024;\r
+    char *comment = NULL, *origcomment = NULL;\r
+    int change_passphrase = FALSE;\r
+    int errs = FALSE, nogo = FALSE;\r
+    int intype = SSH_KEYTYPE_UNOPENABLE;\r
+    int sshver = 0;\r
+    struct ssh2_userkey *ssh2key = NULL;\r
+    struct RSAKey *ssh1key = NULL;\r
+    unsigned char *ssh2blob = NULL;\r
+    char *ssh2alg = NULL;\r
+    const struct ssh_signkey *ssh2algf = NULL;\r
+    int ssh2bloblen;\r
+    char *passphrase = NULL;\r
+    int load_encrypted;\r
+    progfn_t progressfn = is_interactive() ? progress_update : no_progress;\r
+\r
+    /* ------------------------------------------------------------------\r
+     * Parse the command line to figure out what we've been asked to do.\r
+     */\r
+\r
+    /*\r
+     * If run with no arguments at all, print the usage message and\r
+     * return success.\r
+     */\r
+    if (argc <= 1) {\r
+       usage(TRUE);\r
+       return 0;\r
+    }\r
+\r
+    /*\r
+     * Parse command line arguments.\r
+     */\r
+    while (--argc) {\r
+       char *p = *++argv;\r
+       if (*p == '-') {\r
+           /*\r
+            * An option.\r
+            */\r
+           while (p && *++p) {\r
+               char c = *p;\r
+               switch (c) {\r
+                 case '-':\r
+                   /*\r
+                    * Long option.\r
+                    */\r
+                   {\r
+                       char *opt, *val;\r
+                       opt = p++;     /* opt will have _one_ leading - */\r
+                       while (*p && *p != '=')\r
+                           p++;               /* find end of option */\r
+                       if (*p == '=') {\r
+                           *p++ = '\0';\r
+                           val = p;\r
+                       } else\r
+                            val = NULL;\r
+\r
+                       if (!strcmp(opt, "-help")) {\r
+                            if (val) {\r
+                                errs = TRUE;\r
+                                fprintf(stderr, "puttygen: option `-%s'"\r
+                                        " expects no argument\n", opt);\r
+                            } else {\r
+                                help();\r
+                                nogo = TRUE;\r
+                            }\r
+                       } else if (!strcmp(opt, "-version")) {\r
+                            if (val) {\r
+                                errs = TRUE;\r
+                                fprintf(stderr, "puttygen: option `-%s'"\r
+                                        " expects no argument\n", opt);\r
+                            } else {\r
+                                showversion();\r
+                                nogo = TRUE;\r
+                            }\r
+                       } else if (!strcmp(opt, "-pgpfp")) {\r
+                            if (val) {\r
+                                errs = TRUE;\r
+                                fprintf(stderr, "puttygen: option `-%s'"\r
+                                        " expects no argument\n", opt);\r
+                            } else {\r
+                                /* support --pgpfp for consistency */\r
+                                pgp_fingerprints();\r
+                                nogo = TRUE;\r
+                            }\r
+                        }\r
+                       /*\r
+                        * For long options requiring an argument, add\r
+                        * code along the lines of\r
+                        * \r
+                        * else if (!strcmp(opt, "-output")) {\r
+                        *     if (!val) {\r
+                        *         errs = TRUE;\r
+                         *         fprintf(stderr, "puttygen: option `-%s'"\r
+                         *                 " expects an argument\n", opt);\r
+                        *     } else\r
+                        *         ofile = val;\r
+                        * }\r
+                        */\r
+                       else {\r
+                           errs = TRUE;\r
+                           fprintf(stderr,\r
+                                   "puttygen: no such option `-%s'\n", opt);\r
+                       }\r
+                   }\r
+                   p = NULL;\r
+                   break;\r
+                 case 'h':\r
+                 case 'V':\r
+                 case 'P':\r
+                 case 'l':\r
+                 case 'L':\r
+                 case 'p':\r
+                 case 'q':\r
+                   /*\r
+                    * Option requiring no parameter.\r
+                    */\r
+                   switch (c) {\r
+                     case 'h':\r
+                       help();\r
+                       nogo = TRUE;\r
+                       break;\r
+                     case 'V':\r
+                       showversion();\r
+                       nogo = TRUE;\r
+                       break;\r
+                     case 'P':\r
+                       change_passphrase = TRUE;\r
+                       break;\r
+                     case 'l':\r
+                       outtype = FP;\r
+                       break;\r
+                     case 'L':\r
+                       outtype = PUBLICO;\r
+                       break;\r
+                     case 'p':\r
+                       outtype = PUBLIC;\r
+                       break;\r
+                     case 'q':\r
+                       progressfn = no_progress;\r
+                       break;\r
+                   }\r
+                   break;\r
+                 case 't':\r
+                 case 'b':\r
+                 case 'C':\r
+                 case 'O':\r
+                 case 'o':\r
+                   /*\r
+                    * Option requiring parameter.\r
+                    */\r
+                   p++;\r
+                   if (!*p && argc > 1)\r
+                       --argc, p = *++argv;\r
+                   else if (!*p) {\r
+                       fprintf(stderr, "puttygen: option `-%c' expects a"\r
+                               " parameter\n", c);\r
+                       errs = TRUE;\r
+                   }\r
+                   /*\r
+                    * Now c is the option and p is the parameter.\r
+                    */\r
+                   switch (c) {\r
+                     case 't':\r
+                       if (!strcmp(p, "rsa") || !strcmp(p, "rsa2"))\r
+                           keytype = RSA2, sshver = 2;\r
+                       else if (!strcmp(p, "rsa1"))\r
+                           keytype = RSA1, sshver = 1;\r
+                       else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))\r
+                           keytype = DSA, sshver = 2;\r
+                       else {\r
+                           fprintf(stderr,\r
+                                   "puttygen: unknown key type `%s'\n", p);\r
+                           errs = TRUE;\r
+                       }\r
+                        break;\r
+                     case 'b':\r
+                       bits = atoi(p);\r
+                        break;\r
+                     case 'C':\r
+                       comment = p;\r
+                        break;\r
+                     case 'O':\r
+                       if (!strcmp(p, "public"))\r
+                           outtype = PUBLIC;\r
+                       else if (!strcmp(p, "public-openssh"))\r
+                           outtype = PUBLICO;\r
+                       else if (!strcmp(p, "private"))\r
+                           outtype = PRIVATE;\r
+                       else if (!strcmp(p, "fingerprint"))\r
+                           outtype = FP;\r
+                       else if (!strcmp(p, "private-openssh"))\r
+                           outtype = OPENSSH, sshver = 2;\r
+                       else if (!strcmp(p, "private-sshcom"))\r
+                           outtype = SSHCOM, sshver = 2;\r
+                       else {\r
+                           fprintf(stderr,\r
+                                   "puttygen: unknown output type `%s'\n", p);\r
+                           errs = TRUE;\r
+                       }\r
+                        break;\r
+                     case 'o':\r
+                       outfile = p;\r
+                        break;\r
+                   }\r
+                   p = NULL;          /* prevent continued processing */\r
+                   break;\r
+                 default:\r
+                   /*\r
+                    * Unrecognised option.\r
+                    */\r
+                   errs = TRUE;\r
+                   fprintf(stderr, "puttygen: no such option `-%c'\n", c);\r
+                   break;\r
+               }\r
+           }\r
+       } else {\r
+           /*\r
+            * A non-option argument.\r
+            */\r
+           if (!infile)\r
+               infile = p;\r
+           else {\r
+               errs = TRUE;\r
+               fprintf(stderr, "puttygen: cannot handle more than one"\r
+                       " input file\n");\r
+           }\r
+       }\r
+    }\r
+\r
+    if (errs)\r
+       return 1;\r
+\r
+    if (nogo)\r
+       return 0;\r
+\r
+    /*\r
+     * If run with at least one argument _but_ not the required\r
+     * ones, print the usage message and return failure.\r
+     */\r
+    if (!infile && keytype == NOKEYGEN) {\r
+       usage(TRUE);\r
+       return 1;\r
+    }\r
+\r
+    /* ------------------------------------------------------------------\r
+     * Figure out further details of exactly what we're going to do.\r
+     */\r
+\r
+    /*\r
+     * Bomb out if we've been asked to both load and generate a\r
+     * key.\r
+     */\r
+    if (keytype != NOKEYGEN && infile) {\r
+       fprintf(stderr, "puttygen: cannot both load and generate a key\n");\r
+       return 1;\r
+    }\r
+\r
+    /* \r
+     * We must save the private part when generating a new key.\r
+     */\r
+    if (keytype != NOKEYGEN &&\r
+       (outtype != PRIVATE && outtype != OPENSSH && outtype != SSHCOM)) {\r
+       fprintf(stderr, "puttygen: this would generate a new key but "\r
+               "discard the private part\n");\r
+       return 1;\r
+    }\r
+\r
+    /*\r
+     * Analyse the type of the input file, in case this affects our\r
+     * course of action.\r
+     */\r
+    if (infile) {\r
+       infilename = filename_from_str(infile);\r
+\r
+       intype = key_type(&infilename);\r
+\r
+       switch (intype) {\r
+           /*\r
+            * It would be nice here to be able to load _public_\r
+            * key files, in any of a number of forms, and (a)\r
+            * convert them to other public key types, (b) print\r
+            * out their fingerprints. Or, I suppose, for real\r
+            * orthogonality, (c) change their comment!\r
+            * \r
+            * In fact this opens some interesting possibilities.\r
+            * Suppose ssh2_userkey_loadpub() were able to load\r
+            * public key files as well as extracting the public\r
+            * key from private ones. And suppose I did the thing\r
+            * I've been wanting to do, where specifying a\r
+            * particular private key file for authentication\r
+            * causes any _other_ key in the agent to be discarded.\r
+            * Then, if you had an agent forwarded to the machine\r
+            * you were running Unix PuTTY or Plink on, and you\r
+            * needed to specify which of the keys in the agent it\r
+            * should use, you could do that by supplying a\r
+            * _public_ key file, thus not needing to trust even\r
+            * your encrypted private key file to the network. Ooh!\r
+            */\r
+\r
+         case SSH_KEYTYPE_UNOPENABLE:\r
+         case SSH_KEYTYPE_UNKNOWN:\r
+           fprintf(stderr, "puttygen: unable to load file `%s': %s\n",\r
+                   infile, key_type_to_str(intype));\r
+           return 1;\r
+\r
+         case SSH_KEYTYPE_SSH1:\r
+           if (sshver == 2) {\r
+               fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys"\r
+                       " not supported\n");\r
+               return 1;\r
+           }\r
+           sshver = 1;\r
+           break;\r
+\r
+         case SSH_KEYTYPE_SSH2:\r
+         case SSH_KEYTYPE_OPENSSH:\r
+         case SSH_KEYTYPE_SSHCOM:\r
+           if (sshver == 1) {\r
+               fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys"\r
+                       " not supported\n");\r
+               return 1;\r
+           }\r
+           sshver = 2;\r
+           break;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Determine the default output file, if none is provided.\r
+     * \r
+     * This will usually be equal to stdout, except that if the\r
+     * input and output file formats are the same then the default\r
+     * output is to overwrite the input.\r
+     * \r
+     * Also in this code, we bomb out if the input and output file\r
+     * formats are the same and no other action is performed.\r
+     */\r
+    if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) ||\r
+       (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) ||\r
+       (intype == SSH_KEYTYPE_OPENSSH && outtype == OPENSSH) ||\r
+       (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {\r
+       if (!outfile) {\r
+           outfile = infile;\r
+           outfiletmp = dupcat(outfile, ".tmp", NULL);\r
+       }\r
+\r
+       if (!change_passphrase && !comment) {\r
+           fprintf(stderr, "puttygen: this command would perform no useful"\r
+                   " action\n");\r
+           return 1;\r
+       }\r
+    } else {\r
+       if (!outfile) {\r
+           /*\r
+            * Bomb out rather than automatically choosing to write\r
+            * a private key file to stdout.\r
+            */\r
+           if (outtype==PRIVATE || outtype==OPENSSH || outtype==SSHCOM) {\r
+               fprintf(stderr, "puttygen: need to specify an output file\n");\r
+               return 1;\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Figure out whether we need to load the encrypted part of the\r
+     * key. This will be the case if either (a) we need to write\r
+     * out a private key format, or (b) the entire input key file\r
+     * is encrypted.\r
+     */\r
+    if (outtype == PRIVATE || outtype == OPENSSH || outtype == SSHCOM ||\r
+       intype == SSH_KEYTYPE_OPENSSH || intype == SSH_KEYTYPE_SSHCOM)\r
+       load_encrypted = TRUE;\r
+    else\r
+       load_encrypted = FALSE;\r
+\r
+    /* ------------------------------------------------------------------\r
+     * Now we're ready to actually do some stuff.\r
+     */\r
+\r
+    /*\r
+     * Either load or generate a key.\r
+     */\r
+    if (keytype != NOKEYGEN) {\r
+       char *entropy;\r
+       char default_comment[80];\r
+       struct tm tm;\r
+       struct progress prog;\r
+\r
+       prog.phase = -1;\r
+       prog.current = -1;\r
+\r
+       tm = ltime();\r
+       if (keytype == DSA)\r
+           strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);\r
+       else\r
+           strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);\r
+\r
+       random_ref();\r
+       entropy = get_random_data(bits / 8);\r
+       if (!entropy) {\r
+           fprintf(stderr, "puttygen: failed to collect entropy, "\r
+                   "could not generate key\n");\r
+           return 1;\r
+       }\r
+       random_add_heavynoise(entropy, bits / 8);\r
+       memset(entropy, 0, bits/8);\r
+       sfree(entropy);\r
+\r
+       if (keytype == DSA) {\r
+           struct dss_key *dsskey = snew(struct dss_key);\r
+           dsa_generate(dsskey, bits, progressfn, &prog);\r
+           ssh2key = snew(struct ssh2_userkey);\r
+           ssh2key->data = dsskey;\r
+           ssh2key->alg = &ssh_dss;\r
+           ssh1key = NULL;\r
+       } else {\r
+           struct RSAKey *rsakey = snew(struct RSAKey);\r
+           rsa_generate(rsakey, bits, progressfn, &prog);\r
+           rsakey->comment = NULL;\r
+           if (keytype == RSA1) {\r
+               ssh1key = rsakey;\r
+           } else {\r
+               ssh2key = snew(struct ssh2_userkey);\r
+               ssh2key->data = rsakey;\r
+               ssh2key->alg = &ssh_rsa;\r
+           }\r
+       }\r
+       progressfn(&prog, PROGFN_PROGRESS, INT_MAX, -1);\r
+\r
+       if (ssh2key)\r
+           ssh2key->comment = dupstr(default_comment);\r
+       if (ssh1key)\r
+           ssh1key->comment = dupstr(default_comment);\r
+\r
+    } else {\r
+       const char *error = NULL;\r
+       int encrypted;\r
+\r
+       assert(infile != NULL);\r
+\r
+       /*\r
+        * Find out whether the input key is encrypted.\r
+        */\r
+       if (intype == SSH_KEYTYPE_SSH1)\r
+           encrypted = rsakey_encrypted(&infilename, &origcomment);\r
+       else if (intype == SSH_KEYTYPE_SSH2)\r
+           encrypted = ssh2_userkey_encrypted(&infilename, &origcomment);\r
+       else\r
+           encrypted = import_encrypted(&infilename, intype, &origcomment);\r
+\r
+       /*\r
+        * If so, ask for a passphrase.\r
+        */\r
+       if (encrypted && load_encrypted) {\r
+           prompts_t *p = new_prompts(NULL);\r
+           int ret;\r
+           p->to_server = FALSE;\r
+           p->name = dupstr("SSH key passphrase");\r
+           add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE, 512);\r
+           ret = console_get_userpass_input(p, NULL, 0);\r
+           assert(ret >= 0);\r
+           if (!ret) {\r
+               free_prompts(p);\r
+               perror("puttygen: unable to read passphrase");\r
+               return 1;\r
+           } else {\r
+               passphrase = dupstr(p->prompts[0]->result);\r
+               free_prompts(p);\r
+           }\r
+       } else {\r
+           passphrase = NULL;\r
+       }\r
+\r
+       switch (intype) {\r
+           int ret;\r
+\r
+         case SSH_KEYTYPE_SSH1:\r
+           ssh1key = snew(struct RSAKey);\r
+           if (!load_encrypted) {\r
+               void *vblob;\r
+               unsigned char *blob;\r
+               int n, l, bloblen;\r
+\r
+               ret = rsakey_pubblob(&infilename, &vblob, &bloblen,\r
+                                    &origcomment, &error);\r
+               blob = (unsigned char *)vblob;\r
+\r
+               n = 4;                 /* skip modulus bits */\r
+               \r
+               l = ssh1_read_bignum(blob + n, bloblen - n,\r
+                                    &ssh1key->exponent);\r
+               if (l < 0) {\r
+                   error = "SSH-1 public key blob was too short";\r
+               } else {\r
+                   n += l;\r
+                   l = ssh1_read_bignum(blob + n, bloblen - n,\r
+                                        &ssh1key->modulus);\r
+                   if (l < 0) {\r
+                       error = "SSH-1 public key blob was too short";\r
+                   } else\r
+                       n += l;\r
+               }\r
+               ssh1key->comment = dupstr(origcomment);\r
+               ssh1key->private_exponent = NULL;\r
+           } else {\r
+               ret = loadrsakey(&infilename, ssh1key, passphrase, &error);\r
+           }\r
+           if (ret > 0)\r
+               error = NULL;\r
+           else if (!error)\r
+               error = "unknown error";\r
+           break;\r
+\r
+         case SSH_KEYTYPE_SSH2:\r
+           if (!load_encrypted) {\r
+               ssh2blob = ssh2_userkey_loadpub(&infilename, &ssh2alg,\r
+                                               &ssh2bloblen, NULL, &error);\r
+               ssh2algf = find_pubkey_alg(ssh2alg);\r
+               if (ssh2algf)\r
+                   bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen);\r
+               else\r
+                   bits = -1;\r
+           } else {\r
+               ssh2key = ssh2_load_userkey(&infilename, passphrase, &error);\r
+           }\r
+           if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)\r
+               error = NULL;\r
+           else if (!error) {\r
+               if (ssh2key == SSH2_WRONG_PASSPHRASE)\r
+                   error = "wrong passphrase";\r
+               else\r
+                   error = "unknown error";\r
+           }\r
+           break;\r
+\r
+         case SSH_KEYTYPE_OPENSSH:\r
+         case SSH_KEYTYPE_SSHCOM:\r
+           ssh2key = import_ssh2(&infilename, intype, passphrase, &error);\r
+           if (ssh2key) {\r
+               if (ssh2key != SSH2_WRONG_PASSPHRASE)\r
+                   error = NULL;\r
+               else\r
+                   error = "wrong passphrase";\r
+           } else if (!error)\r
+               error = "unknown error";\r
+           break;\r
+\r
+         default:\r
+           assert(0);\r
+       }\r
+\r
+       if (error) {\r
+           fprintf(stderr, "puttygen: error loading `%s': %s\n",\r
+                   infile, error);\r
+           return 1;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Change the comment if asked to.\r
+     */\r
+    if (comment) {\r
+       if (sshver == 1) {\r
+           assert(ssh1key);\r
+           sfree(ssh1key->comment);\r
+           ssh1key->comment = dupstr(comment);\r
+       } else {\r
+           assert(ssh2key);\r
+           sfree(ssh2key->comment);\r
+           ssh2key->comment = dupstr(comment);\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Prompt for a new passphrase if we have been asked to, or if\r
+     * we have just generated a key.\r
+     */\r
+    if (change_passphrase || keytype != NOKEYGEN) {\r
+       prompts_t *p = new_prompts(NULL);\r
+       int ret;\r
+\r
+       p->to_server = FALSE;\r
+       p->name = dupstr("New SSH key passphrase");\r
+       add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE, 512);\r
+       add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE, 512);\r
+       ret = console_get_userpass_input(p, NULL, 0);\r
+       assert(ret >= 0);\r
+       if (!ret) {\r
+           free_prompts(p);\r
+           perror("puttygen: unable to read new passphrase");\r
+           return 1;\r
+       } else {\r
+           if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) {\r
+               free_prompts(p);\r
+               fprintf(stderr, "puttygen: passphrases do not match\n");\r
+               return 1;\r
+           }\r
+           if (passphrase) {\r
+               memset(passphrase, 0, strlen(passphrase));\r
+               sfree(passphrase);\r
+           }\r
+           passphrase = dupstr(p->prompts[0]->result);\r
+           free_prompts(p);\r
+           if (!*passphrase) {\r
+               sfree(passphrase);\r
+               passphrase = NULL;\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Write output.\r
+     * \r
+     * (In the case where outfile and outfiletmp are both NULL,\r
+     * there is no semantic reason to initialise outfilename at\r
+     * all; but we have to write _something_ to it or some compiler\r
+     * will probably complain that it might be used uninitialised.)\r
+     */\r
+    if (outfiletmp)\r
+       outfilename = filename_from_str(outfiletmp);\r
+    else\r
+       outfilename = filename_from_str(outfile ? outfile : "");\r
+\r
+    switch (outtype) {\r
+       int ret;\r
+\r
+      case PRIVATE:\r
+       if (sshver == 1) {\r
+           assert(ssh1key);\r
+           ret = saversakey(&outfilename, ssh1key, passphrase);\r
+           if (!ret) {\r
+               fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");\r
+               return 1;\r
+           }\r
+       } else {\r
+           assert(ssh2key);\r
+           ret = ssh2_save_userkey(&outfilename, ssh2key, passphrase);\r
+           if (!ret) {\r
+               fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");\r
+               return 1;\r
+           }\r
+       }\r
+       if (outfiletmp) {\r
+           if (!move(outfiletmp, outfile))\r
+               return 1;              /* rename failed */\r
+       }\r
+       break;\r
+\r
+      case PUBLIC:\r
+      case PUBLICO:\r
+       if (sshver == 1) {\r
+           FILE *fp;\r
+           char *dec1, *dec2;\r
+\r
+           assert(ssh1key);\r
+\r
+           if (outfile)\r
+               fp = f_open(outfilename, "w", FALSE);\r
+           else\r
+               fp = stdout;\r
+           dec1 = bignum_decimal(ssh1key->exponent);\r
+           dec2 = bignum_decimal(ssh1key->modulus);\r
+           fprintf(fp, "%d %s %s %s\n", bignum_bitcount(ssh1key->modulus),\r
+                   dec1, dec2, ssh1key->comment);\r
+           sfree(dec1);\r
+           sfree(dec2);\r
+           if (outfile)\r
+               fclose(fp);\r
+       } else if (outtype == PUBLIC) {\r
+           if (!ssh2blob) {\r
+               assert(ssh2key);\r
+               ssh2blob = ssh2key->alg->public_blob(ssh2key->data,\r
+                                                    &ssh2bloblen);\r
+           }\r
+           save_ssh2_pubkey(outfile, ssh2key ? ssh2key->comment : origcomment,\r
+                            ssh2blob, ssh2bloblen);\r
+       } else if (outtype == PUBLICO) {\r
+           char *buffer, *p;\r
+           int i;\r
+           FILE *fp;\r
+\r
+           if (!ssh2blob) {\r
+               assert(ssh2key);\r
+               ssh2blob = ssh2key->alg->public_blob(ssh2key->data,\r
+                                                    &ssh2bloblen);\r
+           }\r
+           if (!ssh2alg) {\r
+               assert(ssh2key);\r
+               ssh2alg = ssh2key->alg->name;\r
+           }\r
+           if (ssh2key)\r
+               comment = ssh2key->comment;\r
+           else\r
+               comment = origcomment;\r
+\r
+           buffer = snewn(strlen(ssh2alg) +\r
+                          4 * ((ssh2bloblen+2) / 3) +\r
+                          strlen(comment) + 3, char);\r
+           strcpy(buffer, ssh2alg);\r
+           p = buffer + strlen(buffer);\r
+           *p++ = ' ';\r
+           i = 0;\r
+           while (i < ssh2bloblen) {\r
+               int n = (ssh2bloblen - i < 3 ? ssh2bloblen - i : 3);\r
+               base64_encode_atom(ssh2blob + i, n, p);\r
+               i += n;\r
+               p += 4;\r
+           }\r
+           if (*comment) {\r
+               *p++ = ' ';\r
+               strcpy(p, comment);\r
+           } else\r
+               *p++ = '\0';\r
+\r
+           if (outfile)\r
+               fp = f_open(outfilename, "w", FALSE);\r
+           else\r
+               fp = stdout;\r
+           fprintf(fp, "%s\n", buffer);\r
+           if (outfile)\r
+               fclose(fp);\r
+\r
+           sfree(buffer);\r
+       }\r
+       break;\r
+\r
+      case FP:\r
+       {\r
+           FILE *fp;\r
+           char *fingerprint;\r
+\r
+           if (sshver == 1) {\r
+               assert(ssh1key);\r
+               fingerprint = snewn(128, char);\r
+               rsa_fingerprint(fingerprint, 128, ssh1key);\r
+           } else {\r
+               if (ssh2key) {\r
+                   fingerprint = ssh2key->alg->fingerprint(ssh2key->data);\r
+               } else {\r
+                   assert(ssh2blob);\r
+                   fingerprint = blobfp(ssh2alg, bits, ssh2blob, ssh2bloblen);\r
+               }\r
+           }\r
+\r
+           if (outfile)\r
+               fp = f_open(outfilename, "w", FALSE);\r
+           else\r
+               fp = stdout;\r
+           fprintf(fp, "%s\n", fingerprint);\r
+           if (outfile)\r
+               fclose(fp);\r
+\r
+           sfree(fingerprint);\r
+       }\r
+       break;\r
+       \r
+      case OPENSSH:\r
+      case SSHCOM:\r
+       assert(sshver == 2);\r
+       assert(ssh2key);\r
+       ret = export_ssh2(&outfilename, outtype, ssh2key, passphrase);\r
+       if (!ret) {\r
+           fprintf(stderr, "puttygen: unable to export key\n");\r
+           return 1;\r
+       }\r
+       if (outfiletmp) {\r
+           if (!move(outfiletmp, outfile))\r
+               return 1;              /* rename failed */\r
+       }\r
+       break;\r
+    }\r
+\r
+    if (passphrase) {\r
+       memset(passphrase, 0, strlen(passphrase));\r
+       sfree(passphrase);\r
+    }\r
+\r
+    if (ssh1key)\r
+       freersakey(ssh1key);\r
+    if (ssh2key) {\r
+       ssh2key->alg->freekey(ssh2key->data);\r
+       sfree(ssh2key);\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+#ifdef TEST_CMDGEN\r
+\r
+#undef main\r
+\r
+#include <stdarg.h>\r
+\r
+int passes, fails;\r
+\r
+void setup_passphrases(char *first, ...)\r
+{\r
+    va_list ap;\r
+    char *next;\r
+\r
+    nprompts = 0;\r
+    if (first) {\r
+       prompts[nprompts++] = first;\r
+       va_start(ap, first);\r
+       while ((next = va_arg(ap, char *)) != NULL) {\r
+           assert(nprompts < lenof(prompts));\r
+           prompts[nprompts++] = next;\r
+       }\r
+       va_end(ap);\r
+    }\r
+}\r
+\r
+void test(int retval, ...)\r
+{\r
+    va_list ap;\r
+    int i, argc, ret;\r
+    char **argv;\r
+\r
+    argc = 0;\r
+    va_start(ap, retval);\r
+    while (va_arg(ap, char *) != NULL)\r
+       argc++;\r
+    va_end(ap);\r
+\r
+    argv = snewn(argc+1, char *);\r
+    va_start(ap, retval);\r
+    for (i = 0; i <= argc; i++)\r
+       argv[i] = va_arg(ap, char *);\r
+    va_end(ap);\r
+\r
+    promptsgot = 0;\r
+    ret = cmdgen_main(argc, argv);\r
+\r
+    if (ret != retval) {\r
+       printf("FAILED retval (exp %d got %d):", retval, ret);\r
+       for (i = 0; i < argc; i++)\r
+           printf(" %s", argv[i]);\r
+       printf("\n");\r
+       fails++;\r
+    } else if (promptsgot != nprompts) {\r
+       printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot);\r
+       for (i = 0; i < argc; i++)\r
+           printf(" %s", argv[i]);\r
+       printf("\n");\r
+       fails++;\r
+    } else {\r
+       passes++;\r
+    }\r
+}\r
+\r
+void filecmp(char *file1, char *file2, char *fmt, ...)\r
+{\r
+    /*\r
+     * Ideally I should do file comparison myself, to maximise the\r
+     * portability of this test suite once this application begins\r
+     * running on non-Unix platforms. For the moment, though,\r
+     * calling Unix diff is perfectly adequate.\r
+     */\r
+    char *buf;\r
+    int ret;\r
+\r
+    buf = dupprintf("diff -q '%s' '%s'", file1, file2);\r
+    ret = system(buf);\r
+    sfree(buf);\r
+\r
+    if (ret) {\r
+       va_list ap;\r
+\r
+       printf("FAILED diff (ret=%d): ", ret);\r
+\r
+       va_start(ap, fmt);\r
+       vprintf(fmt, ap);\r
+       va_end(ap);\r
+\r
+       printf("\n");\r
+\r
+       fails++;\r
+    } else\r
+       passes++;\r
+}\r
+\r
+char *cleanup_fp(char *s)\r
+{\r
+    char *p;\r
+\r
+    if (!strncmp(s, "ssh-", 4)) {\r
+       s += strcspn(s, " \n\t");\r
+       s += strspn(s, " \n\t");\r
+    }\r
+\r
+    p = s;\r
+    s += strcspn(s, " \n\t");\r
+    s += strspn(s, " \n\t");\r
+    s += strcspn(s, " \n\t");\r
+\r
+    return dupprintf("%.*s", s - p, p);\r
+}\r
+\r
+char *get_fp(char *filename)\r
+{\r
+    FILE *fp;\r
+    char buf[256], *ret;\r
+\r
+    fp = fopen(filename, "r");\r
+    if (!fp)\r
+       return NULL;\r
+    ret = fgets(buf, sizeof(buf), fp);\r
+    fclose(fp);\r
+    if (!ret)\r
+       return NULL;\r
+    return cleanup_fp(buf);\r
+}\r
+\r
+void check_fp(char *filename, char *fp, char *fmt, ...)\r
+{\r
+    char *newfp;\r
+\r
+    if (!fp)\r
+       return;\r
+\r
+    newfp = get_fp(filename);\r
+\r
+    if (!strcmp(fp, newfp)) {\r
+       passes++;\r
+    } else {\r
+       va_list ap;\r
+\r
+       printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp);\r
+\r
+       va_start(ap, fmt);\r
+       vprintf(fmt, ap);\r
+       va_end(ap);\r
+\r
+       printf("\n");\r
+\r
+       fails++;\r
+    }\r
+\r
+    sfree(newfp);\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    int i;\r
+    static char *const keytypes[] = { "rsa1", "dsa", "rsa" };\r
+\r
+    /*\r
+     * Even when this thing is compiled for automatic test mode,\r
+     * it's helpful to be able to invoke it with command-line\r
+     * options for _manual_ tests.\r
+     */\r
+    if (argc > 1)\r
+       return cmdgen_main(argc, argv);\r
+\r
+    passes = fails = 0;\r
+\r
+    for (i = 0; i < lenof(keytypes); i++) {\r
+       char filename[128], osfilename[128], scfilename[128];\r
+       char pubfilename[128], tmpfilename1[128], tmpfilename2[128];\r
+       char *fp;\r
+\r
+       sprintf(filename, "test-%s.ppk", keytypes[i]);\r
+       sprintf(pubfilename, "test-%s.pub", keytypes[i]);\r
+       sprintf(osfilename, "test-%s.os", keytypes[i]);\r
+       sprintf(scfilename, "test-%s.sc", keytypes[i]);\r
+       sprintf(tmpfilename1, "test-%s.tmp1", keytypes[i]);\r
+       sprintf(tmpfilename2, "test-%s.tmp2", keytypes[i]);\r
+\r
+       /*\r
+        * Create an encrypted key.\r
+        */\r
+       setup_passphrases("sponge", "sponge", NULL);\r
+       test(0, "puttygen", "-t", keytypes[i], "-o", filename, NULL);\r
+\r
+       /*\r
+        * List the public key in OpenSSH format.\r
+        */\r
+       setup_passphrases(NULL);\r
+       test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL);\r
+       {\r
+           char cmdbuf[256];\r
+           fp = NULL;\r
+           sprintf(cmdbuf, "ssh-keygen -l -f '%s' > '%s'",\r
+                   pubfilename, tmpfilename1);\r
+           if (system(cmdbuf) ||\r
+               (fp = get_fp(tmpfilename1)) == NULL) {\r
+               printf("UNABLE to test fingerprint matching against OpenSSH");\r
+           }\r
+       }\r
+\r
+       /*\r
+        * List the public key in IETF/ssh.com format.\r
+        */\r
+       setup_passphrases(NULL);\r
+       test(0, "puttygen", "-p", filename, NULL);\r
+\r
+       /*\r
+        * List the fingerprint of the key.\r
+        */\r
+       setup_passphrases(NULL);\r
+       test(0, "puttygen", "-l", filename, "-o", tmpfilename1, NULL);\r
+       if (!fp) {\r
+           /*\r
+            * If we can't test fingerprints against OpenSSH, we\r
+            * can at the very least test equality of all the\r
+            * fingerprints we generate of this key throughout\r
+            * testing.\r
+            */\r
+           fp = get_fp(tmpfilename1);\r
+       } else {\r
+           check_fp(tmpfilename1, fp, "%s initial fp", keytypes[i]);\r
+       }\r
+\r
+       /*\r
+        * Change the comment of the key; this _does_ require a\r
+        * passphrase owing to the tamperproofing.\r
+        * \r
+        * NOTE: In SSH-1, this only requires a passphrase because\r
+        * of inadequacies of the loading and saving mechanisms. In\r
+        * _principle_, it should be perfectly possible to modify\r
+        * the comment on an SSH-1 key without requiring a\r
+        * passphrase; the only reason I can't do it is because my\r
+        * loading and saving mechanisms don't include a method of\r
+        * loading all the key data without also trying to decrypt\r
+        * the private section.\r
+        * \r
+        * I don't consider this to be a problem worth solving,\r
+        * because (a) to fix it would probably end up bloating\r
+        * PuTTY proper, and (b) SSH-1 is on the way out anyway so\r
+        * it shouldn't be highly significant. If it seriously\r
+        * bothers anyone then perhaps I _might_ be persuadable.\r
+        */\r
+       setup_passphrases("sponge", NULL);\r
+       test(0, "puttygen", "-C", "new-comment", filename, NULL);\r
+\r
+       /*\r
+        * Change the passphrase to nothing.\r
+        */\r
+       setup_passphrases("sponge", "", "", NULL);\r
+       test(0, "puttygen", "-P", filename, NULL);\r
+\r
+       /*\r
+        * Change the comment of the key again; this time we expect no\r
+        * passphrase to be required.\r
+        */\r
+       setup_passphrases(NULL);\r
+       test(0, "puttygen", "-C", "new-comment-2", filename, NULL);\r
+\r
+       /*\r
+        * Export the private key into OpenSSH format; no passphrase\r
+        * should be required since the key is currently unencrypted.\r
+        * For RSA1 keys, this should give an error.\r
+        */\r
+       setup_passphrases(NULL);\r
+       test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,\r
+            filename, NULL);\r
+\r
+       if (i) {\r
+           /*\r
+            * List the fingerprint of the OpenSSH-formatted key.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);\r
+           check_fp(tmpfilename1, fp, "%s openssh clear fp", keytypes[i]);\r
+\r
+           /*\r
+            * List the public half of the OpenSSH-formatted key in\r
+            * OpenSSH format.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", "-L", osfilename, NULL);\r
+\r
+           /*\r
+            * List the public half of the OpenSSH-formatted key in\r
+            * IETF/ssh.com format.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", "-p", osfilename, NULL);\r
+       }\r
+\r
+       /*\r
+        * Export the private key into ssh.com format; no passphrase\r
+        * should be required since the key is currently unencrypted.\r
+        * For RSA1 keys, this should give an error.\r
+        */\r
+       setup_passphrases(NULL);\r
+       test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,\r
+            filename, NULL);\r
+\r
+       if (i) {\r
+           /*\r
+            * List the fingerprint of the ssh.com-formatted key.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);\r
+           check_fp(tmpfilename1, fp, "%s ssh.com clear fp", keytypes[i]);\r
+\r
+           /*\r
+            * List the public half of the ssh.com-formatted key in\r
+            * OpenSSH format.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", "-L", scfilename, NULL);\r
+\r
+           /*\r
+            * List the public half of the ssh.com-formatted key in\r
+            * IETF/ssh.com format.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", "-p", scfilename, NULL);\r
+       }\r
+\r
+       if (i) {\r
+           /*\r
+            * Convert from OpenSSH into ssh.com.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", osfilename, "-o", tmpfilename1,\r
+                "-O", "private-sshcom", NULL);\r
+\r
+           /*\r
+            * Convert from ssh.com back into a PuTTY key,\r
+            * supplying the same comment as we had before we\r
+            * started to ensure the comparison works.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",\r
+                "-o", tmpfilename2, NULL);\r
+\r
+           /*\r
+            * See if the PuTTY key thus generated is the same as\r
+            * the original.\r
+            */\r
+           filecmp(filename, tmpfilename2,\r
+                   "p->o->s->p clear %s", keytypes[i]);\r
+\r
+           /*\r
+            * Convert from ssh.com to OpenSSH.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", scfilename, "-o", tmpfilename1,\r
+                "-O", "private-openssh", NULL);\r
+\r
+           /*\r
+            * Convert from OpenSSH back into a PuTTY key,\r
+            * supplying the same comment as we had before we\r
+            * started to ensure the comparison works.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",\r
+                "-o", tmpfilename2, NULL);\r
+\r
+           /*\r
+            * See if the PuTTY key thus generated is the same as\r
+            * the original.\r
+            */\r
+           filecmp(filename, tmpfilename2,\r
+                   "p->s->o->p clear %s", keytypes[i]);\r
+\r
+           /*\r
+            * Finally, do a round-trip conversion between PuTTY\r
+            * and ssh.com without involving OpenSSH, to test that\r
+            * the key comment is preserved in that case.\r
+            */\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,\r
+                filename, NULL);\r
+           setup_passphrases(NULL);\r
+           test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);\r
+           filecmp(filename, tmpfilename2,\r
+                   "p->s->p clear %s", keytypes[i]);\r
+       }\r
+\r
+       /*\r
+        * Check that mismatched passphrases cause an error.\r
+        */\r
+       setup_passphrases("sponge2", "sponge3", NULL);\r
+       test(1, "puttygen", "-P", filename, NULL);\r
+\r
+       /*\r
+        * Put a passphrase back on.\r
+        */\r
+       setup_passphrases("sponge2", "sponge2", NULL);\r
+       test(0, "puttygen", "-P", filename, NULL);\r
+\r
+       /*\r
+        * Export the private key into OpenSSH format, this time\r
+        * while encrypted. For RSA1 keys, this should give an\r
+        * error.\r
+        */\r
+       if (i == 0)\r
+           setup_passphrases(NULL);   /* error, hence no passphrase read */\r
+       else\r
+           setup_passphrases("sponge2", NULL);\r
+       test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,\r
+            filename, NULL);\r
+\r
+       if (i) {\r
+           /*\r
+            * List the fingerprint of the OpenSSH-formatted key.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);\r
+           check_fp(tmpfilename1, fp, "%s openssh encrypted fp", keytypes[i]);\r
+\r
+           /*\r
+            * List the public half of the OpenSSH-formatted key in\r
+            * OpenSSH format.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", "-L", osfilename, NULL);\r
+\r
+           /*\r
+            * List the public half of the OpenSSH-formatted key in\r
+            * IETF/ssh.com format.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", "-p", osfilename, NULL);\r
+       }\r
+\r
+       /*\r
+        * Export the private key into ssh.com format, this time\r
+        * while encrypted. For RSA1 keys, this should give an\r
+        * error.\r
+        */\r
+       if (i == 0)\r
+           setup_passphrases(NULL);   /* error, hence no passphrase read */\r
+       else\r
+           setup_passphrases("sponge2", NULL);\r
+       test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,\r
+            filename, NULL);\r
+\r
+       if (i) {\r
+           /*\r
+            * List the fingerprint of the ssh.com-formatted key.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);\r
+           check_fp(tmpfilename1, fp, "%s ssh.com encrypted fp", keytypes[i]);\r
+\r
+           /*\r
+            * List the public half of the ssh.com-formatted key in\r
+            * OpenSSH format.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", "-L", scfilename, NULL);\r
+\r
+           /*\r
+            * List the public half of the ssh.com-formatted key in\r
+            * IETF/ssh.com format.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", "-p", scfilename, NULL);\r
+       }\r
+\r
+       if (i) {\r
+           /*\r
+            * Convert from OpenSSH into ssh.com.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", osfilename, "-o", tmpfilename1,\r
+                "-O", "private-sshcom", NULL);\r
+\r
+           /*\r
+            * Convert from ssh.com back into a PuTTY key,\r
+            * supplying the same comment as we had before we\r
+            * started to ensure the comparison works.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",\r
+                "-o", tmpfilename2, NULL);\r
+\r
+           /*\r
+            * See if the PuTTY key thus generated is the same as\r
+            * the original.\r
+            */\r
+           filecmp(filename, tmpfilename2,\r
+                   "p->o->s->p encrypted %s", keytypes[i]);\r
+\r
+           /*\r
+            * Convert from ssh.com to OpenSSH.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", scfilename, "-o", tmpfilename1,\r
+                "-O", "private-openssh", NULL);\r
+\r
+           /*\r
+            * Convert from OpenSSH back into a PuTTY key,\r
+            * supplying the same comment as we had before we\r
+            * started to ensure the comparison works.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",\r
+                "-o", tmpfilename2, NULL);\r
+\r
+           /*\r
+            * See if the PuTTY key thus generated is the same as\r
+            * the original.\r
+            */\r
+           filecmp(filename, tmpfilename2,\r
+                   "p->s->o->p encrypted %s", keytypes[i]);\r
+\r
+           /*\r
+            * Finally, do a round-trip conversion between PuTTY\r
+            * and ssh.com without involving OpenSSH, to test that\r
+            * the key comment is preserved in that case.\r
+            */\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,\r
+                filename, NULL);\r
+           setup_passphrases("sponge2", NULL);\r
+           test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);\r
+           filecmp(filename, tmpfilename2,\r
+                   "p->s->p encrypted %s", keytypes[i]);\r
+       }\r
+\r
+       /*\r
+        * Load with the wrong passphrase.\r
+        */\r
+       setup_passphrases("sponge8", NULL);\r
+       test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL);\r
+\r
+       /*\r
+        * Load a totally bogus file.\r
+        */\r
+       setup_passphrases(NULL);\r
+       test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL);\r
+    }\r
+    printf("%d passes, %d fails\n", passes, fails);\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/CMDLINE.C b/putty/CMDLINE.C
new file mode 100644 (file)
index 0000000..aa376a0
--- /dev/null
@@ -0,0 +1,566 @@
+/*\r
+ * cmdline.c - command-line parsing shared between many of the\r
+ * PuTTY applications\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include "putty.h"\r
+\r
+/*\r
+ * Some command-line parameters need to be saved up until after\r
+ * we've loaded the saved session which will form the basis of our\r
+ * eventual running configuration. For this we use the macro\r
+ * SAVEABLE, which notices if the `need_save' parameter is set and\r
+ * saves the parameter and value on a list.\r
+ * \r
+ * We also assign priorities to saved parameters, just to slightly\r
+ * ameliorate silly ordering problems. For example, if you specify\r
+ * a saved session to load, it will be loaded _before_ all your\r
+ * local modifications such as -L are evaluated; and if you specify\r
+ * a protocol and a port, the protocol is set up first so that the\r
+ * port can override its choice of port number.\r
+ * \r
+ * (In fact -load is not saved at all, since in at least Plink the\r
+ * processing of further command-line options depends on whether or\r
+ * not the loaded session contained a hostname. So it must be\r
+ * executed immediately.)\r
+ */\r
+\r
+#define NPRIORITIES 2\r
+\r
+struct cmdline_saved_param {\r
+    char *p, *value;\r
+};\r
+struct cmdline_saved_param_set {\r
+    struct cmdline_saved_param *params;\r
+    int nsaved, savesize;\r
+};\r
+\r
+/*\r
+ * C guarantees this structure will be initialised to all zero at\r
+ * program start, which is exactly what we want.\r
+ */\r
+static struct cmdline_saved_param_set saves[NPRIORITIES];\r
+\r
+static void cmdline_save_param(char *p, char *value, int pri)\r
+{\r
+    if (saves[pri].nsaved >= saves[pri].savesize) {\r
+       saves[pri].savesize = saves[pri].nsaved + 32;\r
+       saves[pri].params = sresize(saves[pri].params, saves[pri].savesize,\r
+                                   struct cmdline_saved_param);\r
+    }\r
+    saves[pri].params[saves[pri].nsaved].p = p;\r
+    saves[pri].params[saves[pri].nsaved].value = value;\r
+    saves[pri].nsaved++;\r
+}\r
+\r
+static char *cmdline_password = NULL;\r
+\r
+void cmdline_cleanup(void)\r
+{\r
+    int pri;\r
+\r
+    if (cmdline_password) {\r
+       memset(cmdline_password, 0, strlen(cmdline_password));\r
+       sfree(cmdline_password);\r
+       cmdline_password = NULL;\r
+    }\r
+    \r
+    for (pri = 0; pri < NPRIORITIES; pri++) {\r
+       sfree(saves[pri].params);\r
+       saves[pri].params = NULL;\r
+       saves[pri].savesize = 0;\r
+       saves[pri].nsaved = 0;\r
+    }\r
+}\r
+\r
+#define SAVEABLE(pri) do { \\r
+    if (need_save) { cmdline_save_param(p, value, pri); return ret; } \\r
+} while (0)\r
+\r
+/*\r
+ * Similar interface to get_userpass_input(), except that here a -1\r
+ * return means that we aren't capable of processing the prompt and\r
+ * someone else should do it.\r
+ */\r
+int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) {\r
+\r
+    static int tried_once = 0;\r
+\r
+    /*\r
+     * We only handle prompts which don't echo (which we assume to be\r
+     * passwords), and (currently) we only cope with a password prompt\r
+     * that comes in a prompt-set on its own.\r
+     */\r
+    if (!cmdline_password || in || p->n_prompts != 1 || p->prompts[0]->echo) {\r
+       return -1;\r
+    }\r
+\r
+    /*\r
+     * If we've tried once, return utter failure (no more passwords left\r
+     * to try).\r
+     */\r
+    if (tried_once)\r
+       return 0;\r
+\r
+    strncpy(p->prompts[0]->result, cmdline_password,\r
+           p->prompts[0]->result_len);\r
+    p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0';\r
+    memset(cmdline_password, 0, strlen(cmdline_password));\r
+    sfree(cmdline_password);\r
+    cmdline_password = NULL;\r
+    tried_once = 1;\r
+    return 1;\r
+\r
+}\r
+\r
+/*\r
+ * Here we have a flags word which describes the capabilities of\r
+ * the particular tool on whose behalf we're running. We will\r
+ * refuse certain command-line options if a particular tool\r
+ * inherently can't do anything sensible. For example, the file\r
+ * transfer tools (psftp, pscp) can't do a great deal with protocol\r
+ * selections (ever tried running scp over telnet?) or with port\r
+ * forwarding (even if it wasn't a hideously bad idea, they don't\r
+ * have the select() infrastructure to make them work).\r
+ */\r
+int cmdline_tooltype = 0;\r
+\r
+static int cmdline_check_unavailable(int flag, char *p)\r
+{\r
+    if (cmdline_tooltype & flag) {\r
+       cmdline_error("option \"%s\" not available in this tool", p);\r
+       return 1;\r
+    }\r
+    return 0;\r
+}\r
+\r
+#define UNAVAILABLE_IN(flag) do { \\r
+    if (cmdline_check_unavailable(flag, p)) return ret; \\r
+} while (0)\r
+\r
+/*\r
+ * Process a standard command-line parameter. `p' is the parameter\r
+ * in question; `value' is the subsequent element of argv, which\r
+ * may or may not be required as an operand to the parameter.\r
+ * If `need_save' is 1, arguments which need to be saved as\r
+ * described at this top of this file are, for later execution;\r
+ * if 0, they are processed normally. (-1 is a special value used\r
+ * by pterm to count arguments for a preliminary pass through the\r
+ * argument list; it causes immediate return with an appropriate\r
+ * value with no action taken.)\r
+ * Return value is 2 if both arguments were used; 1 if only p was\r
+ * used; 0 if the parameter wasn't one we recognised; -2 if it\r
+ * should have been 2 but value was NULL.\r
+ */\r
+\r
+#define RETURN(x) do { \\r
+    if ((x) == 2 && !value) return -2; \\r
+    ret = x; \\r
+    if (need_save < 0) return x; \\r
+} while (0)\r
+\r
+int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)\r
+{\r
+    int ret = 0;\r
+\r
+    if (!strcmp(p, "-load")) {\r
+       RETURN(2);\r
+       /* This parameter must be processed immediately rather than being\r
+        * saved. */\r
+       do_defaults(value, cfg);\r
+       loaded_session = TRUE;\r
+       cmdline_session_name = dupstr(value);\r
+       return 2;\r
+    }\r
+    if (!strcmp(p, "-ssh")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_SSH;\r
+       default_port = cfg->port = 22;\r
+       return 1;\r
+    }\r
+    if (!strcmp(p, "-telnet")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_TELNET;\r
+       default_port = cfg->port = 23;\r
+       return 1;\r
+    }\r
+    if (!strcmp(p, "-rlogin")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_RLOGIN;\r
+       default_port = cfg->port = 513;\r
+       return 1;\r
+    }\r
+    if (!strcmp(p, "-raw")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_RAW;\r
+    }\r
+    if (!strcmp(p, "-serial")) {\r
+       RETURN(1);\r
+       /* Serial is not NONNETWORK in an odd sense of the word */\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_SERIAL;\r
+       /* The host parameter will already be loaded into cfg->host, so copy it across */\r
+       strncpy(cfg->serline, cfg->host, sizeof(cfg->serline) - 1);\r
+       cfg->serline[sizeof(cfg->serline) - 1] = '\0';\r
+    }\r
+    if (!strcmp(p, "-v")) {\r
+       RETURN(1);\r
+       flags |= FLAG_VERBOSE;\r
+    }\r
+    if (!strcmp(p, "-l")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       strncpy(cfg->username, value, sizeof(cfg->username));\r
+       cfg->username[sizeof(cfg->username) - 1] = '\0';\r
+    }\r
+    if (!strcmp(p, "-loghost")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       strncpy(cfg->loghost, value, sizeof(cfg->loghost));\r
+       cfg->loghost[sizeof(cfg->loghost) - 1] = '\0';\r
+    }\r
+    if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) {\r
+       char *fwd, *ptr, *q, *qq;\r
+       int dynamic, i=0;\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       dynamic = !strcmp(p, "-D");\r
+       fwd = value;\r
+       ptr = cfg->portfwd;\r
+       /* if existing forwards, find end of list */\r
+       while (*ptr) {\r
+           while (*ptr)\r
+               ptr++;\r
+           ptr++;\r
+       }\r
+       i = ptr - cfg->portfwd;\r
+       ptr[0] = p[1];  /* insert a 'L', 'R' or 'D' at the start */\r
+       ptr++;\r
+       if (1 + strlen(fwd) + 2 > sizeof(cfg->portfwd) - i) {\r
+           cmdline_error("out of space for port forwardings");\r
+           return ret;\r
+       }\r
+       strncpy(ptr, fwd, sizeof(cfg->portfwd) - i - 2);\r
+       if (!dynamic) {\r
+           /*\r
+            * We expect _at least_ two colons in this string. The\r
+            * possible formats are `sourceport:desthost:destport',\r
+            * or `sourceip:sourceport:desthost:destport' if you're\r
+            * specifying a particular loopback address. We need to\r
+            * replace the one between source and dest with a \t;\r
+            * this means we must find the second-to-last colon in\r
+            * the string.\r
+            */\r
+           q = qq = strchr(ptr, ':');\r
+           while (qq) {\r
+               char *qqq = strchr(qq+1, ':');\r
+               if (qqq)\r
+                   q = qq;\r
+               qq = qqq;\r
+           }\r
+           if (q) *q = '\t';          /* replace second-last colon with \t */\r
+       }\r
+       cfg->portfwd[sizeof(cfg->portfwd) - 1] = '\0';\r
+       cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0';\r
+       ptr[strlen(ptr)+1] = '\000';    /* append 2nd '\000' */\r
+    }\r
+    if ((!strcmp(p, "-nc"))) {\r
+       char *host, *portp;\r
+\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+\r
+       host = portp = value;\r
+       while (*portp && *portp != ':')\r
+           portp++;\r
+       if (*portp) {\r
+           unsigned len = portp - host;\r
+           if (len >= sizeof(cfg->ssh_nc_host))\r
+               len = sizeof(cfg->ssh_nc_host) - 1;\r
+           memcpy(cfg->ssh_nc_host, value, len);\r
+           cfg->ssh_nc_host[len] = '\0';\r
+           cfg->ssh_nc_port = atoi(portp+1);\r
+       } else {\r
+           cmdline_error("-nc expects argument of form 'host:port'");\r
+           return ret;\r
+       }\r
+    }\r
+    if (!strcmp(p, "-m")) {\r
+       char *filename, *command;\r
+       int cmdlen, cmdsize;\r
+       FILE *fp;\r
+       int c, d;\r
+\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+\r
+       filename = value;\r
+\r
+       cmdlen = cmdsize = 0;\r
+       command = NULL;\r
+       fp = fopen(filename, "r");\r
+       if (!fp) {\r
+           cmdline_error("unable to open command "\r
+                         "file \"%s\"", filename);\r
+           return ret;\r
+       }\r
+       do {\r
+           c = fgetc(fp);\r
+           d = c;\r
+           if (c == EOF)\r
+               d = 0;\r
+           if (cmdlen >= cmdsize) {\r
+               cmdsize = cmdlen + 512;\r
+               command = sresize(command, cmdsize, char);\r
+           }\r
+           command[cmdlen++] = d;\r
+       } while (c != EOF);\r
+       cfg->remote_cmd_ptr = command;\r
+       cfg->remote_cmd_ptr2 = NULL;\r
+       cfg->nopty = TRUE;      /* command => no terminal */\r
+       fclose(fp);\r
+    }\r
+    if (!strcmp(p, "-P")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);                   /* lower priority than -ssh,-telnet */\r
+       cfg->port = atoi(value);\r
+    }\r
+    if (!strcmp(p, "-pw")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);\r
+       /* We delay evaluating this until after the protocol is decided,\r
+        * so that we can warn if it's of no use with the selected protocol */\r
+       if (cfg->protocol != PROT_SSH)\r
+           cmdline_error("the -pw option can only be used with the "\r
+                         "SSH protocol");\r
+       else {\r
+           cmdline_password = dupstr(value);\r
+           /* Assuming that `value' is directly from argv, make a good faith\r
+            * attempt to trample it, to stop it showing up in `ps' output\r
+            * on Unix-like systems. Not guaranteed, of course. */\r
+           memset(value, 0, strlen(value));\r
+       }\r
+    }\r
+\r
+    if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") ||\r
+       !strcmp(p, "-pageant")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->tryagent = TRUE;\r
+    }\r
+    if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") ||\r
+       !strcmp(p, "-nopageant")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->tryagent = FALSE;\r
+    }\r
+\r
+    if (!strcmp(p, "-A")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->agentfwd = 1;\r
+    }\r
+    if (!strcmp(p, "-a")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->agentfwd = 0;\r
+    }\r
+\r
+    if (!strcmp(p, "-X")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->x11_forward = 1;\r
+    }\r
+    if (!strcmp(p, "-x")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->x11_forward = 0;\r
+    }\r
+\r
+    if (!strcmp(p, "-t")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);    /* lower priority than -m */\r
+       cfg->nopty = 0;\r
+    }\r
+    if (!strcmp(p, "-T")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);\r
+       cfg->nopty = 1;\r
+    }\r
+\r
+    if (!strcmp(p, "-N")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->ssh_no_shell = 1;\r
+    }\r
+\r
+    if (!strcmp(p, "-C")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->compression = 1;\r
+    }\r
+\r
+    if (!strcmp(p, "-1")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->sshprot = 0;              /* ssh protocol 1 only */\r
+    }\r
+    if (!strcmp(p, "-2")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->sshprot = 3;              /* ssh protocol 2 only */\r
+    }\r
+\r
+    if (!strcmp(p, "-i")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->keyfile = filename_from_str(value);\r
+    }\r
+\r
+    if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) {\r
+       RETURN(1);\r
+       SAVEABLE(1);\r
+       cfg->addressfamily = ADDRTYPE_IPV4;\r
+    }\r
+    if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) {\r
+       RETURN(1);\r
+       SAVEABLE(1);\r
+       cfg->addressfamily = ADDRTYPE_IPV6;\r
+    }\r
+    if (!strcmp(p, "-sercfg")) {\r
+       char* nextitem;\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);\r
+       if (cfg->protocol != PROT_SERIAL)\r
+           cmdline_error("the -sercfg option can only be used with the "\r
+                         "serial protocol");\r
+       /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */\r
+       nextitem = value;\r
+       while (nextitem[0] != '\0') {\r
+           int length, skip;\r
+           char *end = strchr(nextitem, ',');\r
+           if (!end) {\r
+               length = strlen(nextitem);\r
+               skip = 0;\r
+           } else {\r
+               length = end - nextitem;\r
+               nextitem[length] = '\0';\r
+               skip = 1;\r
+           }\r
+           if (length == 1) {\r
+               switch (*nextitem) {\r
+                 case '1':\r
+                   cfg->serstopbits = 2;\r
+                   break;\r
+                 case '2':\r
+                   cfg->serstopbits = 4;\r
+                   break;\r
+\r
+                 case '5':\r
+                   cfg->serdatabits = 5;\r
+                   break;\r
+                 case '6':\r
+                   cfg->serdatabits = 6;\r
+                   break;\r
+                 case '7':\r
+                   cfg->serdatabits = 7;\r
+                   break;\r
+                 case '8':\r
+                   cfg->serdatabits = 8;\r
+                   break;\r
+                 case '9':\r
+                   cfg->serdatabits = 9;\r
+                   break;\r
+\r
+                 case 'n':\r
+                   cfg->serparity = SER_PAR_NONE;\r
+                   break;\r
+                 case 'o':\r
+                   cfg->serparity = SER_PAR_ODD;\r
+                   break;\r
+                 case 'e':\r
+                   cfg->serparity = SER_PAR_EVEN;\r
+                   break;\r
+                 case 'm':\r
+                   cfg->serparity = SER_PAR_MARK;\r
+                   break;\r
+                 case 's':\r
+                   cfg->serparity = SER_PAR_SPACE;\r
+                   break;\r
+\r
+                 case 'N':\r
+                   cfg->serflow = SER_FLOW_NONE;\r
+                   break;\r
+                 case 'X':\r
+                   cfg->serflow = SER_FLOW_XONXOFF;\r
+                   break;\r
+                 case 'R':\r
+                   cfg->serflow = SER_FLOW_RTSCTS;\r
+                   break;\r
+                 case 'D':\r
+                   cfg->serflow = SER_FLOW_DSRDTR;\r
+                   break;\r
+\r
+                 default:\r
+                   cmdline_error("Unrecognised suboption \"-sercfg %c\"",\r
+                                 *nextitem);\r
+               }\r
+           } else if (length == 3 && !strncmp(nextitem,"1.5",3)) {\r
+               /* Messy special case */\r
+               cfg->serstopbits = 3;\r
+           } else {\r
+               int serspeed = atoi(nextitem);\r
+               if (serspeed != 0) {\r
+                   cfg->serspeed = serspeed;\r
+               } else {\r
+                   cmdline_error("Unrecognised suboption \"-sercfg %s\"",\r
+                                 nextitem);\r
+               }\r
+           }\r
+           nextitem += length + skip;\r
+       }\r
+    }\r
+    return ret;                               /* unrecognised */\r
+}\r
+\r
+void cmdline_run_saved(Config *cfg)\r
+{\r
+    int pri, i;\r
+    for (pri = 0; pri < NPRIORITIES; pri++)\r
+       for (i = 0; i < saves[pri].nsaved; i++)\r
+           cmdline_process_param(saves[pri].params[i].p,\r
+                                 saves[pri].params[i].value, 0, cfg);\r
+}\r
diff --git a/putty/CONFIG.C b/putty/CONFIG.C
new file mode 100644 (file)
index 0000000..38e47b6
--- /dev/null
@@ -0,0 +1,2388 @@
+/*\r
+ * config.c - the platform-independent parts of the PuTTY\r
+ * configuration box.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "storage.h"\r
+\r
+#define PRINTER_DISABLED_STRING "None (printing disabled)"\r
+\r
+#define HOST_BOX_TITLE "Host Name (or IP address)"\r
+#define PORT_BOX_TITLE "Port"\r
+\r
+static void config_host_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+\r
+    /*\r
+     * This function works just like the standard edit box handler,\r
+     * only it has to choose the control's label and text from two\r
+     * different places depending on the protocol.\r
+     */\r
+    if (event == EVENT_REFRESH) {\r
+       if (cfg->protocol == PROT_SERIAL) {\r
+           /*\r
+            * This label text is carefully chosen to contain an n,\r
+            * since that's the shortcut for the host name control.\r
+            */\r
+           dlg_label_change(ctrl, dlg, "Serial line");\r
+           dlg_editbox_set(ctrl, dlg, cfg->serline);\r
+       } else {\r
+           dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);\r
+           dlg_editbox_set(ctrl, dlg, cfg->host);\r
+       }\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       if (cfg->protocol == PROT_SERIAL)\r
+           dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline));\r
+       else\r
+           dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host));\r
+    }\r
+}\r
+\r
+static void config_port_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    char buf[80];\r
+\r
+    /*\r
+     * This function works similarly to the standard edit box handler,\r
+     * only it has to choose the control's label and text from two\r
+     * different places depending on the protocol.\r
+     */\r
+    if (event == EVENT_REFRESH) {\r
+       if (cfg->protocol == PROT_SERIAL) {\r
+           /*\r
+            * This label text is carefully chosen to contain a p,\r
+            * since that's the shortcut for the port control.\r
+            */\r
+           dlg_label_change(ctrl, dlg, "Speed");\r
+           sprintf(buf, "%d", cfg->serspeed);\r
+       } else {\r
+           dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);\r
+           if (cfg->port != 0)\r
+               sprintf(buf, "%d", cfg->port);\r
+           else\r
+               /* Display an (invalid) port of 0 as blank */\r
+               buf[0] = '\0';\r
+       }\r
+       dlg_editbox_set(ctrl, dlg, buf);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_editbox_get(ctrl, dlg, buf, lenof(buf));\r
+       if (cfg->protocol == PROT_SERIAL)\r
+           cfg->serspeed = atoi(buf);\r
+       else\r
+           cfg->port = atoi(buf);\r
+    }\r
+}\r
+\r
+struct hostport {\r
+    union control *host, *port;\r
+};\r
+\r
+/*\r
+ * We export this function so that platform-specific config\r
+ * routines can use it to conveniently identify the protocol radio\r
+ * buttons in order to add to them.\r
+ */\r
+void config_protocolbuttons_handler(union control *ctrl, void *dlg,\r
+                                   void *data, int event)\r
+{\r
+    int button;\r
+    Config *cfg = (Config *)data;\r
+    struct hostport *hp = (struct hostport *)ctrl->radio.context.p;\r
+\r
+    /*\r
+     * This function works just like the standard radio-button\r
+     * handler, except that it also has to change the setting of\r
+     * the port box, and refresh both host and port boxes when. We\r
+     * expect the context parameter to point at a hostport\r
+     * structure giving the `union control's for both.\r
+     */\r
+    if (event == EVENT_REFRESH) {\r
+       for (button = 0; button < ctrl->radio.nbuttons; button++)\r
+           if (cfg->protocol == ctrl->radio.buttondata[button].i)\r
+               break;\r
+       /* We expected that `break' to happen, in all circumstances. */\r
+       assert(button < ctrl->radio.nbuttons);\r
+       dlg_radiobutton_set(ctrl, dlg, button);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       int oldproto = cfg->protocol;\r
+       button = dlg_radiobutton_get(ctrl, dlg);\r
+       assert(button >= 0 && button < ctrl->radio.nbuttons);\r
+       cfg->protocol = ctrl->radio.buttondata[button].i;\r
+       if (oldproto != cfg->protocol) {\r
+           Backend *ob = backend_from_proto(oldproto);\r
+           Backend *nb = backend_from_proto(cfg->protocol);\r
+           assert(ob);\r
+           assert(nb);\r
+           /* Iff the user hasn't changed the port from the old protocol's\r
+            * default, update it with the new protocol's default.\r
+            * (This includes a "default" of 0, implying that there is no\r
+            * sensible default for that protocol; in this case it's\r
+            * displayed as a blank.)\r
+            * This helps with the common case of tabbing through the\r
+            * controls in order and setting a non-default port before\r
+            * getting to the protocol; we want that non-default port\r
+            * to be preserved. */\r
+           if (cfg->port == ob->default_port)\r
+               cfg->port = nb->default_port;\r
+       }\r
+       dlg_refresh(hp->host, dlg);\r
+       dlg_refresh(hp->port, dlg);\r
+    }\r
+}\r
+\r
+static void loggingbuttons_handler(union control *ctrl, void *dlg,\r
+                                  void *data, int event)\r
+{\r
+    int button;\r
+    Config *cfg = (Config *)data;\r
+    /* This function works just like the standard radio-button handler,\r
+     * but it has to fall back to "no logging" in situations where the\r
+     * configured logging type isn't applicable.\r
+     */\r
+    if (event == EVENT_REFRESH) {\r
+        for (button = 0; button < ctrl->radio.nbuttons; button++)\r
+            if (cfg->logtype == ctrl->radio.buttondata[button].i)\r
+               break;\r
+    \r
+    /* We fell off the end, so we lack the configured logging type */\r
+    if (button == ctrl->radio.nbuttons) {\r
+        button=0;\r
+        cfg->logtype=LGTYP_NONE;\r
+    }\r
+    dlg_radiobutton_set(ctrl, dlg, button);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+        button = dlg_radiobutton_get(ctrl, dlg);\r
+        assert(button >= 0 && button < ctrl->radio.nbuttons);\r
+        cfg->logtype = ctrl->radio.buttondata[button].i;\r
+    }\r
+}\r
+\r
+static void numeric_keypad_handler(union control *ctrl, void *dlg,\r
+                                  void *data, int event)\r
+{\r
+    int button;\r
+    Config *cfg = (Config *)data;\r
+    /*\r
+     * This function works much like the standard radio button\r
+     * handler, but it has to handle two fields in Config.\r
+     */\r
+    if (event == EVENT_REFRESH) {\r
+       if (cfg->nethack_keypad)\r
+           button = 2;\r
+       else if (cfg->app_keypad)\r
+           button = 1;\r
+       else\r
+           button = 0;\r
+       assert(button < ctrl->radio.nbuttons);\r
+       dlg_radiobutton_set(ctrl, dlg, button);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       button = dlg_radiobutton_get(ctrl, dlg);\r
+       assert(button >= 0 && button < ctrl->radio.nbuttons);\r
+       if (button == 2) {\r
+           cfg->app_keypad = FALSE;\r
+           cfg->nethack_keypad = TRUE;\r
+       } else {\r
+           cfg->app_keypad = (button != 0);\r
+           cfg->nethack_keypad = FALSE;\r
+       }\r
+    }\r
+}\r
+\r
+static void cipherlist_handler(union control *ctrl, void *dlg,\r
+                              void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    if (event == EVENT_REFRESH) {\r
+       int i;\r
+\r
+       static const struct { char *s; int c; } ciphers[] = {\r
+           { "3DES",                   CIPHER_3DES },\r
+           { "Blowfish",               CIPHER_BLOWFISH },\r
+           { "DES",                    CIPHER_DES },\r
+           { "AES (SSH-2 only)",       CIPHER_AES },\r
+           { "Arcfour (SSH-2 only)",   CIPHER_ARCFOUR },\r
+           { "-- warn below here --",  CIPHER_WARN }\r
+       };\r
+\r
+       /* Set up the "selected ciphers" box. */\r
+       /* (cipherlist assumed to contain all ciphers) */\r
+       dlg_update_start(ctrl, dlg);\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       for (i = 0; i < CIPHER_MAX; i++) {\r
+           int c = cfg->ssh_cipherlist[i];\r
+           int j;\r
+           char *cstr = NULL;\r
+           for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {\r
+               if (ciphers[j].c == c) {\r
+                   cstr = ciphers[j].s;\r
+                   break;\r
+               }\r
+           }\r
+           dlg_listbox_addwithid(ctrl, dlg, cstr, c);\r
+       }\r
+       dlg_update_done(ctrl, dlg);\r
+\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       int i;\r
+\r
+       /* Update array to match the list box. */\r
+       for (i=0; i < CIPHER_MAX; i++)\r
+           cfg->ssh_cipherlist[i] = dlg_listbox_getid(ctrl, dlg, i);\r
+\r
+    }\r
+}\r
+\r
+#ifndef NO_GSSAPI\r
+static void gsslist_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    if (event == EVENT_REFRESH) {\r
+       int i;\r
+\r
+       dlg_update_start(ctrl, dlg);\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       for (i = 0; i < ngsslibs; i++) {\r
+           int id = cfg->ssh_gsslist[i];\r
+           assert(id >= 0 && id < ngsslibs);\r
+           dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);\r
+       }\r
+       dlg_update_done(ctrl, dlg);\r
+\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       int i;\r
+\r
+       /* Update array to match the list box. */\r
+       for (i=0; i < ngsslibs; i++)\r
+           cfg->ssh_gsslist[i] = dlg_listbox_getid(ctrl, dlg, i);\r
+    }\r
+}\r
+#endif\r
+\r
+static void kexlist_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    if (event == EVENT_REFRESH) {\r
+       int i;\r
+\r
+       static const struct { char *s; int k; } kexes[] = {\r
+           { "Diffie-Hellman group 1",         KEX_DHGROUP1 },\r
+           { "Diffie-Hellman group 14",        KEX_DHGROUP14 },\r
+           { "Diffie-Hellman group exchange",  KEX_DHGEX },\r
+           { "RSA-based key exchange",         KEX_RSA },\r
+           { "-- warn below here --",          KEX_WARN }\r
+       };\r
+\r
+       /* Set up the "kex preference" box. */\r
+       /* (kexlist assumed to contain all algorithms) */\r
+       dlg_update_start(ctrl, dlg);\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       for (i = 0; i < KEX_MAX; i++) {\r
+           int k = cfg->ssh_kexlist[i];\r
+           int j;\r
+           char *kstr = NULL;\r
+           for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {\r
+               if (kexes[j].k == k) {\r
+                   kstr = kexes[j].s;\r
+                   break;\r
+               }\r
+           }\r
+           dlg_listbox_addwithid(ctrl, dlg, kstr, k);\r
+       }\r
+       dlg_update_done(ctrl, dlg);\r
+\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       int i;\r
+\r
+       /* Update array to match the list box. */\r
+       for (i=0; i < KEX_MAX; i++)\r
+           cfg->ssh_kexlist[i] = dlg_listbox_getid(ctrl, dlg, i);\r
+\r
+    }\r
+}\r
+\r
+static void printerbox_handler(union control *ctrl, void *dlg,\r
+                              void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    if (event == EVENT_REFRESH) {\r
+       int nprinters, i;\r
+       printer_enum *pe;\r
+\r
+       dlg_update_start(ctrl, dlg);\r
+       /*\r
+        * Some backends may wish to disable the drop-down list on\r
+        * this edit box. Be prepared for this.\r
+        */\r
+       if (ctrl->editbox.has_list) {\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);\r
+           pe = printer_start_enum(&nprinters);\r
+           for (i = 0; i < nprinters; i++)\r
+               dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));\r
+           printer_finish_enum(pe);\r
+       }\r
+       dlg_editbox_set(ctrl, dlg,\r
+                       (*cfg->printer ? cfg->printer :\r
+                        PRINTER_DISABLED_STRING));\r
+       dlg_update_done(ctrl, dlg);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_editbox_get(ctrl, dlg, cfg->printer, sizeof(cfg->printer));\r
+       if (!strcmp(cfg->printer, PRINTER_DISABLED_STRING))\r
+           *cfg->printer = '\0';\r
+    }\r
+}\r
+\r
+static void codepage_handler(union control *ctrl, void *dlg,\r
+                            void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    if (event == EVENT_REFRESH) {\r
+       int i;\r
+       const char *cp, *thiscp;\r
+       dlg_update_start(ctrl, dlg);\r
+       thiscp = cp_name(decode_codepage(cfg->line_codepage));\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)\r
+           dlg_listbox_add(ctrl, dlg, cp);\r
+       dlg_editbox_set(ctrl, dlg, thiscp);\r
+       strcpy(cfg->line_codepage, thiscp);\r
+       dlg_update_done(ctrl, dlg);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_editbox_get(ctrl, dlg, cfg->line_codepage,\r
+                       sizeof(cfg->line_codepage));\r
+       strcpy(cfg->line_codepage,\r
+              cp_name(decode_codepage(cfg->line_codepage)));\r
+    }\r
+}\r
+\r
+static void sshbug_handler(union control *ctrl, void *dlg,\r
+                          void *data, int event)\r
+{\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_update_start(ctrl, dlg);\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);\r
+       dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);\r
+       dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);\r
+       switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) {\r
+         case AUTO:      dlg_listbox_select(ctrl, dlg, 0); break;\r
+         case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;\r
+         case FORCE_ON:  dlg_listbox_select(ctrl, dlg, 2); break;\r
+       }\r
+       dlg_update_done(ctrl, dlg);\r
+    } else if (event == EVENT_SELCHANGE) {\r
+       int i = dlg_listbox_index(ctrl, dlg);\r
+       if (i < 0)\r
+           i = AUTO;\r
+       else\r
+           i = dlg_listbox_getid(ctrl, dlg, i);\r
+       *(int *)ATOFFSET(data, ctrl->listbox.context.i) = i;\r
+    }\r
+}\r
+\r
+#define SAVEDSESSION_LEN 2048\r
+\r
+struct sessionsaver_data {\r
+    union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;\r
+    union control *okbutton, *cancelbutton;\r
+    struct sesslist sesslist;\r
+    int midsession;\r
+};\r
+\r
+/* \r
+ * Helper function to load the session selected in the list box, if\r
+ * any, as this is done in more than one place below. Returns 0 for\r
+ * failure.\r
+ */\r
+static int load_selected_session(struct sessionsaver_data *ssd,\r
+                                char *savedsession,\r
+                                void *dlg, Config *cfg, int *maybe_launch)\r
+{\r
+    int i = dlg_listbox_index(ssd->listbox, dlg);\r
+    int isdef;\r
+    if (i < 0) {\r
+       dlg_beep(dlg);\r
+       return 0;\r
+    }\r
+    isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");\r
+    load_settings(ssd->sesslist.sessions[i], cfg);\r
+    if (!isdef) {\r
+       strncpy(savedsession, ssd->sesslist.sessions[i],\r
+               SAVEDSESSION_LEN);\r
+       savedsession[SAVEDSESSION_LEN-1] = '\0';\r
+       if (maybe_launch)\r
+           *maybe_launch = TRUE;\r
+    } else {\r
+       savedsession[0] = '\0';\r
+       if (maybe_launch)\r
+           *maybe_launch = FALSE;\r
+    }\r
+    dlg_refresh(NULL, dlg);\r
+    /* Restore the selection, which might have been clobbered by\r
+     * changing the value of the edit box. */\r
+    dlg_listbox_select(ssd->listbox, dlg, i);\r
+    return 1;\r
+}\r
+\r
+static void sessionsaver_handler(union control *ctrl, void *dlg,\r
+                                void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    struct sessionsaver_data *ssd =\r
+       (struct sessionsaver_data *)ctrl->generic.context.p;\r
+    char *savedsession;\r
+\r
+    /*\r
+     * The first time we're called in a new dialog, we must\r
+     * allocate space to store the current contents of the saved\r
+     * session edit box (since it must persist even when we switch\r
+     * panels, but is not part of the Config).\r
+     */\r
+    if (!ssd->editbox) {\r
+        savedsession = NULL;\r
+    } else if (!dlg_get_privdata(ssd->editbox, dlg)) {\r
+       savedsession = (char *)\r
+           dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN);\r
+       savedsession[0] = '\0';\r
+    } else {\r
+       savedsession = dlg_get_privdata(ssd->editbox, dlg);\r
+    }\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       if (ctrl == ssd->editbox) {\r
+           dlg_editbox_set(ctrl, dlg, savedsession);\r
+       } else if (ctrl == ssd->listbox) {\r
+           int i;\r
+           dlg_update_start(ctrl, dlg);\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           for (i = 0; i < ssd->sesslist.nsessions; i++)\r
+               dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);\r
+           dlg_update_done(ctrl, dlg);\r
+       }\r
+    } else if (event == EVENT_VALCHANGE) {\r
+        int top, bottom, halfway, i;\r
+       if (ctrl == ssd->editbox) {\r
+           dlg_editbox_get(ctrl, dlg, savedsession,\r
+                           SAVEDSESSION_LEN);\r
+           top = ssd->sesslist.nsessions;\r
+           bottom = -1;\r
+           while (top-bottom > 1) {\r
+               halfway = (top+bottom)/2;\r
+               i = strcmp(savedsession, ssd->sesslist.sessions[halfway]);\r
+               if (i <= 0 ) {\r
+                   top = halfway;\r
+               } else {\r
+                   bottom = halfway;\r
+               }\r
+           }\r
+           if (top == ssd->sesslist.nsessions) {\r
+               top -= 1;\r
+           }\r
+           dlg_listbox_select(ssd->listbox, dlg, top);\r
+       }\r
+    } else if (event == EVENT_ACTION) {\r
+       int mbl = FALSE;\r
+       if (!ssd->midsession &&\r
+           (ctrl == ssd->listbox ||\r
+            (ssd->loadbutton && ctrl == ssd->loadbutton))) {\r
+           /*\r
+            * The user has double-clicked a session, or hit Load.\r
+            * We must load the selected session, and then\r
+            * terminate the configuration dialog _if_ there was a\r
+            * double-click on the list box _and_ that session\r
+            * contains a hostname.\r
+            */\r
+           if (load_selected_session(ssd, savedsession, dlg, cfg, &mbl) &&\r
+               (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) {\r
+               dlg_end(dlg, 1);       /* it's all over, and succeeded */\r
+           }\r
+       } else if (ctrl == ssd->savebutton) {\r
+           int isdef = !strcmp(savedsession, "Default Settings");\r
+           if (!savedsession[0]) {\r
+               int i = dlg_listbox_index(ssd->listbox, dlg);\r
+               if (i < 0) {\r
+                   dlg_beep(dlg);\r
+                   return;\r
+               }\r
+               isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");\r
+               if (!isdef) {\r
+                   strncpy(savedsession, ssd->sesslist.sessions[i],\r
+                           SAVEDSESSION_LEN);\r
+                   savedsession[SAVEDSESSION_LEN-1] = '\0';\r
+               } else {\r
+                   savedsession[0] = '\0';\r
+               }\r
+           }\r
+            {\r
+                char *errmsg = save_settings(savedsession, cfg);\r
+                if (errmsg) {\r
+                    dlg_error_msg(dlg, errmsg);\r
+                    sfree(errmsg);\r
+                }\r
+            }\r
+           get_sesslist(&ssd->sesslist, FALSE);\r
+           get_sesslist(&ssd->sesslist, TRUE);\r
+           dlg_refresh(ssd->editbox, dlg);\r
+           dlg_refresh(ssd->listbox, dlg);\r
+       } else if (!ssd->midsession &&\r
+                  ssd->delbutton && ctrl == ssd->delbutton) {\r
+           int i = dlg_listbox_index(ssd->listbox, dlg);\r
+           if (i <= 0) {\r
+               dlg_beep(dlg);\r
+           } else {\r
+               del_settings(ssd->sesslist.sessions[i]);\r
+               get_sesslist(&ssd->sesslist, FALSE);\r
+               get_sesslist(&ssd->sesslist, TRUE);\r
+               dlg_refresh(ssd->listbox, dlg);\r
+           }\r
+       } else if (ctrl == ssd->okbutton) {\r
+            if (ssd->midsession) {\r
+                /* In a mid-session Change Settings, Apply is always OK. */\r
+               dlg_end(dlg, 1);\r
+                return;\r
+            }\r
+           /*\r
+            * Annoying special case. If the `Open' button is\r
+            * pressed while no host name is currently set, _and_\r
+            * the session list previously had the focus, _and_\r
+            * there was a session selected in that which had a\r
+            * valid host name in it, then load it and go.\r
+            */\r
+           if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&\r
+               !cfg_launchable(cfg)) {\r
+               Config cfg2;\r
+               int mbl = FALSE;\r
+               if (!load_selected_session(ssd, savedsession, dlg,\r
+                                          &cfg2, &mbl)) {\r
+                   dlg_beep(dlg);\r
+                   return;\r
+               }\r
+               /* If at this point we have a valid session, go! */\r
+               if (mbl && cfg_launchable(&cfg2)) {\r
+                   *cfg = cfg2;       /* structure copy */\r
+                   cfg->remote_cmd_ptr = NULL;\r
+                   dlg_end(dlg, 1);\r
+               } else\r
+                   dlg_beep(dlg);\r
+                return;\r
+           }\r
+\r
+           /*\r
+            * Otherwise, do the normal thing: if we have a valid\r
+            * session, get going.\r
+            */\r
+           if (cfg_launchable(cfg)) {\r
+               dlg_end(dlg, 1);\r
+           } else\r
+               dlg_beep(dlg);\r
+       } else if (ctrl == ssd->cancelbutton) {\r
+           dlg_end(dlg, 0);\r
+       }\r
+    }\r
+}\r
+\r
+struct charclass_data {\r
+    union control *listbox, *editbox, *button;\r
+};\r
+\r
+static void charclass_handler(union control *ctrl, void *dlg,\r
+                             void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    struct charclass_data *ccd =\r
+       (struct charclass_data *)ctrl->generic.context.p;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       if (ctrl == ccd->listbox) {\r
+           int i;\r
+           dlg_update_start(ctrl, dlg);\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           for (i = 0; i < 128; i++) {\r
+               char str[100];\r
+               sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,\r
+                       (i >= 0x21 && i != 0x7F) ? i : ' ', cfg->wordness[i]);\r
+               dlg_listbox_add(ctrl, dlg, str);\r
+           }\r
+           dlg_update_done(ctrl, dlg);\r
+       }\r
+    } else if (event == EVENT_ACTION) {\r
+       if (ctrl == ccd->button) {\r
+           char str[100];\r
+           int i, n;\r
+           dlg_editbox_get(ccd->editbox, dlg, str, sizeof(str));\r
+           n = atoi(str);\r
+           for (i = 0; i < 128; i++) {\r
+               if (dlg_listbox_issel(ccd->listbox, dlg, i))\r
+                   cfg->wordness[i] = n;\r
+           }\r
+           dlg_refresh(ccd->listbox, dlg);\r
+       }\r
+    }\r
+}\r
+\r
+struct colour_data {\r
+    union control *listbox, *redit, *gedit, *bedit, *button;\r
+};\r
+\r
+static const char *const colours[] = {\r
+    "Default Foreground", "Default Bold Foreground",\r
+    "Default Background", "Default Bold Background",\r
+    "Cursor Text", "Cursor Colour",\r
+    "ANSI Black", "ANSI Black Bold",\r
+    "ANSI Red", "ANSI Red Bold",\r
+    "ANSI Green", "ANSI Green Bold",\r
+    "ANSI Yellow", "ANSI Yellow Bold",\r
+    "ANSI Blue", "ANSI Blue Bold",\r
+    "ANSI Magenta", "ANSI Magenta Bold",\r
+    "ANSI Cyan", "ANSI Cyan Bold",\r
+    "ANSI White", "ANSI White Bold"\r
+};\r
+\r
+static void colour_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    struct colour_data *cd =\r
+       (struct colour_data *)ctrl->generic.context.p;\r
+    int update = FALSE, clear = FALSE, r, g, b;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       if (ctrl == cd->listbox) {\r
+           int i;\r
+           dlg_update_start(ctrl, dlg);\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           for (i = 0; i < lenof(colours); i++)\r
+               dlg_listbox_add(ctrl, dlg, colours[i]);\r
+           dlg_update_done(ctrl, dlg);\r
+           clear = TRUE;\r
+           update = TRUE;\r
+       }\r
+    } else if (event == EVENT_SELCHANGE) {\r
+       if (ctrl == cd->listbox) {\r
+           /* The user has selected a colour. Update the RGB text. */\r
+           int i = dlg_listbox_index(ctrl, dlg);\r
+           if (i < 0) {\r
+               clear = TRUE;\r
+           } else {\r
+               clear = FALSE;\r
+               r = cfg->colours[i][0];\r
+               g = cfg->colours[i][1];\r
+               b = cfg->colours[i][2];\r
+           }\r
+           update = TRUE;\r
+       }\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {\r
+           /* The user has changed the colour using the edit boxes. */\r
+           char buf[80];\r
+           int i, cval;\r
+\r
+           dlg_editbox_get(ctrl, dlg, buf, lenof(buf));\r
+           cval = atoi(buf);\r
+           if (cval > 255) cval = 255;\r
+           if (cval < 0)   cval = 0;\r
+\r
+           i = dlg_listbox_index(cd->listbox, dlg);\r
+           if (i >= 0) {\r
+               if (ctrl == cd->redit)\r
+                   cfg->colours[i][0] = cval;\r
+               else if (ctrl == cd->gedit)\r
+                   cfg->colours[i][1] = cval;\r
+               else if (ctrl == cd->bedit)\r
+                   cfg->colours[i][2] = cval;\r
+           }\r
+       }\r
+    } else if (event == EVENT_ACTION) {\r
+       if (ctrl == cd->button) {\r
+           int i = dlg_listbox_index(cd->listbox, dlg);\r
+           if (i < 0) {\r
+               dlg_beep(dlg);\r
+               return;\r
+           }\r
+           /*\r
+            * Start a colour selector, which will send us an\r
+            * EVENT_CALLBACK when it's finished and allow us to\r
+            * pick up the results.\r
+            */\r
+           dlg_coloursel_start(ctrl, dlg,\r
+                               cfg->colours[i][0],\r
+                               cfg->colours[i][1],\r
+                               cfg->colours[i][2]);\r
+       }\r
+    } else if (event == EVENT_CALLBACK) {\r
+       if (ctrl == cd->button) {\r
+           int i = dlg_listbox_index(cd->listbox, dlg);\r
+           /*\r
+            * Collect the results of the colour selector. Will\r
+            * return nonzero on success, or zero if the colour\r
+            * selector did nothing (user hit Cancel, for example).\r
+            */\r
+           if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {\r
+               cfg->colours[i][0] = r;\r
+               cfg->colours[i][1] = g;\r
+               cfg->colours[i][2] = b;\r
+               clear = FALSE;\r
+               update = TRUE;\r
+           }\r
+       }\r
+    }\r
+\r
+    if (update) {\r
+       if (clear) {\r
+           dlg_editbox_set(cd->redit, dlg, "");\r
+           dlg_editbox_set(cd->gedit, dlg, "");\r
+           dlg_editbox_set(cd->bedit, dlg, "");\r
+       } else {\r
+           char buf[40];\r
+           sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);\r
+           sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);\r
+           sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);\r
+       }\r
+    }\r
+}\r
+\r
+struct ttymodes_data {\r
+    union control *modelist, *valradio, *valbox;\r
+    union control *addbutton, *rembutton, *listbox;\r
+};\r
+\r
+static void ttymodes_handler(union control *ctrl, void *dlg,\r
+                            void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    struct ttymodes_data *td =\r
+       (struct ttymodes_data *)ctrl->generic.context.p;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       if (ctrl == td->listbox) {\r
+           char *p = cfg->ttymodes;\r
+           dlg_update_start(ctrl, dlg);\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           while (*p) {\r
+               int tabpos = strchr(p, '\t') - p;\r
+               char *disp = dupprintf("%.*s\t%s", tabpos, p,\r
+                                      (p[tabpos+1] == 'A') ? "(auto)" :\r
+                                      p+tabpos+2);\r
+               dlg_listbox_add(ctrl, dlg, disp);\r
+               p += strlen(p) + 1;\r
+               sfree(disp);\r
+           }\r
+           dlg_update_done(ctrl, dlg);\r
+       } else if (ctrl == td->modelist) {\r
+           int i;\r
+           dlg_update_start(ctrl, dlg);\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           for (i = 0; ttymodes[i]; i++)\r
+               dlg_listbox_add(ctrl, dlg, ttymodes[i]);\r
+           dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */\r
+           dlg_update_done(ctrl, dlg);\r
+       } else if (ctrl == td->valradio) {\r
+           dlg_radiobutton_set(ctrl, dlg, 0);\r
+       }\r
+    } else if (event == EVENT_ACTION) {\r
+       if (ctrl == td->addbutton) {\r
+           int ind = dlg_listbox_index(td->modelist, dlg);\r
+           if (ind >= 0) {\r
+               char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';\r
+               int slen, left;\r
+               char *p, str[lenof(cfg->ttymodes)];\r
+               /* Construct new entry */\r
+               memset(str, 0, lenof(str));\r
+               strncpy(str, ttymodes[ind], lenof(str)-3);\r
+               slen = strlen(str);\r
+               str[slen] = '\t';\r
+               str[slen+1] = type;\r
+               slen += 2;\r
+               if (type == 'V') {\r
+                   dlg_editbox_get(td->valbox, dlg, str+slen, lenof(str)-slen);\r
+               }\r
+               /* Find end of list, deleting any existing instance */\r
+               p = cfg->ttymodes;\r
+               left = lenof(cfg->ttymodes);\r
+               while (*p) {\r
+                   int t = strchr(p, '\t') - p;\r
+                   if (t == strlen(ttymodes[ind]) &&\r
+                       strncmp(p, ttymodes[ind], t) == 0) {\r
+                       memmove(p, p+strlen(p)+1, left - (strlen(p)+1));\r
+                       continue;\r
+                   }\r
+                   left -= strlen(p) + 1;\r
+                   p    += strlen(p) + 1;\r
+               }\r
+               /* Append new entry */\r
+               memset(p, 0, left);\r
+               strncpy(p, str, left - 2);\r
+               dlg_refresh(td->listbox, dlg);\r
+           } else\r
+               dlg_beep(dlg);\r
+       } else if (ctrl == td->rembutton) {\r
+           char *p = cfg->ttymodes;\r
+           int i = 0, len = lenof(cfg->ttymodes);\r
+           while (*p) {\r
+               int multisel = dlg_listbox_index(td->listbox, dlg) < 0;\r
+               if (dlg_listbox_issel(td->listbox, dlg, i)) {\r
+                   if (!multisel) {\r
+                       /* Populate controls with entry we're about to\r
+                        * delete, for ease of editing.\r
+                        * (If multiple entries were selected, don't\r
+                        * touch the controls.) */\r
+                       char *val = strchr(p, '\t');\r
+                       if (val) {\r
+                           int ind = 0;\r
+                           val++;\r
+                           while (ttymodes[ind]) {\r
+                               if (strlen(ttymodes[ind]) == val-p-1 &&\r
+                                   !strncmp(ttymodes[ind], p, val-p-1))\r
+                                   break;\r
+                               ind++;\r
+                           }\r
+                           dlg_listbox_select(td->modelist, dlg, ind);\r
+                           dlg_radiobutton_set(td->valradio, dlg,\r
+                                               (*val == 'V'));\r
+                           dlg_editbox_set(td->valbox, dlg, val+1);\r
+                       }\r
+                   }\r
+                   memmove(p, p+strlen(p)+1, len - (strlen(p)+1));\r
+                   i++;\r
+                   continue;\r
+               }\r
+               len -= strlen(p) + 1;\r
+               p   += strlen(p) + 1;\r
+               i++;\r
+           }\r
+           memset(p, 0, lenof(cfg->ttymodes) - len);\r
+           dlg_refresh(td->listbox, dlg);\r
+       }\r
+    }\r
+}\r
+\r
+struct environ_data {\r
+    union control *varbox, *valbox, *addbutton, *rembutton, *listbox;\r
+};\r
+\r
+static void environ_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    struct environ_data *ed =\r
+       (struct environ_data *)ctrl->generic.context.p;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       if (ctrl == ed->listbox) {\r
+           char *p = cfg->environmt;\r
+           dlg_update_start(ctrl, dlg);\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           while (*p) {\r
+               dlg_listbox_add(ctrl, dlg, p);\r
+               p += strlen(p) + 1;\r
+           }\r
+           dlg_update_done(ctrl, dlg);\r
+       }\r
+    } else if (event == EVENT_ACTION) {\r
+       if (ctrl == ed->addbutton) {\r
+           char str[sizeof(cfg->environmt)];\r
+           char *p;\r
+           dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1);\r
+           if (!*str) {\r
+               dlg_beep(dlg);\r
+               return;\r
+           }\r
+           p = str + strlen(str);\r
+           *p++ = '\t';\r
+           dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str));\r
+           if (!*p) {\r
+               dlg_beep(dlg);\r
+               return;\r
+           }\r
+           p = cfg->environmt;\r
+           while (*p) {\r
+               while (*p)\r
+                   p++;\r
+               p++;\r
+           }\r
+           if ((p - cfg->environmt) + strlen(str) + 2 <\r
+               sizeof(cfg->environmt)) {\r
+               strcpy(p, str);\r
+               p[strlen(str) + 1] = '\0';\r
+               dlg_listbox_add(ed->listbox, dlg, str);\r
+               dlg_editbox_set(ed->varbox, dlg, "");\r
+               dlg_editbox_set(ed->valbox, dlg, "");\r
+           } else {\r
+               dlg_error_msg(dlg, "Environment too big");\r
+           }\r
+       } else if (ctrl == ed->rembutton) {\r
+           int i = dlg_listbox_index(ed->listbox, dlg);\r
+           if (i < 0) {\r
+               dlg_beep(dlg);\r
+           } else {\r
+               char *p, *q, *str;\r
+\r
+               dlg_listbox_del(ed->listbox, dlg, i);\r
+               p = cfg->environmt;\r
+               while (i > 0) {\r
+                   if (!*p)\r
+                       goto disaster;\r
+                   while (*p)\r
+                       p++;\r
+                   p++;\r
+                   i--;\r
+               }\r
+               q = p;\r
+               if (!*p)\r
+                   goto disaster;\r
+               /* Populate controls with the entry we're about to delete\r
+                * for ease of editing */\r
+               str = p;\r
+               p = strchr(p, '\t');\r
+               if (!p)\r
+                   goto disaster;\r
+               *p = '\0';\r
+               dlg_editbox_set(ed->varbox, dlg, str);\r
+               p++;\r
+               str = p;\r
+               dlg_editbox_set(ed->valbox, dlg, str);\r
+               p = strchr(p, '\0');\r
+               if (!p)\r
+                   goto disaster;\r
+               p++;\r
+               while (*p) {\r
+                   while (*p)\r
+                       *q++ = *p++;\r
+                   *q++ = *p++;\r
+               }\r
+               *q = '\0';\r
+               disaster:;\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+struct portfwd_data {\r
+    union control *addbutton, *rembutton, *listbox;\r
+    union control *sourcebox, *destbox, *direction;\r
+#ifndef NO_IPV6\r
+    union control *addressfamily;\r
+#endif\r
+};\r
+\r
+static void portfwd_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    Config *cfg = (Config *)data;\r
+    struct portfwd_data *pfd =\r
+       (struct portfwd_data *)ctrl->generic.context.p;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       if (ctrl == pfd->listbox) {\r
+           char *p = cfg->portfwd;\r
+           dlg_update_start(ctrl, dlg);\r
+           dlg_listbox_clear(ctrl, dlg);\r
+           while (*p) {\r
+               dlg_listbox_add(ctrl, dlg, p);\r
+               p += strlen(p) + 1;\r
+           }\r
+           dlg_update_done(ctrl, dlg);\r
+       } else if (ctrl == pfd->direction) {\r
+           /*\r
+            * Default is Local.\r
+            */\r
+           dlg_radiobutton_set(ctrl, dlg, 0);\r
+#ifndef NO_IPV6\r
+       } else if (ctrl == pfd->addressfamily) {\r
+           dlg_radiobutton_set(ctrl, dlg, 0);\r
+#endif\r
+       }\r
+    } else if (event == EVENT_ACTION) {\r
+       if (ctrl == pfd->addbutton) {\r
+           char str[sizeof(cfg->portfwd)];\r
+           char *p;\r
+           int i, type;\r
+           int whichbutton;\r
+\r
+           i = 0;\r
+#ifndef NO_IPV6\r
+           whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);\r
+           if (whichbutton == 1)\r
+               str[i++] = '4';\r
+           else if (whichbutton == 2)\r
+               str[i++] = '6';\r
+#endif\r
+\r
+           whichbutton = dlg_radiobutton_get(pfd->direction, dlg);\r
+           if (whichbutton == 0)\r
+               type = 'L';\r
+           else if (whichbutton == 1)\r
+               type = 'R';\r
+           else\r
+               type = 'D';\r
+           str[i++] = type;\r
+\r
+           dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i);\r
+           if (!str[i]) {\r
+               dlg_error_msg(dlg, "You need to specify a source port number");\r
+               return;\r
+           }\r
+           p = str + strlen(str);\r
+           if (type != 'D') {\r
+               *p++ = '\t';\r
+               dlg_editbox_get(pfd->destbox, dlg, p,\r
+                               sizeof(str) - (p - str));\r
+               if (!*p || !strchr(p, ':')) {\r
+                   dlg_error_msg(dlg,\r
+                                 "You need to specify a destination address\n"\r
+                                 "in the form \"host.name:port\"");\r
+                   return;\r
+               }\r
+           } else\r
+               *p = '\0';\r
+           p = cfg->portfwd;\r
+           while (*p) {\r
+               if (strcmp(p,str) == 0) {\r
+                   dlg_error_msg(dlg, "Specified forwarding already exists");\r
+                   break;\r
+               }\r
+               while (*p)\r
+                   p++;\r
+               p++;\r
+           }\r
+           if (!*p) {\r
+               if ((p - cfg->portfwd) + strlen(str) + 2 <=\r
+                   sizeof(cfg->portfwd)) {\r
+                   strcpy(p, str);\r
+                   p[strlen(str) + 1] = '\0';\r
+                   dlg_listbox_add(pfd->listbox, dlg, str);\r
+                   dlg_editbox_set(pfd->sourcebox, dlg, "");\r
+                   dlg_editbox_set(pfd->destbox, dlg, "");\r
+               } else {\r
+                   dlg_error_msg(dlg, "Too many forwardings");\r
+               }\r
+           }\r
+       } else if (ctrl == pfd->rembutton) {\r
+           int i = dlg_listbox_index(pfd->listbox, dlg);\r
+           if (i < 0)\r
+               dlg_beep(dlg);\r
+           else {\r
+               char *p, *q, *src, *dst;\r
+               char dir;\r
+\r
+               dlg_listbox_del(pfd->listbox, dlg, i);\r
+               p = cfg->portfwd;\r
+               while (i > 0) {\r
+                   if (!*p)\r
+                       goto disaster2;\r
+                   while (*p)\r
+                       p++;\r
+                   p++;\r
+                   i--;\r
+               }\r
+               q = p;\r
+               if (!*p)\r
+                   goto disaster2;\r
+               /* Populate the controls with the entry we're about to\r
+                * delete, for ease of editing. */\r
+               {\r
+                   static const char *const afs = "A46";\r
+                   char *afp = strchr(afs, *p);\r
+#ifndef NO_IPV6\r
+                   int idx = afp ? afp-afs : 0;\r
+#endif\r
+                   if (afp)\r
+                       p++;\r
+#ifndef NO_IPV6\r
+                   dlg_radiobutton_set(pfd->addressfamily, dlg, idx);\r
+#endif\r
+               }\r
+               {\r
+                   static const char *const dirs = "LRD";\r
+                   dir = *p;\r
+                   dlg_radiobutton_set(pfd->direction, dlg,\r
+                                       strchr(dirs, dir) - dirs);\r
+               }\r
+               p++;\r
+               if (dir != 'D') {\r
+                   src = p;\r
+                   p = strchr(p, '\t');\r
+                   if (!p)\r
+                       goto disaster2;\r
+                   *p = '\0';\r
+                   p++;\r
+                   dst = p;\r
+               } else {\r
+                   src = p;\r
+                   dst = "";\r
+               }\r
+               p = strchr(p, '\0');\r
+               if (!p)\r
+                   goto disaster2;\r
+               dlg_editbox_set(pfd->sourcebox, dlg, src);\r
+               dlg_editbox_set(pfd->destbox, dlg, dst);\r
+               p++;\r
+               while (*p) {\r
+                   while (*p)\r
+                       *q++ = *p++;\r
+                   *q++ = *p++;\r
+               }\r
+               *q = '\0';\r
+               disaster2:;\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+void setup_config_box(struct controlbox *b, int midsession,\r
+                     int protocol, int protcfginfo)\r
+{\r
+    struct controlset *s;\r
+    struct sessionsaver_data *ssd;\r
+    struct charclass_data *ccd;\r
+    struct colour_data *cd;\r
+    struct ttymodes_data *td;\r
+    struct environ_data *ed;\r
+    struct portfwd_data *pfd;\r
+    union control *c;\r
+    char *str;\r
+\r
+    ssd = (struct sessionsaver_data *)\r
+       ctrl_alloc(b, sizeof(struct sessionsaver_data));\r
+    memset(ssd, 0, sizeof(*ssd));\r
+    ssd->midsession = midsession;\r
+\r
+    /*\r
+     * The standard panel that appears at the bottom of all panels:\r
+     * Open, Cancel, Apply etc.\r
+     */\r
+    s = ctrl_getset(b, "", "", "");\r
+    ctrl_columns(s, 5, 20, 20, 20, 20, 20);\r
+    ssd->okbutton = ctrl_pushbutton(s,\r
+                                   (midsession ? "Apply" : "Open"),\r
+                                   (char)(midsession ? 'a' : 'o'),\r
+                                   HELPCTX(no_help),\r
+                                   sessionsaver_handler, P(ssd));\r
+    ssd->okbutton->button.isdefault = TRUE;\r
+    ssd->okbutton->generic.column = 3;\r
+    ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),\r
+                                       sessionsaver_handler, P(ssd));\r
+    ssd->cancelbutton->button.iscancel = TRUE;\r
+    ssd->cancelbutton->generic.column = 4;\r
+    /* We carefully don't close the 5-column part, so that platform-\r
+     * specific add-ons can put extra buttons alongside Open and Cancel. */\r
+\r
+    /*\r
+     * The Session panel.\r
+     */\r
+    str = dupprintf("Basic options for your %s session", appname);\r
+    ctrl_settitle(b, "Session", str);\r
+    sfree(str);\r
+\r
+    if (!midsession) {\r
+       struct hostport *hp = (struct hostport *)\r
+           ctrl_alloc(b, sizeof(struct hostport));\r
+\r
+       s = ctrl_getset(b, "Session", "hostport",\r
+                       "Specify the destination you want to connect to");\r
+       ctrl_columns(s, 2, 75, 25);\r
+       c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,\r
+                        HELPCTX(session_hostname),\r
+                        config_host_handler, I(0), I(0));\r
+       c->generic.column = 0;\r
+       hp->host = c;\r
+       c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,\r
+                        HELPCTX(session_hostname),\r
+                        config_port_handler, I(0), I(0));\r
+       c->generic.column = 1;\r
+       hp->port = c;\r
+       ctrl_columns(s, 1, 100);\r
+\r
+       if (!backend_from_proto(PROT_SSH)) {\r
+           ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,\r
+                             HELPCTX(session_hostname),\r
+                             config_protocolbuttons_handler, P(hp),\r
+                             "Raw", 'w', I(PROT_RAW),\r
+                             "Telnet", 't', I(PROT_TELNET),\r
+                             "Rlogin", 'i', I(PROT_RLOGIN),\r
+                             NULL);\r
+       } else {\r
+           ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,\r
+                             HELPCTX(session_hostname),\r
+                             config_protocolbuttons_handler, P(hp),\r
+                             "Raw", 'w', I(PROT_RAW),\r
+                             "Telnet", 't', I(PROT_TELNET),\r
+                             "Rlogin", 'i', I(PROT_RLOGIN),\r
+                             "SSH", 's', I(PROT_SSH),\r
+                             NULL);\r
+       }\r
+    }\r
+\r
+    /*\r
+     * The Load/Save panel is available even in mid-session.\r
+     */\r
+    s = ctrl_getset(b, "Session", "savedsessions",\r
+                   midsession ? "Save the current session settings" :\r
+                   "Load, save or delete a stored session");\r
+    ctrl_columns(s, 2, 75, 25);\r
+    get_sesslist(&ssd->sesslist, TRUE);\r
+    ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,\r
+                               HELPCTX(session_saved),\r
+                               sessionsaver_handler, P(ssd), P(NULL));\r
+    ssd->editbox->generic.column = 0;\r
+    /* Reset columns so that the buttons are alongside the list, rather\r
+     * than alongside that edit box. */\r
+    ctrl_columns(s, 1, 100);\r
+    ctrl_columns(s, 2, 75, 25);\r
+    ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
+                               HELPCTX(session_saved),\r
+                               sessionsaver_handler, P(ssd));\r
+    ssd->listbox->generic.column = 0;\r
+    ssd->listbox->listbox.height = 7;\r
+    if (!midsession) {\r
+       ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',\r
+                                         HELPCTX(session_saved),\r
+                                         sessionsaver_handler, P(ssd));\r
+       ssd->loadbutton->generic.column = 1;\r
+    } else {\r
+       /* We can't offer the Load button mid-session, as it would allow the\r
+        * user to load and subsequently save settings they can't see. (And\r
+        * also change otherwise immutable settings underfoot; that probably\r
+        * shouldn't be a problem, but.) */\r
+       ssd->loadbutton = NULL;\r
+    }\r
+    /* "Save" button is permitted mid-session. */\r
+    ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',\r
+                                     HELPCTX(session_saved),\r
+                                     sessionsaver_handler, P(ssd));\r
+    ssd->savebutton->generic.column = 1;\r
+    if (!midsession) {\r
+       ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',\r
+                                        HELPCTX(session_saved),\r
+                                        sessionsaver_handler, P(ssd));\r
+       ssd->delbutton->generic.column = 1;\r
+    } else {\r
+       /* Disable the Delete button mid-session too, for UI consistency. */\r
+       ssd->delbutton = NULL;\r
+    }\r
+    ctrl_columns(s, 1, 100);\r
+\r
+    s = ctrl_getset(b, "Session", "otheropts", NULL);\r
+    c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,\r
+                         HELPCTX(session_coe),\r
+                         dlg_stdradiobutton_handler,\r
+                         I(offsetof(Config, close_on_exit)),\r
+                         "Always", I(FORCE_ON),\r
+                         "Never", I(FORCE_OFF),\r
+                         "Only on clean exit", I(AUTO), NULL);\r
+\r
+    /*\r
+     * The Session/Logging panel.\r
+     */\r
+    ctrl_settitle(b, "Session/Logging", "Options controlling session logging");\r
+\r
+    s = ctrl_getset(b, "Session/Logging", "main", NULL);\r
+    /*\r
+     * The logging buttons change depending on whether SSH packet\r
+     * logging can sensibly be available.\r
+     */\r
+    {\r
+       char *sshlogname, *sshrawlogname;\r
+       if ((midsession && protocol == PROT_SSH) ||\r
+           (!midsession && backend_from_proto(PROT_SSH))) {\r
+           sshlogname = "SSH packets";\r
+           sshrawlogname = "SSH packets and raw data";\r
+        } else {\r
+           sshlogname = NULL;         /* this will disable both buttons */\r
+           sshrawlogname = NULL;      /* this will just placate optimisers */\r
+        }\r
+       ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,\r
+                         HELPCTX(logging_main),\r
+                         loggingbuttons_handler,\r
+                         I(offsetof(Config, logtype)),\r
+                         "None", 't', I(LGTYP_NONE),\r
+                         "Printable output", 'p', I(LGTYP_ASCII),\r
+                         "All session output", 'l', I(LGTYP_DEBUG),\r
+                         sshlogname, 's', I(LGTYP_PACKETS),\r
+                         sshrawlogname, 'r', I(LGTYP_SSHRAW),\r
+                         NULL);\r
+    }\r
+    ctrl_filesel(s, "Log file name:", 'f',\r
+                NULL, TRUE, "Select session log file name",\r
+                HELPCTX(logging_filename),\r
+                dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));\r
+    ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"\r
+             " &T for time, and &H for host name)",\r
+             HELPCTX(logging_filename));\r
+    ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,\r
+                     HELPCTX(logging_exists),\r
+                     dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),\r
+                     "Always overwrite it", I(LGXF_OVR),\r
+                     "Always append to the end of it", I(LGXF_APN),\r
+                     "Ask the user every time", I(LGXF_ASK), NULL);\r
+    ctrl_checkbox(s, "Flush log file frequently", 'u',\r
+                HELPCTX(logging_flush),\r
+                dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));\r
+\r
+    if ((midsession && protocol == PROT_SSH) ||\r
+       (!midsession && backend_from_proto(PROT_SSH))) {\r
+       s = ctrl_getset(b, "Session/Logging", "ssh",\r
+                       "Options specific to SSH packet logging");\r
+       ctrl_checkbox(s, "Omit known password fields", 'k',\r
+                     HELPCTX(logging_ssh_omit_password),\r
+                     dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass)));\r
+       ctrl_checkbox(s, "Omit session data", 'd',\r
+                     HELPCTX(logging_ssh_omit_data),\r
+                     dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata)));\r
+    }\r
+\r
+    /*\r
+     * The Terminal panel.\r
+     */\r
+    ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");\r
+\r
+    s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");\r
+    ctrl_checkbox(s, "Auto wrap mode initially on", 'w',\r
+                 HELPCTX(terminal_autowrap),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));\r
+    ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',\r
+                 HELPCTX(terminal_decom),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));\r
+    ctrl_checkbox(s, "Implicit CR in every LF", 'r',\r
+                 HELPCTX(terminal_lfhascr),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));\r
+    ctrl_checkbox(s, "Implicit LF in every CR", 'f',\r
+                 HELPCTX(terminal_crhaslf),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf)));\r
+    ctrl_checkbox(s, "Use background colour to erase screen", 'e',\r
+                 HELPCTX(terminal_bce),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,bce)));\r
+    ctrl_checkbox(s, "Enable blinking text", 'n',\r
+                 HELPCTX(terminal_blink),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));\r
+    ctrl_editbox(s, "Answerback to ^E:", 's', 100,\r
+                HELPCTX(terminal_answerback),\r
+                dlg_stdeditbox_handler, I(offsetof(Config,answerback)),\r
+                I(sizeof(((Config *)0)->answerback)));\r
+\r
+    s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");\r
+    ctrl_radiobuttons(s, "Local echo:", 'l', 3,\r
+                     HELPCTX(terminal_localecho),\r
+                     dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),\r
+                     "Auto", I(AUTO),\r
+                     "Force on", I(FORCE_ON),\r
+                     "Force off", I(FORCE_OFF), NULL);\r
+    ctrl_radiobuttons(s, "Local line editing:", 't', 3,\r
+                     HELPCTX(terminal_localedit),\r
+                     dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),\r
+                     "Auto", I(AUTO),\r
+                     "Force on", I(FORCE_ON),\r
+                     "Force off", I(FORCE_OFF), NULL);\r
+\r
+    s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");\r
+    ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,\r
+                 HELPCTX(terminal_printing),\r
+                 printerbox_handler, P(NULL), P(NULL));\r
+\r
+    /*\r
+     * The Terminal/Keyboard panel.\r
+     */\r
+    ctrl_settitle(b, "Terminal/Keyboard",\r
+                 "Options controlling the effects of keys");\r
+\r
+    s = ctrl_getset(b, "Terminal/Keyboard", "mappings",\r
+                   "Change the sequences sent by:");\r
+    ctrl_radiobuttons(s, "The Backspace key", 'b', 2,\r
+                     HELPCTX(keyboard_backspace),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, bksp_is_delete)),\r
+                     "Control-H", I(0), "Control-? (127)", I(1), NULL);\r
+    ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,\r
+                     HELPCTX(keyboard_homeend),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, rxvt_homeend)),\r
+                     "Standard", I(0), "rxvt", I(1), NULL);\r
+    ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,\r
+                     HELPCTX(keyboard_funkeys),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, funky_type)),\r
+                     "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),\r
+                     "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);\r
+\r
+    s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",\r
+                   "Application keypad settings:");\r
+    ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,\r
+                     HELPCTX(keyboard_appcursor),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, app_cursor)),\r
+                     "Normal", I(0), "Application", I(1), NULL);\r
+    ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,\r
+                     HELPCTX(keyboard_appkeypad),\r
+                     numeric_keypad_handler, P(NULL),\r
+                     "Normal", I(0), "Application", I(1), "NetHack", I(2),\r
+                     NULL);\r
+\r
+    /*\r
+     * The Terminal/Bell panel.\r
+     */\r
+    ctrl_settitle(b, "Terminal/Bell",\r
+                 "Options controlling the terminal bell");\r
+\r
+    s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");\r
+    ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,\r
+                     HELPCTX(bell_style),\r
+                     dlg_stdradiobutton_handler, I(offsetof(Config, beep)),\r
+                     "None (bell disabled)", I(BELL_DISABLED),\r
+                     "Make default system alert sound", I(BELL_DEFAULT),\r
+                     "Visual bell (flash window)", I(BELL_VISUAL), NULL);\r
+\r
+    s = ctrl_getset(b, "Terminal/Bell", "overload",\r
+                   "Control the bell overload behaviour");\r
+    ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',\r
+                 HELPCTX(bell_overload),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));\r
+    ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,\r
+                HELPCTX(bell_overload),\r
+                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));\r
+    ctrl_editbox(s, "... in this many seconds", 't', 20,\r
+                HELPCTX(bell_overload),\r
+                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),\r
+                I(-TICKSPERSEC));\r
+    ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",\r
+             HELPCTX(bell_overload));\r
+    ctrl_editbox(s, "Seconds of silence required", 's', 20,\r
+                HELPCTX(bell_overload),\r
+                dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),\r
+                I(-TICKSPERSEC));\r
+\r
+    /*\r
+     * The Terminal/Features panel.\r
+     */\r
+    ctrl_settitle(b, "Terminal/Features",\r
+                 "Enabling and disabling advanced terminal features");\r
+\r
+    s = ctrl_getset(b, "Terminal/Features", "main", NULL);\r
+    ctrl_checkbox(s, "Disable application cursor keys mode", 'u',\r
+                 HELPCTX(features_application),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));\r
+    ctrl_checkbox(s, "Disable application keypad mode", 'k',\r
+                 HELPCTX(features_application),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));\r
+    ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',\r
+                 HELPCTX(features_mouse),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));\r
+    ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',\r
+                 HELPCTX(features_resize),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,no_remote_resize)));\r
+    ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',\r
+                 HELPCTX(features_altscreen),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));\r
+    ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',\r
+                 HELPCTX(features_retitle),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,no_remote_wintitle)));\r
+    ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,\r
+                     HELPCTX(features_qtitle),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config,remote_qtitle_action)),\r
+                     "None", I(TITLE_NONE),\r
+                     "Empty string", I(TITLE_EMPTY),\r
+                     "Window title", I(TITLE_REAL), NULL);\r
+    ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',\r
+                 HELPCTX(features_dbackspace),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));\r
+    ctrl_checkbox(s, "Disable remote-controlled character set configuration",\r
+                 'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,no_remote_charset)));\r
+    ctrl_checkbox(s, "Disable Arabic text shaping",\r
+                 'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config, arabicshaping)));\r
+    ctrl_checkbox(s, "Disable bidirectional text display",\r
+                 'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config, bidi)));\r
+\r
+    /*\r
+     * The Window panel.\r
+     */\r
+    str = dupprintf("Options controlling %s's window", appname);\r
+    ctrl_settitle(b, "Window", str);\r
+    sfree(str);\r
+\r
+    s = ctrl_getset(b, "Window", "size", "Set the size of the window");\r
+    ctrl_columns(s, 2, 50, 50);\r
+    c = ctrl_editbox(s, "Columns", 'm', 100,\r
+                    HELPCTX(window_size),\r
+                    dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));\r
+    c->generic.column = 0;\r
+    c = ctrl_editbox(s, "Rows", 'r', 100,\r
+                    HELPCTX(window_size),\r
+                    dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));\r
+    c->generic.column = 1;\r
+    ctrl_columns(s, 1, 100);\r
+\r
+    s = ctrl_getset(b, "Window", "scrollback",\r
+                   "Control the scrollback in the window");\r
+    ctrl_editbox(s, "Lines of scrollback", 's', 50,\r
+                HELPCTX(window_scrollback),\r
+                dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));\r
+    ctrl_checkbox(s, "Display scrollbar", 'd',\r
+                 HELPCTX(window_scrollback),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));\r
+    ctrl_checkbox(s, "Reset scrollback on keypress", 'k',\r
+                 HELPCTX(window_scrollback),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));\r
+    ctrl_checkbox(s, "Reset scrollback on display activity", 'p',\r
+                 HELPCTX(window_scrollback),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));\r
+    ctrl_checkbox(s, "Push erased text into scrollback", 'e',\r
+                 HELPCTX(window_erased),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,erase_to_scrollback)));\r
+\r
+    /*\r
+     * The Window/Appearance panel.\r
+     */\r
+    str = dupprintf("Configure the appearance of %s's window", appname);\r
+    ctrl_settitle(b, "Window/Appearance", str);\r
+    sfree(str);\r
+\r
+    s = ctrl_getset(b, "Window/Appearance", "cursor",\r
+                   "Adjust the use of the cursor");\r
+    ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,\r
+                     HELPCTX(appearance_cursor),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, cursor_type)),\r
+                     "Block", 'l', I(0),\r
+                     "Underline", 'u', I(1),\r
+                     "Vertical line", 'v', I(2), NULL);\r
+    ctrl_checkbox(s, "Cursor blinks", 'b',\r
+                 HELPCTX(appearance_cursor),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));\r
+\r
+    s = ctrl_getset(b, "Window/Appearance", "font",\r
+                   "Font settings");\r
+    ctrl_fontsel(s, "Font used in the terminal window", 'n',\r
+                HELPCTX(appearance_font),\r
+                dlg_stdfontsel_handler, I(offsetof(Config, font)));\r
+\r
+    s = ctrl_getset(b, "Window/Appearance", "mouse",\r
+                   "Adjust the use of the mouse pointer");\r
+    ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',\r
+                 HELPCTX(appearance_hidemouse),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));\r
+\r
+    s = ctrl_getset(b, "Window/Appearance", "border",\r
+                   "Adjust the window border");\r
+    ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,\r
+                HELPCTX(appearance_border),\r
+                dlg_stdeditbox_handler,\r
+                I(offsetof(Config,window_border)), I(-1));\r
+\r
+    /*\r
+     * The Window/Behaviour panel.\r
+     */\r
+    str = dupprintf("Configure the behaviour of %s's window", appname);\r
+    ctrl_settitle(b, "Window/Behaviour", str);\r
+    sfree(str);\r
+\r
+    s = ctrl_getset(b, "Window/Behaviour", "title",\r
+                   "Adjust the behaviour of the window title");\r
+    ctrl_editbox(s, "Window title:", 't', 100,\r
+                HELPCTX(appearance_title),\r
+                dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),\r
+                I(sizeof(((Config *)0)->wintitle)));\r
+    ctrl_checkbox(s, "Separate window and icon titles", 'i',\r
+                 HELPCTX(appearance_title),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));\r
+\r
+    s = ctrl_getset(b, "Window/Behaviour", "main", NULL);\r
+    ctrl_checkbox(s, "Warn before closing window", 'w',\r
+                 HELPCTX(behaviour_closewarn),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));\r
+\r
+    /*\r
+     * The Window/Translation panel.\r
+     */\r
+    ctrl_settitle(b, "Window/Translation",\r
+                 "Options controlling character set translation");\r
+\r
+    s = ctrl_getset(b, "Window/Translation", "trans",\r
+                   "Character set translation");\r
+    ctrl_combobox(s, "Remote character set:",\r
+                 'r', 100, HELPCTX(translation_codepage),\r
+                 codepage_handler, P(NULL), P(NULL));\r
+\r
+    s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);\r
+    ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',\r
+                 HELPCTX(translation_cjk_ambig_wide),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide)));\r
+\r
+    str = dupprintf("Adjust how %s handles line drawing characters", appname);\r
+    s = ctrl_getset(b, "Window/Translation", "linedraw", str);\r
+    sfree(str);\r
+    ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,\r
+                     HELPCTX(translation_linedraw),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, vtmode)),\r
+                     "Use Unicode line drawing code points",'u',I(VT_UNICODE),\r
+                     "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),\r
+                     NULL);\r
+    ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',\r
+                 HELPCTX(selection_linedraw),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));\r
+\r
+    /*\r
+     * The Window/Selection panel.\r
+     */\r
+    ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");\r
+       \r
+    s = ctrl_getset(b, "Window/Selection", "mouse",\r
+                   "Control use of mouse");\r
+    ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',\r
+                 HELPCTX(selection_shiftdrag),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));\r
+    ctrl_radiobuttons(s,\r
+                     "Default selection mode (Alt+drag does the other one):",\r
+                     NO_SHORTCUT, 2,\r
+                     HELPCTX(selection_rect),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, rect_select)),\r
+                     "Normal", 'n', I(0),\r
+                     "Rectangular block", 'r', I(1), NULL);\r
+\r
+    s = ctrl_getset(b, "Window/Selection", "charclass",\r
+                   "Control the select-one-word-at-a-time mode");\r
+    ccd = (struct charclass_data *)\r
+       ctrl_alloc(b, sizeof(struct charclass_data));\r
+    ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',\r
+                               HELPCTX(selection_charclasses),\r
+                               charclass_handler, P(ccd));\r
+    ccd->listbox->listbox.multisel = 1;\r
+    ccd->listbox->listbox.ncols = 4;\r
+    ccd->listbox->listbox.percentages = snewn(4, int);\r
+    ccd->listbox->listbox.percentages[0] = 15;\r
+    ccd->listbox->listbox.percentages[1] = 25;\r
+    ccd->listbox->listbox.percentages[2] = 20;\r
+    ccd->listbox->listbox.percentages[3] = 40;\r
+    ctrl_columns(s, 2, 67, 33);\r
+    ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,\r
+                               HELPCTX(selection_charclasses),\r
+                               charclass_handler, P(ccd), P(NULL));\r
+    ccd->editbox->generic.column = 0;\r
+    ccd->button = ctrl_pushbutton(s, "Set", 's',\r
+                                 HELPCTX(selection_charclasses),\r
+                                 charclass_handler, P(ccd));\r
+    ccd->button->generic.column = 1;\r
+    ctrl_columns(s, 1, 100);\r
+\r
+    /*\r
+     * The Window/Colours panel.\r
+     */\r
+    ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");\r
+\r
+    s = ctrl_getset(b, "Window/Colours", "general",\r
+                   "General options for colour usage");\r
+    ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',\r
+                 HELPCTX(colours_ansi),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));\r
+    ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',\r
+                 HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,xterm_256_colour)));\r
+    ctrl_checkbox(s, "Bolded text is a different colour", 'b',\r
+                 HELPCTX(colours_bold),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));\r
+\r
+    str = dupprintf("Adjust the precise colours %s displays", appname);\r
+    s = ctrl_getset(b, "Window/Colours", "adjust", str);\r
+    sfree(str);\r
+    ctrl_text(s, "Select a colour from the list, and then click the"\r
+             " Modify button to change its appearance.",\r
+             HELPCTX(colours_config));\r
+    ctrl_columns(s, 2, 67, 33);\r
+    cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));\r
+    cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',\r
+                              HELPCTX(colours_config), colour_handler, P(cd));\r
+    cd->listbox->generic.column = 0;\r
+    cd->listbox->listbox.height = 7;\r
+    c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));\r
+    c->generic.column = 1;\r
+    cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),\r
+                            colour_handler, P(cd), P(NULL));\r
+    cd->redit->generic.column = 1;\r
+    cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),\r
+                            colour_handler, P(cd), P(NULL));\r
+    cd->gedit->generic.column = 1;\r
+    cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),\r
+                            colour_handler, P(cd), P(NULL));\r
+    cd->bedit->generic.column = 1;\r
+    cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),\r
+                                colour_handler, P(cd));\r
+    cd->button->generic.column = 1;\r
+    ctrl_columns(s, 1, 100);\r
+\r
+    /*\r
+     * The Connection panel. This doesn't show up if we're in a\r
+     * non-network utility such as pterm. We tell this by being\r
+     * passed a protocol < 0.\r
+     */\r
+    if (protocol >= 0) {\r
+       ctrl_settitle(b, "Connection", "Options controlling the connection");\r
+\r
+       s = ctrl_getset(b, "Connection", "keepalive",\r
+                       "Sending of null packets to keep session active");\r
+       ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,\r
+                    HELPCTX(connection_keepalive),\r
+                    dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),\r
+                    I(-1));\r
+\r
+       if (!midsession) {\r
+           s = ctrl_getset(b, "Connection", "tcp",\r
+                           "Low-level TCP connection options");\r
+           ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",\r
+                         'n', HELPCTX(connection_nodelay),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,tcp_nodelay)));\r
+           ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",\r
+                         'p', HELPCTX(connection_tcpkeepalive),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,tcp_keepalives)));\r
+#ifndef NO_IPV6\r
+           s = ctrl_getset(b, "Connection", "ipversion",\r
+                         "Internet protocol version");\r
+           ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,\r
+                         HELPCTX(connection_ipversion),\r
+                         dlg_stdradiobutton_handler,\r
+                         I(offsetof(Config, addressfamily)),\r
+                         "Auto", 'u', I(ADDRTYPE_UNSPEC),\r
+                         "IPv4", '4', I(ADDRTYPE_IPV4),\r
+                         "IPv6", '6', I(ADDRTYPE_IPV6),\r
+                         NULL);\r
+#endif\r
+\r
+           {\r
+               char *label = backend_from_proto(PROT_SSH) ?\r
+                   "Logical name of remote host (e.g. for SSH key lookup):" :\r
+                   "Logical name of remote host:";\r
+               s = ctrl_getset(b, "Connection", "identity",\r
+                               "Logical name of remote host");\r
+               ctrl_editbox(s, label, 'm', 100,\r
+                            HELPCTX(connection_loghost),\r
+                            dlg_stdeditbox_handler, I(offsetof(Config,loghost)),\r
+                            I(sizeof(((Config *)0)->loghost)));\r
+           }\r
+       }\r
+\r
+       /*\r
+        * A sub-panel Connection/Data, containing options that\r
+        * decide on data to send to the server.\r
+        */\r
+       if (!midsession) {\r
+           ctrl_settitle(b, "Connection/Data", "Data to send to the server");\r
+\r
+           s = ctrl_getset(b, "Connection/Data", "login",\r
+                           "Login details");\r
+           ctrl_editbox(s, "Auto-login username", 'u', 50,\r
+                        HELPCTX(connection_username),\r
+                        dlg_stdeditbox_handler, I(offsetof(Config,username)),\r
+                        I(sizeof(((Config *)0)->username)));\r
+           {\r
+               /* We assume the local username is sufficiently stable\r
+                * to include on the dialog box. */\r
+               char *user = get_username();\r
+               char *userlabel = dupprintf("Use system username (%s)",\r
+                                           user ? user : "");\r
+               sfree(user);\r
+               ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,\r
+                                 HELPCTX(connection_username_from_env),\r
+                                 dlg_stdradiobutton_handler,\r
+                                 I(offsetof(Config, username_from_env)),\r
+                                 "Prompt", I(FALSE),\r
+                                 userlabel, I(TRUE),\r
+                                 NULL);\r
+               sfree(userlabel);\r
+           }\r
+\r
+           s = ctrl_getset(b, "Connection/Data", "term",\r
+                           "Terminal details");\r
+           ctrl_editbox(s, "Terminal-type string", 't', 50,\r
+                        HELPCTX(connection_termtype),\r
+                        dlg_stdeditbox_handler, I(offsetof(Config,termtype)),\r
+                        I(sizeof(((Config *)0)->termtype)));\r
+           ctrl_editbox(s, "Terminal speeds", 's', 50,\r
+                        HELPCTX(connection_termspeed),\r
+                        dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),\r
+                        I(sizeof(((Config *)0)->termspeed)));\r
+\r
+           s = ctrl_getset(b, "Connection/Data", "env",\r
+                           "Environment variables");\r
+           ctrl_columns(s, 2, 80, 20);\r
+           ed = (struct environ_data *)\r
+               ctrl_alloc(b, sizeof(struct environ_data));\r
+           ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,\r
+                                     HELPCTX(telnet_environ),\r
+                                     environ_handler, P(ed), P(NULL));\r
+           ed->varbox->generic.column = 0;\r
+           ed->valbox = ctrl_editbox(s, "Value", 'l', 60,\r
+                                     HELPCTX(telnet_environ),\r
+                                     environ_handler, P(ed), P(NULL));\r
+           ed->valbox->generic.column = 0;\r
+           ed->addbutton = ctrl_pushbutton(s, "Add", 'd',\r
+                                           HELPCTX(telnet_environ),\r
+                                           environ_handler, P(ed));\r
+           ed->addbutton->generic.column = 1;\r
+           ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',\r
+                                           HELPCTX(telnet_environ),\r
+                                           environ_handler, P(ed));\r
+           ed->rembutton->generic.column = 1;\r
+           ctrl_columns(s, 1, 100);\r
+           ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
+                                      HELPCTX(telnet_environ),\r
+                                      environ_handler, P(ed));\r
+           ed->listbox->listbox.height = 3;\r
+           ed->listbox->listbox.ncols = 2;\r
+           ed->listbox->listbox.percentages = snewn(2, int);\r
+           ed->listbox->listbox.percentages[0] = 30;\r
+           ed->listbox->listbox.percentages[1] = 70;\r
+       }\r
+\r
+    }\r
+\r
+    if (!midsession) {\r
+       /*\r
+        * The Connection/Proxy panel.\r
+        */\r
+       ctrl_settitle(b, "Connection/Proxy",\r
+                     "Options controlling proxy usage");\r
+\r
+       s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);\r
+       ctrl_radiobuttons(s, "Proxy type:", 't', 3,\r
+                         HELPCTX(proxy_type),\r
+                         dlg_stdradiobutton_handler,\r
+                         I(offsetof(Config, proxy_type)),\r
+                         "None", I(PROXY_NONE),\r
+                         "SOCKS 4", I(PROXY_SOCKS4),\r
+                         "SOCKS 5", I(PROXY_SOCKS5),\r
+                         "HTTP", I(PROXY_HTTP),\r
+                         "Telnet", I(PROXY_TELNET),\r
+                         NULL);\r
+       ctrl_columns(s, 2, 80, 20);\r
+       c = ctrl_editbox(s, "Proxy hostname", 'y', 100,\r
+                        HELPCTX(proxy_main),\r
+                        dlg_stdeditbox_handler,\r
+                        I(offsetof(Config,proxy_host)),\r
+                        I(sizeof(((Config *)0)->proxy_host)));\r
+       c->generic.column = 0;\r
+       c = ctrl_editbox(s, "Port", 'p', 100,\r
+                        HELPCTX(proxy_main),\r
+                        dlg_stdeditbox_handler,\r
+                        I(offsetof(Config,proxy_port)),\r
+                        I(-1));\r
+       c->generic.column = 1;\r
+       ctrl_columns(s, 1, 100);\r
+       ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,\r
+                    HELPCTX(proxy_exclude),\r
+                    dlg_stdeditbox_handler,\r
+                    I(offsetof(Config,proxy_exclude_list)),\r
+                    I(sizeof(((Config *)0)->proxy_exclude_list)));\r
+       ctrl_checkbox(s, "Consider proxying local host connections", 'x',\r
+                     HELPCTX(proxy_exclude),\r
+                     dlg_stdcheckbox_handler,\r
+                     I(offsetof(Config,even_proxy_localhost)));\r
+       ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,\r
+                         HELPCTX(proxy_dns),\r
+                         dlg_stdradiobutton_handler,\r
+                         I(offsetof(Config, proxy_dns)),\r
+                         "No", I(FORCE_OFF),\r
+                         "Auto", I(AUTO),\r
+                         "Yes", I(FORCE_ON), NULL);\r
+       ctrl_editbox(s, "Username", 'u', 60,\r
+                    HELPCTX(proxy_auth),\r
+                    dlg_stdeditbox_handler,\r
+                    I(offsetof(Config,proxy_username)),\r
+                    I(sizeof(((Config *)0)->proxy_username)));\r
+       c = ctrl_editbox(s, "Password", 'w', 60,\r
+                        HELPCTX(proxy_auth),\r
+                        dlg_stdeditbox_handler,\r
+                        I(offsetof(Config,proxy_password)),\r
+                        I(sizeof(((Config *)0)->proxy_password)));\r
+       c->editbox.password = 1;\r
+       ctrl_editbox(s, "Telnet command", 'm', 100,\r
+                    HELPCTX(proxy_command),\r
+                    dlg_stdeditbox_handler,\r
+                    I(offsetof(Config,proxy_telnet_command)),\r
+                    I(sizeof(((Config *)0)->proxy_telnet_command)));\r
+    }\r
+\r
+    /*\r
+     * The Telnet panel exists in the base config box, and in a\r
+     * mid-session reconfig box _if_ we're using Telnet.\r
+     */\r
+    if (!midsession || protocol == PROT_TELNET) {\r
+       /*\r
+        * The Connection/Telnet panel.\r
+        */\r
+       ctrl_settitle(b, "Connection/Telnet",\r
+                     "Options controlling Telnet connections");\r
+\r
+       s = ctrl_getset(b, "Connection/Telnet", "protocol",\r
+                       "Telnet protocol adjustments");\r
+\r
+       if (!midsession) {\r
+           ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",\r
+                             NO_SHORTCUT, 2,\r
+                             HELPCTX(telnet_oldenviron),\r
+                             dlg_stdradiobutton_handler,\r
+                             I(offsetof(Config, rfc_environ)),\r
+                             "BSD (commonplace)", 'b', I(0),\r
+                             "RFC 1408 (unusual)", 'f', I(1), NULL);\r
+           ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,\r
+                             HELPCTX(telnet_passive),\r
+                             dlg_stdradiobutton_handler,\r
+                             I(offsetof(Config, passive_telnet)),\r
+                             "Passive", I(1), "Active", I(0), NULL);\r
+       }\r
+       ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',\r
+                     HELPCTX(telnet_specialkeys),\r
+                     dlg_stdcheckbox_handler,\r
+                     I(offsetof(Config,telnet_keyboard)));\r
+       ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",\r
+                     'm', HELPCTX(telnet_newline),\r
+                     dlg_stdcheckbox_handler,\r
+                     I(offsetof(Config,telnet_newline)));\r
+    }\r
+\r
+    if (!midsession) {\r
+\r
+       /*\r
+        * The Connection/Rlogin panel.\r
+        */\r
+       ctrl_settitle(b, "Connection/Rlogin",\r
+                     "Options controlling Rlogin connections");\r
+\r
+       s = ctrl_getset(b, "Connection/Rlogin", "data",\r
+                       "Data to send to the server");\r
+       ctrl_editbox(s, "Local username:", 'l', 50,\r
+                    HELPCTX(rlogin_localuser),\r
+                    dlg_stdeditbox_handler, I(offsetof(Config,localusername)),\r
+                    I(sizeof(((Config *)0)->localusername)));\r
+\r
+    }\r
+\r
+    /*\r
+     * All the SSH stuff is omitted in PuTTYtel, or in a reconfig\r
+     * when we're not doing SSH.\r
+     */\r
+\r
+    if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {\r
+\r
+       /*\r
+        * The Connection/SSH panel.\r
+        */\r
+       ctrl_settitle(b, "Connection/SSH",\r
+                     "Options controlling SSH connections");\r
+\r
+       if (midsession && protcfginfo == 1) {\r
+           s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);\r
+           ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"\r
+                     "session; it is only here so that sub-panels of it can "\r
+                     "exist without looking strange.", HELPCTX(no_help));\r
+       }\r
+\r
+       if (!midsession) {\r
+\r
+           s = ctrl_getset(b, "Connection/SSH", "data",\r
+                           "Data to send to the server");\r
+           ctrl_editbox(s, "Remote command:", 'r', 100,\r
+                        HELPCTX(ssh_command),\r
+                        dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),\r
+                        I(sizeof(((Config *)0)->remote_cmd)));\r
+\r
+           s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");\r
+           ctrl_checkbox(s, "Don't start a shell or command at all", 'n',\r
+                         HELPCTX(ssh_noshell),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,ssh_no_shell)));\r
+       }\r
+\r
+       if (!midsession || protcfginfo != 1) {\r
+           s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");\r
+\r
+           ctrl_checkbox(s, "Enable compression", 'e',\r
+                         HELPCTX(ssh_compress),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,compression)));\r
+       }\r
+\r
+       if (!midsession) {\r
+           s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");\r
+\r
+           ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,\r
+                             HELPCTX(ssh_protocol),\r
+                             dlg_stdradiobutton_handler,\r
+                             I(offsetof(Config, sshprot)),\r
+                             "1 only", 'l', I(0),\r
+                             "1", '1', I(1),\r
+                             "2", '2', I(2),\r
+                             "2 only", 'y', I(3), NULL);\r
+       }\r
+\r
+       if (!midsession || protcfginfo != 1) {\r
+           s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");\r
+           c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',\r
+                             HELPCTX(ssh_ciphers),\r
+                             cipherlist_handler, P(NULL));\r
+           c->listbox.height = 6;\r
+\r
+           ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',\r
+                         HELPCTX(ssh_ciphers),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,ssh2_des_cbc)));\r
+       }\r
+\r
+       /*\r
+        * The Connection/SSH/Kex panel. (Owing to repeat key\r
+        * exchange, this is all meaningful in mid-session _if_\r
+        * we're using SSH-2 or haven't decided yet.)\r
+        */\r
+       if (protcfginfo != 1) {\r
+           ctrl_settitle(b, "Connection/SSH/Kex",\r
+                         "Options controlling SSH key exchange");\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/Kex", "main",\r
+                           "Key exchange algorithm options");\r
+           c = ctrl_draglist(s, "Algorithm selection policy:", 's',\r
+                             HELPCTX(ssh_kexlist),\r
+                             kexlist_handler, P(NULL));\r
+           c->listbox.height = 5;\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",\r
+                           "Options controlling key re-exchange");\r
+\r
+           ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,\r
+                        HELPCTX(ssh_kex_repeat),\r
+                        dlg_stdeditbox_handler,\r
+                        I(offsetof(Config,ssh_rekey_time)),\r
+                        I(-1));\r
+           ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,\r
+                        HELPCTX(ssh_kex_repeat),\r
+                        dlg_stdeditbox_handler,\r
+                        I(offsetof(Config,ssh_rekey_data)),\r
+                        I(16));\r
+           ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",\r
+                     HELPCTX(ssh_kex_repeat));\r
+       }\r
+\r
+       if (!midsession) {\r
+\r
+           /*\r
+            * The Connection/SSH/Auth panel.\r
+            */\r
+           ctrl_settitle(b, "Connection/SSH/Auth",\r
+                         "Options controlling SSH authentication");\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);\r
+           ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',\r
+                         HELPCTX(ssh_auth_bypass),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,ssh_no_userauth)));\r
+           ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",\r
+                         'd', HELPCTX(ssh_auth_banner),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,ssh_show_banner)));\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/Auth", "methods",\r
+                           "Authentication methods");\r
+           ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',\r
+                         HELPCTX(ssh_auth_pageant),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,tryagent)));\r
+           ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',\r
+                         HELPCTX(ssh_auth_tis),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,try_tis_auth)));\r
+           ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",\r
+                         'i', HELPCTX(ssh_auth_ki),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,try_ki_auth)));\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/Auth", "params",\r
+                           "Authentication parameters");\r
+           ctrl_checkbox(s, "Allow agent forwarding", 'f',\r
+                         HELPCTX(ssh_auth_agentfwd),\r
+                         dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));\r
+           ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,\r
+                         HELPCTX(ssh_auth_changeuser),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,change_username)));\r
+           ctrl_filesel(s, "Private key file for authentication:", 'k',\r
+                        FILTER_KEY_FILES, FALSE, "Select private key file",\r
+                        HELPCTX(ssh_auth_privkey),\r
+                        dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));\r
+\r
+#ifndef NO_GSSAPI\r
+           /*\r
+            * Connection/SSH/Auth/GSSAPI, which sadly won't fit on\r
+            * the main Auth panel.\r
+            */\r
+           ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",\r
+                         "Options controlling GSSAPI authentication");\r
+           s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);\r
+\r
+           ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",\r
+                         't', HELPCTX(ssh_gssapi),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,try_gssapi_auth)));\r
+\r
+           ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',\r
+                         HELPCTX(ssh_gssapi_delegation),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,gssapifwd)));\r
+\r
+           /*\r
+            * GSSAPI library selection.\r
+            */\r
+           if (ngsslibs > 1) {\r
+               c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",\r
+                                 'p', HELPCTX(ssh_gssapi_libraries),\r
+                                 gsslist_handler, P(NULL));\r
+               c->listbox.height = ngsslibs;\r
+\r
+               /*\r
+                * I currently assume that if more than one GSS\r
+                * library option is available, then one of them is\r
+                * 'user-supplied' and so we should present the\r
+                * following file selector. This is at least half-\r
+                * reasonable, because if we're using statically\r
+                * linked GSSAPI then there will only be one option\r
+                * and no way to load from a user-supplied library,\r
+                * whereas if we're using dynamic libraries then\r
+                * there will almost certainly be some default\r
+                * option in addition to a user-supplied path. If\r
+                * anyone ever ports PuTTY to a system on which\r
+                * dynamic-library GSSAPI is available but there is\r
+                * absolutely no consensus on where to keep the\r
+                * libraries, there'll need to be a flag alongside\r
+                * ngsslibs to control whether the file selector is\r
+                * displayed. \r
+                */\r
+\r
+               ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',\r
+                            FILTER_DYNLIB_FILES, FALSE, "Select library file",\r
+                            HELPCTX(ssh_gssapi_libraries),\r
+                            dlg_stdfilesel_handler,\r
+                            I(offsetof(Config, ssh_gss_custom)));\r
+           }\r
+#endif\r
+       }\r
+\r
+       if (!midsession) {\r
+           /*\r
+            * The Connection/SSH/TTY panel.\r
+            */\r
+           ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);\r
+           ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',\r
+                         HELPCTX(ssh_nopty),\r
+                         dlg_stdcheckbox_handler,\r
+                         I(offsetof(Config,nopty)));\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",\r
+                           "Terminal modes");\r
+           td = (struct ttymodes_data *)\r
+               ctrl_alloc(b, sizeof(struct ttymodes_data));\r
+           ctrl_columns(s, 2, 75, 25);\r
+           c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));\r
+           c->generic.column = 0;\r
+           td->rembutton = ctrl_pushbutton(s, "Remove", 'r',\r
+                                           HELPCTX(ssh_ttymodes),\r
+                                           ttymodes_handler, P(td));\r
+           td->rembutton->generic.column = 1;\r
+           td->rembutton->generic.tabdelay = 1;\r
+           ctrl_columns(s, 1, 100);\r
+           td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
+                                      HELPCTX(ssh_ttymodes),\r
+                                      ttymodes_handler, P(td));\r
+           td->listbox->listbox.multisel = 1;\r
+           td->listbox->listbox.height = 4;\r
+           td->listbox->listbox.ncols = 2;\r
+           td->listbox->listbox.percentages = snewn(2, int);\r
+           td->listbox->listbox.percentages[0] = 40;\r
+           td->listbox->listbox.percentages[1] = 60;\r
+           ctrl_tabdelay(s, td->rembutton);\r
+           ctrl_columns(s, 2, 75, 25);\r
+           td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,\r
+                                        HELPCTX(ssh_ttymodes),\r
+                                        ttymodes_handler, P(td));\r
+           td->modelist->generic.column = 0;\r
+           td->addbutton = ctrl_pushbutton(s, "Add", 'd',\r
+                                           HELPCTX(ssh_ttymodes),\r
+                                           ttymodes_handler, P(td));\r
+           td->addbutton->generic.column = 1;\r
+           td->addbutton->generic.tabdelay = 1;\r
+           ctrl_columns(s, 1, 100);        /* column break */\r
+           /* Bit of a hack to get the value radio buttons and\r
+            * edit-box on the same row. */\r
+           ctrl_columns(s, 3, 25, 50, 25);\r
+           c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));\r
+           c->generic.column = 0;\r
+           td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,\r
+                                            HELPCTX(ssh_ttymodes),\r
+                                            ttymodes_handler, P(td),\r
+                                            "Auto", NO_SHORTCUT, P(NULL),\r
+                                            "This:", NO_SHORTCUT, P(NULL),\r
+                                            NULL);\r
+           td->valradio->generic.column = 1;\r
+           td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,\r
+                                     HELPCTX(ssh_ttymodes),\r
+                                     ttymodes_handler, P(td), P(NULL));\r
+           td->valbox->generic.column = 2;\r
+           ctrl_tabdelay(s, td->addbutton);\r
+\r
+       }\r
+\r
+       if (!midsession) {\r
+           /*\r
+            * The Connection/SSH/X11 panel.\r
+            */\r
+           ctrl_settitle(b, "Connection/SSH/X11",\r
+                         "Options controlling SSH X11 forwarding");\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");\r
+           ctrl_checkbox(s, "Enable X11 forwarding", 'e',\r
+                         HELPCTX(ssh_tunnels_x11),\r
+                         dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));\r
+           ctrl_editbox(s, "X display location", 'x', 50,\r
+                        HELPCTX(ssh_tunnels_x11),\r
+                        dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),\r
+                        I(sizeof(((Config *)0)->x11_display)));\r
+           ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,\r
+                             HELPCTX(ssh_tunnels_x11auth),\r
+                             dlg_stdradiobutton_handler,\r
+                             I(offsetof(Config, x11_auth)),\r
+                             "MIT-Magic-Cookie-1", I(X11_MIT),\r
+                             "XDM-Authorization-1", I(X11_XDM), NULL);\r
+       }\r
+\r
+       /*\r
+        * The Tunnels panel _is_ still available in mid-session.\r
+        */\r
+       ctrl_settitle(b, "Connection/SSH/Tunnels",\r
+                     "Options controlling SSH port forwarding");\r
+\r
+       s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",\r
+                       "Port forwarding");\r
+       ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',\r
+                     HELPCTX(ssh_tunnels_portfwd_localhost),\r
+                     dlg_stdcheckbox_handler,\r
+                     I(offsetof(Config,lport_acceptall)));\r
+       ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',\r
+                     HELPCTX(ssh_tunnels_portfwd_localhost),\r
+                     dlg_stdcheckbox_handler,\r
+                     I(offsetof(Config,rport_acceptall)));\r
+\r
+       ctrl_columns(s, 3, 55, 20, 25);\r
+       c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));\r
+       c->generic.column = COLUMN_FIELD(0,2);\r
+       /* You want to select from the list, _then_ hit Remove. So tab order\r
+        * should be that way round. */\r
+       pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));\r
+       pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',\r
+                                        HELPCTX(ssh_tunnels_portfwd),\r
+                                        portfwd_handler, P(pfd));\r
+       pfd->rembutton->generic.column = 2;\r
+       pfd->rembutton->generic.tabdelay = 1;\r
+       pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
+                                   HELPCTX(ssh_tunnels_portfwd),\r
+                                   portfwd_handler, P(pfd));\r
+       pfd->listbox->listbox.height = 3;\r
+       pfd->listbox->listbox.ncols = 2;\r
+       pfd->listbox->listbox.percentages = snewn(2, int);\r
+       pfd->listbox->listbox.percentages[0] = 20;\r
+       pfd->listbox->listbox.percentages[1] = 80;\r
+       ctrl_tabdelay(s, pfd->rembutton);\r
+       ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));\r
+       /* You want to enter source, destination and type, _then_ hit Add.\r
+        * Again, we adjust the tab order to reflect this. */\r
+       pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',\r
+                                        HELPCTX(ssh_tunnels_portfwd),\r
+                                        portfwd_handler, P(pfd));\r
+       pfd->addbutton->generic.column = 2;\r
+       pfd->addbutton->generic.tabdelay = 1;\r
+       pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,\r
+                                     HELPCTX(ssh_tunnels_portfwd),\r
+                                     portfwd_handler, P(pfd), P(NULL));\r
+       pfd->sourcebox->generic.column = 0;\r
+       pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,\r
+                                   HELPCTX(ssh_tunnels_portfwd),\r
+                                   portfwd_handler, P(pfd), P(NULL));\r
+       pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,\r
+                                          HELPCTX(ssh_tunnels_portfwd),\r
+                                          portfwd_handler, P(pfd),\r
+                                          "Local", 'l', P(NULL),\r
+                                          "Remote", 'm', P(NULL),\r
+                                          "Dynamic", 'y', P(NULL),\r
+                                          NULL);\r
+#ifndef NO_IPV6\r
+       pfd->addressfamily =\r
+           ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,\r
+                             HELPCTX(ssh_tunnels_portfwd_ipversion),\r
+                             portfwd_handler, P(pfd),\r
+                             "Auto", 'u', I(ADDRTYPE_UNSPEC),\r
+                             "IPv4", '4', I(ADDRTYPE_IPV4),\r
+                             "IPv6", '6', I(ADDRTYPE_IPV6),\r
+                             NULL);\r
+#endif\r
+       ctrl_tabdelay(s, pfd->addbutton);\r
+       ctrl_columns(s, 1, 100);\r
+\r
+       if (!midsession) {\r
+           /*\r
+            * The Connection/SSH/Bugs panel.\r
+            */\r
+           ctrl_settitle(b, "Connection/SSH/Bugs",\r
+                         "Workarounds for SSH server bugs");\r
+\r
+           s = ctrl_getset(b, "Connection/SSH/Bugs", "main",\r
+                           "Detection of known bugs in SSH servers");\r
+           ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,\r
+                         HELPCTX(ssh_bugs_ignore1),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_ignore1)));\r
+           ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,\r
+                         HELPCTX(ssh_bugs_plainpw1),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));\r
+           ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,\r
+                         HELPCTX(ssh_bugs_rsa1),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_rsa1)));\r
+           ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,\r
+                         HELPCTX(ssh_bugs_ignore2),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_ignore2)));\r
+           ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,\r
+                         HELPCTX(ssh_bugs_hmac2),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_hmac2)));\r
+           ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,\r
+                         HELPCTX(ssh_bugs_derivekey2),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));\r
+           ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,\r
+                         HELPCTX(ssh_bugs_rsapad2),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));\r
+           ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,\r
+                         HELPCTX(ssh_bugs_pksessid2),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));\r
+           ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,\r
+                         HELPCTX(ssh_bugs_rekey2),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_rekey2)));\r
+           ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,\r
+                         HELPCTX(ssh_bugs_maxpkt2),\r
+                         sshbug_handler, I(offsetof(Config,sshbug_maxpkt2)));\r
+       }\r
+    }\r
+}\r
diff --git a/putty/CONTRIB/CYGTERMD/MAIN.C b/putty/CONTRIB/CYGTERMD/MAIN.C
new file mode 100644 (file)
index 0000000..f499b3c
--- /dev/null
@@ -0,0 +1,174 @@
+/*\r
+ * Main program.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stddef.h>\r
+#include <stdarg.h>\r
+#include <signal.h>\r
+#include <string.h>\r
+#include <errno.h>\r
+\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/wait.h>\r
+\r
+#include "sel.h"\r
+#include "pty.h"\r
+#include "telnet.h"\r
+\r
+int signalpipe[2];\r
+\r
+sel *asel;\r
+sel_rfd *netr, *ptyr, *sigr;\r
+int ptyfd;\r
+sel_wfd *netw, *ptyw;\r
+Telnet telnet;\r
+\r
+#define BUF 65536\r
+\r
+void sigchld(int signum)\r
+{\r
+    write(signalpipe[1], "C", 1);\r
+}\r
+\r
+void fatal(const char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "FIXME: ");\r
+    va_start(ap, fmt);\r
+    vfprintf(stderr, fmt, ap);\r
+    va_end(ap);\r
+    fprintf(stderr, "\n");\r
+    exit(1);\r
+}\r
+\r
+void net_readdata(sel_rfd *rfd, void *data, size_t len)\r
+{\r
+    if (len == 0)\r
+       exit(0);                       /* EOF on network - client went away */\r
+    telnet_from_net(telnet, data, len);\r
+    if (sel_write(netw, NULL, 0) > BUF)\r
+       sel_rfd_freeze(ptyr);\r
+    if (sel_write(ptyw, NULL, 0) > BUF)\r
+       sel_rfd_freeze(netr);\r
+}\r
+\r
+void net_readerr(sel_rfd *rfd, int error)\r
+{\r
+    fprintf(stderr, "standard input: read: %s\n", strerror(errno));\r
+    exit(1);\r
+}\r
+\r
+void net_written(sel_wfd *wfd, size_t bufsize)\r
+{\r
+    if (bufsize < BUF)\r
+       sel_rfd_unfreeze(ptyr);\r
+}\r
+\r
+void net_writeerr(sel_wfd *wfd, int error)\r
+{\r
+    fprintf(stderr, "standard input: write: %s\n", strerror(errno));\r
+    exit(1);\r
+}\r
+\r
+void pty_readdata(sel_rfd *rfd, void *data, size_t len)\r
+{\r
+    if (len == 0)\r
+       exit(0);                       /* EOF on pty */\r
+    telnet_from_pty(telnet, data, len);\r
+    if (sel_write(netw, NULL, 0) > BUF)\r
+       sel_rfd_freeze(ptyr);\r
+    if (sel_write(ptyw, NULL, 0) > BUF)\r
+       sel_rfd_freeze(netr);\r
+}\r
+\r
+void pty_readerr(sel_rfd *rfd, int error)\r
+{\r
+    if (error == EIO)                 /* means EOF, on a pty */\r
+       exit(0);\r
+    fprintf(stderr, "pty: read: %s\n", strerror(errno));\r
+    exit(1);\r
+}\r
+\r
+void pty_written(sel_wfd *wfd, size_t bufsize)\r
+{\r
+    if (bufsize < BUF)\r
+       sel_rfd_unfreeze(netr);\r
+}\r
+\r
+void pty_writeerr(sel_wfd *wfd, int error)\r
+{\r
+    fprintf(stderr, "pty: write: %s\n", strerror(errno));\r
+    exit(1);\r
+}\r
+\r
+void sig_readdata(sel_rfd *rfd, void *data, size_t len)\r
+{\r
+    char *p = data;\r
+\r
+    while (len > 0) {\r
+       if (*p == 'C') {\r
+           int status;\r
+           pid_t pid = waitpid(-1, &status, WNOHANG);\r
+           if (WIFEXITED(status) || WIFSIGNALED(status))\r
+               exit(0);               /* child process vanished */\r
+       }\r
+    }\r
+}\r
+\r
+void sig_readerr(sel_rfd *rfd, int error)\r
+{\r
+    fprintf(stderr, "signal pipe: read: %s\n", strerror(errno));\r
+    exit(1);\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    int ret;\r
+    int shell_started = 0;\r
+    char *directory = NULL;\r
+    char **program_args = NULL;\r
+\r
+    if (argc > 1 && argv[1][0]) {\r
+        directory = argv[1];\r
+        argc--, argv++;\r
+    }\r
+    if (argc > 1) {\r
+        program_args = argv + 1;\r
+    }\r
+\r
+    pty_preinit();\r
+\r
+    asel = sel_new(NULL);\r
+    netr = sel_rfd_add(asel, 0, net_readdata, net_readerr, NULL);\r
+    netw = sel_wfd_add(asel, 1, net_written, net_writeerr, NULL);\r
+    ptyr = sel_rfd_add(asel, -1, pty_readdata, pty_readerr, NULL);\r
+    ptyw = sel_wfd_add(asel, -1, pty_written, pty_writeerr, NULL);\r
+\r
+    telnet = telnet_new(netw, ptyw);\r
+\r
+    if (pipe(signalpipe) < 0) {\r
+       perror("pipe");\r
+       return 1;\r
+    }\r
+    sigr = sel_rfd_add(asel, signalpipe[0], sig_readdata,\r
+                      sig_readerr, NULL);\r
+\r
+    signal(SIGCHLD, sigchld);\r
+\r
+    do {\r
+       struct shell_data shdata;\r
+\r
+       ret = sel_iterate(asel, -1);\r
+       if (!shell_started && telnet_shell_ok(telnet, &shdata)) {\r
+           ptyfd = run_program_in_pty(&shdata, directory, program_args);\r
+           sel_rfd_setfd(ptyr, ptyfd);\r
+           sel_wfd_setfd(ptyw, ptyfd);\r
+           shell_started = 1;\r
+       }\r
+    } while (ret == 0);\r
+\r
+    return 0;\r
+}\r
diff --git a/putty/CONTRIB/CYGTERMD/MAKEFILE b/putty/CONTRIB/CYGTERMD/MAKEFILE
new file mode 100644 (file)
index 0000000..adb5584
--- /dev/null
@@ -0,0 +1,2 @@
+cygtermd.exe: main.c sel.c telnet.c pty.c malloc.c\r
+       gcc -o cygtermd.exe main.c sel.c telnet.c pty.c malloc.c\r
diff --git a/putty/CONTRIB/CYGTERMD/MALLOC.C b/putty/CONTRIB/CYGTERMD/MALLOC.C
new file mode 100644 (file)
index 0000000..0397181
--- /dev/null
@@ -0,0 +1,43 @@
+/*\r
+ * malloc.c: implementation of malloc.h\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include "malloc.h"\r
+\r
+extern void fatal(const char *, ...);\r
+\r
+void *smalloc(size_t size) {\r
+    void *p;\r
+    p = malloc(size);\r
+    if (!p) {\r
+       fatal("out of memory");\r
+    }\r
+    return p;\r
+}\r
+\r
+void sfree(void *p) {\r
+    if (p) {\r
+       free(p);\r
+    }\r
+}\r
+\r
+void *srealloc(void *p, size_t size) {\r
+    void *q;\r
+    if (p) {\r
+       q = realloc(p, size);\r
+    } else {\r
+       q = malloc(size);\r
+    }\r
+    if (!q)\r
+       fatal("out of memory");\r
+    return q;\r
+}\r
+\r
+char *dupstr(const char *s) {\r
+    char *r = smalloc(1+strlen(s));\r
+    strcpy(r,s);\r
+    return r;\r
+}\r
diff --git a/putty/CONTRIB/CYGTERMD/MALLOC.H b/putty/CONTRIB/CYGTERMD/MALLOC.H
new file mode 100644 (file)
index 0000000..0fd4a36
--- /dev/null
@@ -0,0 +1,56 @@
+/*\r
+ * malloc.h: safe wrappers around malloc, realloc, free, strdup\r
+ */\r
+\r
+#ifndef UMLWRAP_MALLOC_H\r
+#define UMLWRAP_MALLOC_H\r
+\r
+#include <stddef.h>\r
+\r
+/*\r
+ * smalloc should guarantee to return a useful pointer - Halibut\r
+ * can do nothing except die when it's out of memory anyway.\r
+ */\r
+void *smalloc(size_t size);\r
+\r
+/*\r
+ * srealloc should guaranteeably be able to realloc NULL\r
+ */\r
+void *srealloc(void *p, size_t size);\r
+\r
+/*\r
+ * sfree should guaranteeably deal gracefully with freeing NULL\r
+ */\r
+void sfree(void *p);\r
+\r
+/*\r
+ * dupstr is like strdup, but with the never-return-NULL property\r
+ * of smalloc (and also reliably defined in all environments :-)\r
+ */\r
+char *dupstr(const char *s);\r
+\r
+/*\r
+ * snew allocates one instance of a given type, and casts the\r
+ * result so as to type-check that you're assigning it to the\r
+ * right kind of pointer. Protects against allocation bugs\r
+ * involving allocating the wrong size of thing.\r
+ */\r
+#define snew(type) \\r
+    ( (type *) smalloc (sizeof (type)) )\r
+\r
+/*\r
+ * snewn allocates n instances of a given type, for arrays.\r
+ */\r
+#define snewn(number, type) \\r
+    ( (type *) smalloc ((number) * sizeof (type)) )\r
+\r
+/*\r
+ * sresize wraps realloc so that you specify the new number of\r
+ * elements and the type of the element, with the same type-\r
+ * checking advantages. Also type-checks the input pointer.\r
+ */\r
+#define sresize(array, number, type) \\r
+    ( (void)sizeof((array)-(type *)0), \\r
+      (type *) srealloc ((array), (number) * sizeof (type)) )\r
+\r
+#endif /* UMLWRAP_MALLOC_H */\r
diff --git a/putty/CONTRIB/CYGTERMD/PTY.C b/putty/CONTRIB/CYGTERMD/PTY.C
new file mode 100644 (file)
index 0000000..dd4d1a4
--- /dev/null
@@ -0,0 +1,188 @@
+/*\r
+ * pty.c - pseudo-terminal handling\r
+ */\r
+\r
+#define _XOPEN_SOURCE\r
+#include <features.h>\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <assert.h>\r
+\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#include <termios.h>\r
+#include <sys/ioctl.h>\r
+#include <sys/types.h>\r
+#include <pwd.h>\r
+\r
+#include "pty.h"\r
+#include "malloc.h"\r
+\r
+static char ptyname[FILENAME_MAX];\r
+int master = -1;\r
+\r
+void pty_preinit(void)\r
+{\r
+    /*\r
+     * Allocate the pty.\r
+     */\r
+    master = open("/dev/ptmx", O_RDWR);\r
+    if (master < 0) {\r
+       perror("/dev/ptmx: open");\r
+       exit(1);\r
+    }\r
+\r
+    if (grantpt(master) < 0) {\r
+       perror("grantpt");\r
+       exit(1);\r
+    }\r
+    \r
+    if (unlockpt(master) < 0) {\r
+       perror("unlockpt");\r
+       exit(1);\r
+    }\r
+}\r
+\r
+void pty_resize(int w, int h)\r
+{\r
+    struct winsize sz;\r
+\r
+    assert(master >= 0);\r
+\r
+    sz.ws_row = h;\r
+    sz.ws_col = w;\r
+    sz.ws_xpixel = sz.ws_ypixel = 0;\r
+    ioctl(master, TIOCSWINSZ, &sz);\r
+}\r
+\r
+int run_program_in_pty(const struct shell_data *shdata,\r
+                       char *directory, char **program_args)\r
+{\r
+    int slave, pid;\r
+    char *fallback_args[2];\r
+\r
+    assert(master >= 0);\r
+\r
+    ptyname[FILENAME_MAX-1] = '\0';\r
+    strncpy(ptyname, ptsname(master), FILENAME_MAX-1);\r
+\r
+#if 0\r
+    {\r
+       struct winsize ws;\r
+       struct termios ts;\r
+\r
+       /*\r
+        * FIXME: think up some good defaults here\r
+        */\r
+\r
+       if (!ioctl(0, TIOCGWINSZ, &ws))\r
+           ioctl(master, TIOCSWINSZ, &ws);\r
+       if (!tcgetattr(0, &ts))\r
+           tcsetattr(master, TCSANOW, &ts);\r
+    }\r
+#endif\r
+\r
+    slave = open(ptyname, O_RDWR | O_NOCTTY);\r
+    if (slave < 0) {\r
+       perror("slave pty: open");\r
+       return 1;\r
+    }\r
+\r
+    /*\r
+     * Fork and execute the command.\r
+     */\r
+    pid = fork();\r
+    if (pid < 0) {\r
+       perror("fork");\r
+       return 1;\r
+    }\r
+\r
+    if (pid == 0) {\r
+       int i, fd;\r
+\r
+       /*\r
+        * We are the child.\r
+        */\r
+       close(master);\r
+\r
+       fcntl(slave, F_SETFD, 0);    /* don't close on exec */\r
+       dup2(slave, 0);\r
+       dup2(slave, 1);\r
+       if (slave != 0 && slave != 1)\r
+           close(slave);\r
+       dup2(1, 2);\r
+       setsid();\r
+       setpgrp();\r
+        i = 0;\r
+#ifdef TIOCNOTTY\r
+        if ((fd = open("/dev/tty", O_RDWR)) >= 0) {\r
+            ioctl(fd, TIOCNOTTY, &i);\r
+            close(fd);\r
+        }\r
+#endif\r
+#ifdef TIOCSCTTY\r
+        ioctl(0, TIOCSCTTY, &i);\r
+#endif\r
+       tcsetpgrp(0, getpgrp());\r
+\r
+       for (i = 0; i < shdata->nenvvars; i++)\r
+            putenv(shdata->envvars[i]);\r
+       if (shdata->termtype)\r
+            putenv(shdata->termtype);\r
+\r
+        if (directory)\r
+            chdir(directory);\r
+\r
+       /*\r
+        * Use the provided shell program name, if the user gave\r
+        * one. Failing that, use $SHELL; failing that, look up\r
+        * the user's default shell in the password file; failing\r
+        * _that_, revert to the bog-standard /bin/sh.\r
+        */\r
+       if (!program_args) {\r
+            char *shell;\r
+            \r
+           shell = getenv("SHELL");\r
+            if (!shell) {\r
+                const char *login;\r
+                uid_t uid;\r
+                struct passwd *pwd;\r
+\r
+                /*\r
+                 * For maximum generality in the face of multiple\r
+                 * /etc/passwd entries with different login names and\r
+                 * shells but a shared uid, we start by using\r
+                 * getpwnam(getlogin()) if it's available - but we\r
+                 * insist that its uid must match our real one, or we\r
+                 * give up and fall back to getpwuid(getuid()).\r
+                 */\r
+                uid = getuid();\r
+                login = getlogin();\r
+                if (login && (pwd = getpwnam(login)) && pwd->pw_uid == uid)\r
+                    shell = pwd->pw_shell;\r
+                else if ((pwd = getpwuid(uid)))\r
+                    shell = pwd->pw_shell;\r
+            }\r
+            if (!shell)\r
+                shell = "/bin/sh";\r
+\r
+            fallback_args[0] = shell;\r
+            fallback_args[1] = NULL;\r
+            program_args = fallback_args;\r
+        }\r
+\r
+        execv(program_args[0], program_args);\r
+\r
+       /*\r
+        * If we're here, exec has gone badly foom.\r
+        */\r
+       perror("exec");\r
+       exit(127);\r
+    }\r
+\r
+    close(slave);\r
+\r
+    return master;\r
+}\r
diff --git a/putty/CONTRIB/CYGTERMD/PTY.H b/putty/CONTRIB/CYGTERMD/PTY.H
new file mode 100644 (file)
index 0000000..d4c22e2
--- /dev/null
@@ -0,0 +1,28 @@
+/*\r
+ * pty.h - FIXME\r
+ */\r
+\r
+#ifndef FIXME_PTY_H\r
+#define FIXME_PTY_H\r
+\r
+#include "telnet.h"                   /* for struct shdata */\r
+\r
+/*\r
+ * Called at program startup to actually allocate a pty, so that\r
+ * we can start passing in resize events as soon as they arrive.\r
+ */\r
+void pty_preinit(void);\r
+\r
+/*\r
+ * Set the terminal size for the pty.\r
+ */\r
+void pty_resize(int w, int h);\r
+\r
+/*\r
+ * Start a program in a subprocess running in the pty we allocated.\r
+ * Returns the fd of the pty master.\r
+ */\r
+int run_program_in_pty(const struct shell_data *shdata,\r
+                       char *directory, char **program_args);\r
+\r
+#endif /* FIXME_PTY_H */\r
diff --git a/putty/CONTRIB/CYGTERMD/README b/putty/CONTRIB/CYGTERMD/README
new file mode 100644 (file)
index 0000000..c4adcc9
--- /dev/null
@@ -0,0 +1,11 @@
+This directory contains 'cygtermd', a small and specialist Telnet\r
+server designed to act as middleware between PuTTY and a Cygwin shell\r
+session running on the same machine, so that PuTTY can act as an\r
+xterm-alike for Cygwin.\r
+\r
+To install it, you must compile it from source using Cygwin gcc,\r
+install it in Cygwin's /bin, and configure PuTTY to use it as a local\r
+proxy process. For detailed instructions, see the PuTTY Wishlist page\r
+at\r
+\r
+http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/cygwin-terminal-window.html\r
diff --git a/putty/CONTRIB/CYGTERMD/SEL.C b/putty/CONTRIB/CYGTERMD/SEL.C
new file mode 100644 (file)
index 0000000..7a26567
--- /dev/null
@@ -0,0 +1,386 @@
+/*\r
+ * sel.c: implementation of sel.h.\r
+ */\r
+\r
+#include <stddef.h>\r
+#include <string.h>\r
+#include <errno.h>\r
+#include <assert.h>\r
+\r
+#include <fcntl.h>\r
+#include <unistd.h>\r
+#include <sys/time.h>\r
+#include <sys/types.h>\r
+#include <sys/select.h>\r
+\r
+#include "sel.h"\r
+#include "malloc.h"\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Chunk of code lifted from PuTTY's misc.c to manage buffers of\r
+ * data to be written to an fd.\r
+ */\r
+\r
+#define BUFFER_GRANULE  512\r
+\r
+typedef struct bufchain_tag {\r
+    struct bufchain_granule *head, *tail;\r
+    size_t buffersize;                /* current amount of buffered data */\r
+} bufchain;\r
+struct bufchain_granule {\r
+    struct bufchain_granule *next;\r
+    size_t buflen, bufpos;\r
+    char buf[BUFFER_GRANULE];\r
+};\r
+\r
+static void bufchain_init(bufchain *ch)\r
+{\r
+    ch->head = ch->tail = NULL;\r
+    ch->buffersize = 0;\r
+}\r
+\r
+static void bufchain_clear(bufchain *ch)\r
+{\r
+    struct bufchain_granule *b;\r
+    while (ch->head) {\r
+       b = ch->head;\r
+       ch->head = ch->head->next;\r
+       sfree(b);\r
+    }\r
+    ch->tail = NULL;\r
+    ch->buffersize = 0;\r
+}\r
+\r
+static size_t bufchain_size(bufchain *ch)\r
+{\r
+    return ch->buffersize;\r
+}\r
+\r
+static void bufchain_add(bufchain *ch, const void *data, size_t len)\r
+{\r
+    const char *buf = (const char *)data;\r
+\r
+    if (len == 0) return;\r
+\r
+    ch->buffersize += len;\r
+\r
+    if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {\r
+       size_t copylen = BUFFER_GRANULE - ch->tail->buflen;\r
+       if (copylen > len)\r
+           copylen = len;\r
+       memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);\r
+       buf += copylen;\r
+       len -= copylen;\r
+       ch->tail->buflen += copylen;\r
+    }\r
+    while (len > 0) {\r
+       struct bufchain_granule *newbuf;\r
+       size_t grainlen = BUFFER_GRANULE;\r
+       if (grainlen > len)\r
+           grainlen = len;\r
+       newbuf = snew(struct bufchain_granule);\r
+       newbuf->bufpos = 0;\r
+       newbuf->buflen = grainlen;\r
+       memcpy(newbuf->buf, buf, grainlen);\r
+       buf += grainlen;\r
+       len -= grainlen;\r
+       if (ch->tail)\r
+           ch->tail->next = newbuf;\r
+       else\r
+           ch->head = ch->tail = newbuf;\r
+       newbuf->next = NULL;\r
+       ch->tail = newbuf;\r
+    }\r
+}\r
+\r
+static void bufchain_consume(bufchain *ch, size_t len)\r
+{\r
+    struct bufchain_granule *tmp;\r
+\r
+    assert(ch->buffersize >= len);\r
+    while (len > 0) {\r
+       size_t remlen = len;\r
+       assert(ch->head != NULL);\r
+       if (remlen >= ch->head->buflen - ch->head->bufpos) {\r
+           remlen = ch->head->buflen - ch->head->bufpos;\r
+           tmp = ch->head;\r
+           ch->head = tmp->next;\r
+           sfree(tmp);\r
+           if (!ch->head)\r
+               ch->tail = NULL;\r
+       } else\r
+           ch->head->bufpos += remlen;\r
+       ch->buffersize -= remlen;\r
+       len -= remlen;\r
+    }\r
+}\r
+\r
+static void bufchain_prefix(bufchain *ch, void **data, size_t *len)\r
+{\r
+    *len = ch->head->buflen - ch->head->bufpos;\r
+    *data = ch->head->buf + ch->head->bufpos;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * The actual implementation of the sel interface.\r
+ */\r
+\r
+struct sel {\r
+    void *ctx;\r
+    sel_rfd *rhead, *rtail;\r
+    sel_wfd *whead, *wtail;\r
+};\r
+\r
+struct sel_rfd {\r
+    sel *parent;\r
+    sel_rfd *prev, *next;\r
+    sel_readdata_fn_t readdata;\r
+    sel_readerr_fn_t readerr;\r
+    void *ctx;\r
+    int fd;\r
+    int frozen;\r
+};\r
+\r
+struct sel_wfd {\r
+    sel *parent;\r
+    sel_wfd *prev, *next;\r
+    sel_written_fn_t written;\r
+    sel_writeerr_fn_t writeerr;\r
+    void *ctx;\r
+    int fd;\r
+    bufchain buf;\r
+};\r
+\r
+sel *sel_new(void *ctx)\r
+{\r
+    sel *sel = snew(struct sel);\r
+\r
+    sel->ctx = ctx;\r
+    sel->rhead = sel->rtail = NULL;\r
+    sel->whead = sel->wtail = NULL;\r
+\r
+    return sel;\r
+}\r
+\r
+sel_wfd *sel_wfd_add(sel *sel, int fd,\r
+                    sel_written_fn_t written, sel_writeerr_fn_t writeerr,\r
+                    void *ctx)\r
+{\r
+    sel_wfd *wfd = snew(sel_wfd);\r
+\r
+    wfd->written = written;\r
+    wfd->writeerr = writeerr;\r
+    wfd->ctx = ctx;\r
+    wfd->fd = fd;\r
+    bufchain_init(&wfd->buf);\r
+\r
+    wfd->next = NULL;\r
+    wfd->prev = sel->wtail;\r
+    if (sel->wtail)\r
+       sel->wtail->next = wfd;\r
+    else\r
+       sel->whead = wfd;\r
+    sel->wtail = wfd;\r
+    wfd->parent = sel;\r
+\r
+    return wfd;\r
+}\r
+\r
+sel_rfd *sel_rfd_add(sel *sel, int fd,\r
+                    sel_readdata_fn_t readdata, sel_readerr_fn_t readerr,\r
+                    void *ctx)\r
+{\r
+    sel_rfd *rfd = snew(sel_rfd);\r
+\r
+    rfd->readdata = readdata;\r
+    rfd->readerr = readerr;\r
+    rfd->ctx = ctx;\r
+    rfd->fd = fd;\r
+    rfd->frozen = 0;\r
+\r
+    rfd->next = NULL;\r
+    rfd->prev = sel->rtail;\r
+    if (sel->rtail)\r
+       sel->rtail->next = rfd;\r
+    else\r
+       sel->rhead = rfd;\r
+    sel->rtail = rfd;\r
+    rfd->parent = sel;\r
+\r
+    return rfd;\r
+}\r
+\r
+size_t sel_write(sel_wfd *wfd, const void *data, size_t len)\r
+{\r
+    bufchain_add(&wfd->buf, data, len);\r
+    return bufchain_size(&wfd->buf);\r
+}\r
+\r
+void sel_wfd_setfd(sel_wfd *wfd, int fd)\r
+{\r
+    wfd->fd = fd;\r
+}\r
+\r
+void sel_rfd_setfd(sel_rfd *rfd, int fd)\r
+{\r
+    rfd->fd = fd;\r
+}\r
+\r
+void sel_rfd_freeze(sel_rfd *rfd)\r
+{\r
+    rfd->frozen = 1;\r
+}\r
+\r
+void sel_rfd_unfreeze(sel_rfd *rfd)\r
+{\r
+    rfd->frozen = 0;\r
+}\r
+\r
+int sel_wfd_delete(sel_wfd *wfd)\r
+{\r
+    sel *sel = wfd->parent;\r
+    int ret;\r
+\r
+    if (wfd->prev)\r
+       wfd->prev->next = wfd->next;\r
+    else\r
+       sel->whead = wfd->next;\r
+    if (wfd->next)\r
+       wfd->next->prev = wfd->prev;\r
+    else\r
+       sel->wtail = wfd->prev;\r
+\r
+    bufchain_clear(&wfd->buf);\r
+\r
+    ret = wfd->fd;\r
+    sfree(wfd);\r
+    return ret;\r
+}\r
+\r
+int sel_rfd_delete(sel_rfd *rfd)\r
+{\r
+    sel *sel = rfd->parent;\r
+    int ret;\r
+\r
+    if (rfd->prev)\r
+       rfd->prev->next = rfd->next;\r
+    else\r
+       sel->rhead = rfd->next;\r
+    if (rfd->next)\r
+       rfd->next->prev = rfd->prev;\r
+    else\r
+       sel->rtail = rfd->prev;\r
+\r
+    ret = rfd->fd;\r
+    sfree(rfd);\r
+    return ret;\r
+}\r
+\r
+void sel_free(sel *sel)\r
+{\r
+    while (sel->whead)\r
+       sel_wfd_delete(sel->whead);\r
+    while (sel->rhead)\r
+       sel_rfd_delete(sel->rhead);\r
+    sfree(sel);\r
+}\r
+\r
+void *sel_get_ctx(sel *sel) { return sel->ctx; }\r
+void sel_set_ctx(sel *sel, void *ctx) { sel->ctx = ctx; }\r
+void *sel_wfd_get_ctx(sel_wfd *wfd) { return wfd->ctx; }\r
+void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx) { wfd->ctx = ctx; }\r
+void *sel_rfd_get_ctx(sel_rfd *rfd) { return rfd->ctx; }\r
+void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx) { rfd->ctx = ctx; }\r
+\r
+int sel_iterate(sel *sel, long timeout)\r
+{\r
+    sel_rfd *rfd;\r
+    sel_wfd *wfd;\r
+    fd_set rset, wset;\r
+    int maxfd = 0;\r
+    struct timeval tv, *ptv;\r
+    char buf[65536];\r
+    int ret;\r
+\r
+    FD_ZERO(&rset);\r
+    FD_ZERO(&wset);\r
+\r
+    for (rfd = sel->rhead; rfd; rfd = rfd->next) {\r
+       if (rfd->fd >= 0 && !rfd->frozen) {\r
+           FD_SET(rfd->fd, &rset);\r
+           if (maxfd < rfd->fd + 1)\r
+               maxfd = rfd->fd + 1;\r
+       }\r
+    }\r
+\r
+    for (wfd = sel->whead; wfd; wfd = wfd->next) {\r
+       if (wfd->fd >= 0 && bufchain_size(&wfd->buf)) {\r
+           FD_SET(wfd->fd, &wset);\r
+           if (maxfd < wfd->fd + 1)\r
+               maxfd = wfd->fd + 1;\r
+       }\r
+    }\r
+\r
+    if (timeout < 0) {\r
+       ptv = NULL;\r
+    } else {\r
+       ptv = &tv;\r
+       tv.tv_sec = timeout / 1000;\r
+       tv.tv_usec = 1000 * (timeout % 1000);\r
+    }\r
+\r
+    do {\r
+       ret = select(maxfd, &rset, &wset, NULL, ptv);\r
+    } while (ret < 0 && (errno == EINTR || errno == EAGAIN));\r
+\r
+    if (ret < 0)\r
+       return errno;\r
+\r
+    /*\r
+     * Just in case one of the callbacks destroys an rfd or wfd we\r
+     * had yet to get round to, we must loop from the start every\r
+     * single time. Algorithmically irritating, but necessary\r
+     * unless we want to store the rfd structures in a heavyweight\r
+     * tree sorted by fd. And let's face it, if we cared about\r
+     * good algorithmic complexity it's not at all clear we'd be\r
+     * using select in the first place.\r
+     */\r
+    do {\r
+       for (wfd = sel->whead; wfd; wfd = wfd->next)\r
+           if (wfd->fd >= 0 && FD_ISSET(wfd->fd, &wset)) {\r
+               void *data;\r
+               size_t len;\r
+\r
+               FD_CLR(wfd->fd, &wset);\r
+               bufchain_prefix(&wfd->buf, &data, &len);\r
+               ret = write(wfd->fd, data, len);\r
+               assert(ret != 0);\r
+               if (ret < 0) {\r
+                   if (wfd->writeerr)\r
+                       wfd->writeerr(wfd, errno);\r
+               } else {\r
+                   bufchain_consume(&wfd->buf, len);\r
+                   if (wfd->written)\r
+                       wfd->written(wfd, bufchain_size(&wfd->buf));\r
+               }\r
+               break;\r
+           }\r
+    } while (wfd);\r
+    do {\r
+       for (rfd = sel->rhead; rfd; rfd = rfd->next) \r
+           if (rfd->fd >= 0 && !rfd->frozen && FD_ISSET(rfd->fd, &rset)) {\r
+               FD_CLR(rfd->fd, &rset);\r
+               ret = read(rfd->fd, buf, sizeof(buf));\r
+               if (ret < 0) {\r
+                   if (rfd->readerr)\r
+                       rfd->readerr(rfd, errno);\r
+               } else {\r
+                   if (rfd->readdata)\r
+                       rfd->readdata(rfd, buf, ret);\r
+               }\r
+               break;\r
+           }\r
+    } while (rfd);\r
+\r
+    return 0;\r
+}\r
diff --git a/putty/CONTRIB/CYGTERMD/SEL.H b/putty/CONTRIB/CYGTERMD/SEL.H
new file mode 100644 (file)
index 0000000..b350c48
--- /dev/null
@@ -0,0 +1,161 @@
+/*\r
+ * sel.h: subsystem to manage the grubby details of a select loop,\r
+ * buffering data to write, and performing the actual writes and\r
+ * reads.\r
+ */\r
+\r
+#ifndef FIXME_SEL_H\r
+#define FIXME_SEL_H\r
+\r
+typedef struct sel sel;\r
+typedef struct sel_wfd sel_wfd;\r
+typedef struct sel_rfd sel_rfd;\r
+\r
+/*\r
+ * Callback called when some data is written to a wfd. "bufsize"\r
+ * is the remaining quantity of data buffered in that wfd.\r
+ */\r
+typedef void (*sel_written_fn_t)(sel_wfd *wfd, size_t bufsize);\r
+\r
+/*\r
+ * Callback called when an error occurs on a wfd, preventing\r
+ * further writing to it. "error" is the errno value.\r
+ */\r
+typedef void (*sel_writeerr_fn_t)(sel_wfd *wfd, int error);\r
+\r
+/*\r
+ * Callback called when some data is read from an rfd. On EOF,\r
+ * this will be called with len==0.\r
+ */\r
+typedef void (*sel_readdata_fn_t)(sel_rfd *rfd, void *data, size_t len);\r
+\r
+/*\r
+ * Callback called when an error occurs on an rfd, preventing\r
+ * further reading from it. "error" is the errno value.\r
+ */\r
+typedef void (*sel_readerr_fn_t)(sel_rfd *rfd, int error);\r
+\r
+/*\r
+ * Create a sel structure, which will oversee a select loop.\r
+ * \r
+ * "ctx" is user-supplied data stored in the sel structure; it can\r
+ * be read and written with sel_get_ctx() and sel_set_ctx().\r
+ */\r
+sel *sel_new(void *ctx);\r
+\r
+/*\r
+ * Add a new fd for writing. Returns a sel_wfd which identifies\r
+ * that fd in the sel structure, e.g. for putting data into its\r
+ * output buffer.\r
+ * \r
+ * "ctx" is user-supplied data stored in the sel structure; it can\r
+ * be read and written with sel_wfd_get_ctx() and sel_wfd_set_ctx().\r
+ * \r
+ * "written" and "writeerr" are called from the event loop when\r
+ * things happen.\r
+ *\r
+ * The fd passed in can be -1, in which case it will be assumed to\r
+ * be unwritable at all times. An actual fd can be passed in later\r
+ * using sel_wfd_setfd.\r
+ */\r
+sel_wfd *sel_wfd_add(sel *sel, int fd,\r
+                    sel_written_fn_t written, sel_writeerr_fn_t writeerr,\r
+                    void *ctx);\r
+\r
+/*\r
+ * Add a new fd for reading. Returns a sel_rfd which identifies\r
+ * that fd in the sel structure.\r
+ * \r
+ * "ctx" is user-supplied data stored in the sel structure; it can\r
+ * be read and written with sel_rfd_get_ctx() and sel_rfd_set_ctx().\r
+ * \r
+ * "readdata" and "readerr" are called from the event loop when\r
+ * things happen. "ctx" is passed to both of them.\r
+ */\r
+sel_rfd *sel_rfd_add(sel *sel, int fd,\r
+                    sel_readdata_fn_t readdata, sel_readerr_fn_t readerr,\r
+                    void *ctx);\r
+\r
+/*\r
+ * Write data into the output buffer of a wfd. Returns the new\r
+ * size of the output buffer. (You can call it with len==0 if you\r
+ * just want to know the buffer size; in that situation data==NULL\r
+ * is also safe.)\r
+ */\r
+size_t sel_write(sel_wfd *wfd, const void *data, size_t len);\r
+\r
+/*\r
+ * Freeze and unfreeze an rfd. When frozen, sel will temporarily\r
+ * not attempt to read from it, but all its state is retained so\r
+ * it can be conveniently unfrozen later. (You might use this\r
+ * facility, for instance, if what you were doing with the\r
+ * incoming data could only accept it at a certain rate: freeze\r
+ * the rfd when you've got lots of backlog, and unfreeze it again\r
+ * when things get calmer.)\r
+ */\r
+void sel_rfd_freeze(sel_rfd *rfd);\r
+void sel_rfd_unfreeze(sel_rfd *rfd);\r
+\r
+/*\r
+ * Delete a wfd structure from its containing sel. Returns the\r
+ * underlying fd, which the client may now consider itself to own\r
+ * once more.\r
+ */\r
+int sel_wfd_delete(sel_wfd *wfd);\r
+\r
+/*\r
+ * Delete an rfd structure from its containing sel. Returns the\r
+ * underlying fd, which the client may now consider itself to own\r
+ * once more.\r
+ */\r
+int sel_rfd_delete(sel_rfd *rfd);\r
+\r
+/*\r
+ * NOT IMPLEMENTED YET: useful functions here might be ones which\r
+ * enumerated all the wfds/rfds in a sel structure in some\r
+ * fashion, so you could go through them and remove them all while\r
+ * doing sensible things to them. Or, at the very least, just\r
+ * return an arbitrary one of the wfds/rfds.\r
+ */\r
+\r
+/*\r
+ * Free a sel structure and all its remaining wfds and rfds.\r
+ */\r
+void sel_free(sel *sel);\r
+\r
+/*\r
+ * Read and write the ctx parameters in sel, sel_wfd and sel_rfd.\r
+ */\r
+void *sel_get_ctx(sel *sel);\r
+void sel_set_ctx(sel *sel, void *ctx);\r
+void *sel_wfd_get_ctx(sel_wfd *wfd);\r
+void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx);\r
+void *sel_rfd_get_ctx(sel_rfd *rfd);\r
+void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx);\r
+\r
+/*\r
+ * Run one iteration of the sel event loop, calling callbacks as\r
+ * necessary. Returns zero on success; in the event of a fatal\r
+ * error, returns the errno value.\r
+ * \r
+ * "timeout" is a value in microseconds to limit the length of the\r
+ * select call. Less than zero means to wait indefinitely.\r
+ */\r
+int sel_iterate(sel *sel, long timeout);\r
+\r
+/*\r
+ * Change the underlying fd in a wfd. If set to -1, no write\r
+ * attempts will take place and the wfd's buffer will simply store\r
+ * everything passed to sel_write(). If later set to something\r
+ * other than -1, all that buffered data will become eligible for\r
+ * real writing.\r
+ */\r
+void sel_wfd_setfd(sel_wfd *wfd, int fd);\r
+\r
+/*\r
+ * Change the underlying fd in a rfd. If set to -1, no read\r
+ * attempts will take place.\r
+ */\r
+void sel_rfd_setfd(sel_rfd *rfd, int fd);\r
+\r
+#endif /* FIXME_SEL_H */\r
diff --git a/putty/CONTRIB/CYGTERMD/TELNET.C b/putty/CONTRIB/CYGTERMD/TELNET.C
new file mode 100644 (file)
index 0000000..9602fd7
--- /dev/null
@@ -0,0 +1,570 @@
+/*\r
+ * Simple Telnet server code, adapted from PuTTY's own Telnet\r
+ * client code for use as a Cygwin local pty proxy.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include "sel.h"\r
+#include "telnet.h"\r
+#include "malloc.h"\r
+#include "pty.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+#define        IAC     255                    /* interpret as command: */\r
+#define        DONT    254                    /* you are not to use option */\r
+#define        DO      253                    /* please, you use option */\r
+#define        WONT    252                    /* I won't use option */\r
+#define        WILL    251                    /* I will use option */\r
+#define        SB      250                    /* interpret as subnegotiation */\r
+#define        SE      240                    /* end sub negotiation */\r
+\r
+#define GA      249                   /* you may reverse the line */\r
+#define EL      248                   /* erase the current line */\r
+#define EC      247                   /* erase the current character */\r
+#define        AYT     246                    /* are you there */\r
+#define        AO      245                    /* abort output--but let prog finish */\r
+#define        IP      244                    /* interrupt process--permanently */\r
+#define        BREAK   243                    /* break */\r
+#define DM      242                   /* data mark--for connect. cleaning */\r
+#define NOP     241                   /* nop */\r
+#define EOR     239                   /* end of record (transparent mode) */\r
+#define ABORT   238                   /* Abort process */\r
+#define SUSP    237                   /* Suspend process */\r
+#define xEOF    236                   /* End of file: EOF is already used... */\r
+\r
+#define TELOPTS(X) \\r
+    X(BINARY, 0)                       /* 8-bit data path */ \\r
+    X(ECHO, 1)                         /* echo */ \\r
+    X(RCP, 2)                          /* prepare to reconnect */ \\r
+    X(SGA, 3)                          /* suppress go ahead */ \\r
+    X(NAMS, 4)                         /* approximate message size */ \\r
+    X(STATUS, 5)                       /* give status */ \\r
+    X(TM, 6)                           /* timing mark */ \\r
+    X(RCTE, 7)                         /* remote controlled transmission and echo */ \\r
+    X(NAOL, 8)                         /* negotiate about output line width */ \\r
+    X(NAOP, 9)                         /* negotiate about output page size */ \\r
+    X(NAOCRD, 10)                      /* negotiate about CR disposition */ \\r
+    X(NAOHTS, 11)                      /* negotiate about horizontal tabstops */ \\r
+    X(NAOHTD, 12)                      /* negotiate about horizontal tab disposition */ \\r
+    X(NAOFFD, 13)                      /* negotiate about formfeed disposition */ \\r
+    X(NAOVTS, 14)                      /* negotiate about vertical tab stops */ \\r
+    X(NAOVTD, 15)                      /* negotiate about vertical tab disposition */ \\r
+    X(NAOLFD, 16)                      /* negotiate about output LF disposition */ \\r
+    X(XASCII, 17)                      /* extended ascic character set */ \\r
+    X(LOGOUT, 18)                      /* force logout */ \\r
+    X(BM, 19)                          /* byte macro */ \\r
+    X(DET, 20)                         /* data entry terminal */ \\r
+    X(SUPDUP, 21)                      /* supdup protocol */ \\r
+    X(SUPDUPOUTPUT, 22)                /* supdup output */ \\r
+    X(SNDLOC, 23)                      /* send location */ \\r
+    X(TTYPE, 24)                       /* terminal type */ \\r
+    X(EOR, 25)                         /* end or record */ \\r
+    X(TUID, 26)                        /* TACACS user identification */ \\r
+    X(OUTMRK, 27)                      /* output marking */ \\r
+    X(TTYLOC, 28)                      /* terminal location number */ \\r
+    X(3270REGIME, 29)                  /* 3270 regime */ \\r
+    X(X3PAD, 30)                       /* X.3 PAD */ \\r
+    X(NAWS, 31)                        /* window size */ \\r
+    X(TSPEED, 32)                      /* terminal speed */ \\r
+    X(LFLOW, 33)                       /* remote flow control */ \\r
+    X(LINEMODE, 34)                    /* Linemode option */ \\r
+    X(XDISPLOC, 35)                    /* X Display Location */ \\r
+    X(OLD_ENVIRON, 36)                 /* Old - Environment variables */ \\r
+    X(AUTHENTICATION, 37)              /* Authenticate */ \\r
+    X(ENCRYPT, 38)                     /* Encryption option */ \\r
+    X(NEW_ENVIRON, 39)                 /* New - Environment variables */ \\r
+    X(TN3270E, 40)                     /* TN3270 enhancements */ \\r
+    X(XAUTH, 41)                       \\r
+    X(CHARSET, 42)                     /* Character set */ \\r
+    X(RSP, 43)                         /* Remote serial port */ \\r
+    X(COM_PORT_OPTION, 44)             /* Com port control */ \\r
+    X(SLE, 45)                         /* Suppress local echo */ \\r
+    X(STARTTLS, 46)                    /* Start TLS */ \\r
+    X(KERMIT, 47)                      /* Automatic Kermit file transfer */ \\r
+    X(SEND_URL, 48)                    \\r
+    X(FORWARD_X, 49)                   \\r
+    X(PRAGMA_LOGON, 138)               \\r
+    X(SSPI_LOGON, 139)                 \\r
+    X(PRAGMA_HEARTBEAT, 140)           \\r
+    X(EXOPL, 255)                      /* extended-options-list */\r
+\r
+#define telnet_enum(x,y) TELOPT_##x = y,\r
+enum { TELOPTS(telnet_enum) dummy=0 };\r
+#undef telnet_enum\r
+\r
+#define        TELQUAL_IS      0              /* option is... */\r
+#define        TELQUAL_SEND    1              /* send option */\r
+#define        TELQUAL_INFO    2              /* ENVIRON: informational version of IS */\r
+#define BSD_VAR 1\r
+#define BSD_VALUE 0\r
+#define RFC_VAR 0\r
+#define RFC_VALUE 1\r
+\r
+#define CR 13\r
+#define LF 10\r
+#define NUL 0\r
+\r
+#define iswritable(x) ( (x) != IAC && (x) != CR )\r
+\r
+static char *telopt(int opt)\r
+{\r
+#define telnet_str(x,y) case TELOPT_##x: return #x;\r
+    switch (opt) {\r
+       TELOPTS(telnet_str)\r
+      default:\r
+       return "<unknown>";\r
+    }\r
+#undef telnet_str\r
+}\r
+\r
+static void telnet_size(void *handle, int width, int height);\r
+\r
+struct Opt {\r
+    int send;                         /* what we initially send */\r
+    int nsend;                        /* -ve send if requested to stop it */\r
+    int ack, nak;                     /* +ve and -ve acknowledgements */\r
+    int option;                               /* the option code */\r
+    int index;                        /* index into telnet->opt_states[] */\r
+    enum {\r
+       REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE\r
+    } initial_state;\r
+};\r
+\r
+enum {\r
+    OPTINDEX_NAWS,\r
+    OPTINDEX_TSPEED,\r
+    OPTINDEX_TTYPE,\r
+    OPTINDEX_OENV,\r
+    OPTINDEX_NENV,\r
+    OPTINDEX_ECHO,\r
+    OPTINDEX_WE_SGA,\r
+    OPTINDEX_THEY_SGA,\r
+    OPTINDEX_WE_BIN,\r
+    OPTINDEX_THEY_BIN,\r
+    NUM_OPTS\r
+};\r
+\r
+static const struct Opt o_naws =\r
+    { DO, DONT, WILL, WONT, TELOPT_NAWS, OPTINDEX_NAWS, REQUESTED };\r
+static const struct Opt o_ttype =\r
+    { DO, DONT, WILL, WONT, TELOPT_TTYPE, OPTINDEX_TTYPE, REQUESTED };\r
+static const struct Opt o_oenv =\r
+    { DO, DONT, WILL, WONT, TELOPT_OLD_ENVIRON, OPTINDEX_OENV, INACTIVE };\r
+static const struct Opt o_nenv =\r
+    { DO, DONT, WILL, WONT, TELOPT_NEW_ENVIRON, OPTINDEX_NENV, REQUESTED };\r
+static const struct Opt o_echo =\r
+    { WILL, WONT, DO, DONT, TELOPT_ECHO, OPTINDEX_ECHO, REQUESTED };\r
+static const struct Opt o_they_sga =\r
+    { DO, DONT, WILL, WONT, TELOPT_SGA, OPTINDEX_WE_SGA, REQUESTED };\r
+static const struct Opt o_we_sga =\r
+    { WILL, WONT, DO, DONT, TELOPT_SGA, OPTINDEX_THEY_SGA, REQUESTED };\r
+\r
+static const struct Opt *const opts[] = {\r
+    &o_echo, &o_we_sga, &o_they_sga, &o_naws, &o_ttype, &o_oenv, &o_nenv, NULL\r
+};\r
+\r
+struct telnet_tag {\r
+    int opt_states[NUM_OPTS];\r
+\r
+    int sb_opt, sb_len;\r
+    unsigned char *sb_buf;\r
+    int sb_size;\r
+\r
+    enum {\r
+       TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,\r
+           SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR\r
+    } state;\r
+\r
+    sel_wfd *net, *pty;\r
+\r
+    /*\r
+     * Options we must finish processing before launching the shell\r
+     */\r
+    int old_environ_done, new_environ_done, ttype_done;\r
+\r
+    /*\r
+     * Ready to start shell?\r
+     */\r
+    int shell_ok;\r
+    int envvarsize;\r
+    struct shell_data shdata;\r
+};\r
+\r
+#define TELNET_MAX_BACKLOG 4096\r
+\r
+#define SB_DELTA 1024\r
+\r
+static void send_opt(Telnet telnet, int cmd, int option)\r
+{\r
+    unsigned char b[3];\r
+\r
+    b[0] = IAC;\r
+    b[1] = cmd;\r
+    b[2] = option;\r
+    sel_write(telnet->net, (char *)b, 3);\r
+}\r
+\r
+static void deactivate_option(Telnet telnet, const struct Opt *o)\r
+{\r
+    if (telnet->opt_states[o->index] == REQUESTED ||\r
+       telnet->opt_states[o->index] == ACTIVE)\r
+       send_opt(telnet, o->nsend, o->option);\r
+    telnet->opt_states[o->index] = REALLY_INACTIVE;\r
+}\r
+\r
+/*\r
+ * Generate side effects of enabling or disabling an option.\r
+ */\r
+static void option_side_effects(Telnet telnet, const struct Opt *o, int enabled)\r
+{\r
+}\r
+\r
+static void activate_option(Telnet telnet, const struct Opt *o)\r
+{\r
+    if (o->option == TELOPT_NEW_ENVIRON ||\r
+       o->option == TELOPT_OLD_ENVIRON ||\r
+       o->option == TELOPT_TTYPE) {\r
+       char buf[6];\r
+       buf[0] = IAC;\r
+       buf[1] = SB;\r
+       buf[2] = o->option;\r
+       buf[3] = TELQUAL_SEND;\r
+       buf[4] = IAC;\r
+       buf[5] = SE;\r
+       sel_write(telnet->net, buf, 6);\r
+    }\r
+    option_side_effects(telnet, o, 1);\r
+}\r
+\r
+static void done_option(Telnet telnet, int option)\r
+{\r
+    if (option == TELOPT_OLD_ENVIRON)\r
+       telnet->old_environ_done = 1;\r
+    else if (option == TELOPT_NEW_ENVIRON)\r
+       telnet->new_environ_done = 1;\r
+    else if (option == TELOPT_TTYPE)\r
+       telnet->ttype_done = 1;\r
+\r
+    if (telnet->old_environ_done && telnet->new_environ_done &&\r
+       telnet->ttype_done) {\r
+       telnet->shell_ok = 1;\r
+    }\r
+}\r
+\r
+static void refused_option(Telnet telnet, const struct Opt *o)\r
+{\r
+    done_option(telnet, o->option);\r
+    if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON &&\r
+       telnet->opt_states[o_oenv.index] == INACTIVE) {\r
+       send_opt(telnet, WILL, TELOPT_OLD_ENVIRON);\r
+       telnet->opt_states[o_oenv.index] = REQUESTED;\r
+       telnet->old_environ_done = 0;\r
+    }\r
+    option_side_effects(telnet, o, 0);\r
+}\r
+\r
+static void proc_rec_opt(Telnet telnet, int cmd, int option)\r
+{\r
+    const struct Opt *const *o;\r
+\r
+    for (o = opts; *o; o++) {\r
+       if ((*o)->option == option && (*o)->ack == cmd) {\r
+           switch (telnet->opt_states[(*o)->index]) {\r
+             case REQUESTED:\r
+               telnet->opt_states[(*o)->index] = ACTIVE;\r
+               activate_option(telnet, *o);\r
+               break;\r
+             case ACTIVE:\r
+               break;\r
+             case INACTIVE:\r
+               telnet->opt_states[(*o)->index] = ACTIVE;\r
+               send_opt(telnet, (*o)->send, option);\r
+               activate_option(telnet, *o);\r
+               break;\r
+             case REALLY_INACTIVE:\r
+               send_opt(telnet, (*o)->nsend, option);\r
+               break;\r
+           }\r
+           return;\r
+       } else if ((*o)->option == option && (*o)->nak == cmd) {\r
+           switch (telnet->opt_states[(*o)->index]) {\r
+             case REQUESTED:\r
+               telnet->opt_states[(*o)->index] = INACTIVE;\r
+               refused_option(telnet, *o);\r
+               break;\r
+             case ACTIVE:\r
+               telnet->opt_states[(*o)->index] = INACTIVE;\r
+               send_opt(telnet, (*o)->nsend, option);\r
+               option_side_effects(telnet, *o, 0);\r
+               break;\r
+             case INACTIVE:\r
+             case REALLY_INACTIVE:\r
+               break;\r
+           }\r
+           return;\r
+       }\r
+    }\r
+    /*\r
+     * If we reach here, the option was one we weren't prepared to\r
+     * cope with. If the request was positive (WILL or DO), we send\r
+     * a negative ack to indicate refusal. If the request was\r
+     * negative (WONT / DONT), we must do nothing.\r
+     */\r
+    if (cmd == WILL || cmd == DO)\r
+        send_opt(telnet, (cmd == WILL ? DONT : WONT), option);\r
+}\r
+\r
+static void process_subneg(Telnet telnet)\r
+{\r
+    unsigned char b[2048], *p, *q;\r
+    int var, value, n;\r
+    char *e;\r
+\r
+    switch (telnet->sb_opt) {\r
+      case TELOPT_OLD_ENVIRON:\r
+      case TELOPT_NEW_ENVIRON:\r
+       if (telnet->sb_buf[0] == TELQUAL_IS) {\r
+           if (telnet->sb_opt == TELOPT_NEW_ENVIRON) {\r
+               var = RFC_VAR;\r
+               value = RFC_VALUE;\r
+           } else {\r
+               if (telnet->sb_len > 1 && !(telnet->sb_buf[0] &~ 1)) {\r
+                   var = telnet->sb_buf[0];\r
+                   value = BSD_VAR ^ BSD_VALUE ^ var;\r
+               } else {\r
+                   var = BSD_VAR;\r
+                   value = BSD_VALUE;\r
+               }\r
+           }\r
+       }\r
+       n = 1;\r
+       while (n < telnet->sb_len && telnet->sb_buf[n] == var) {\r
+           int varpos, varlen, valpos, vallen;\r
+           char *result;\r
+\r
+           varpos = ++n;\r
+           while (n < telnet->sb_len && telnet->sb_buf[n] != value)\r
+               n++;\r
+           if (n == telnet->sb_len)\r
+               break;\r
+           varlen = n - varpos;\r
+           valpos = ++n;\r
+           while (n < telnet->sb_len && telnet->sb_buf[n] != var)\r
+               n++;\r
+           vallen = n - valpos;\r
+\r
+           result = snewn(varlen + vallen + 2, char);\r
+           sprintf(result, "%.*s=%.*s",\r
+                   varlen, telnet->sb_buf+varpos,\r
+                   vallen, telnet->sb_buf+valpos);\r
+           if (telnet->shdata.nenvvars >= telnet->envvarsize) {\r
+               telnet->envvarsize = telnet->shdata.nenvvars * 3 / 2 + 16;\r
+               telnet->shdata.envvars = sresize(telnet->shdata.envvars,\r
+                                                telnet->envvarsize, char *);\r
+           }\r
+           telnet->shdata.envvars[telnet->shdata.nenvvars++] = result;\r
+       }\r
+       done_option(telnet, telnet->sb_opt);\r
+       break;\r
+      case TELOPT_TTYPE:\r
+       if (telnet->sb_len >= 1 && telnet->sb_buf[0] == TELQUAL_IS) {\r
+           telnet->shdata.termtype = snewn(5 + telnet->sb_len, char);\r
+            strcpy(telnet->shdata.termtype, "TERM=");\r
+            for (n = 0; n < telnet->sb_len-1; n++) {\r
+                char c = telnet->sb_buf[n+1];\r
+                if (c >= 'A' && c <= 'Z')\r
+                    c = c + 'a' - 'A';\r
+                telnet->shdata.termtype[n+5] = c;\r
+            }\r
+           telnet->shdata.termtype[telnet->sb_len+5-1] = '\0';\r
+       }\r
+       done_option(telnet, telnet->sb_opt);\r
+       break;\r
+      case TELOPT_NAWS:\r
+       if (telnet->sb_len == 4) {\r
+           int w, h;\r
+           w = (unsigned char)telnet->sb_buf[0];\r
+           w = (w << 8) | (unsigned char)telnet->sb_buf[1];\r
+           h = (unsigned char)telnet->sb_buf[2];\r
+           h = (h << 8) | (unsigned char)telnet->sb_buf[3];\r
+           pty_resize(w, h);\r
+       }\r
+       break;\r
+    }\r
+}\r
+\r
+void telnet_from_net(Telnet telnet, char *buf, int len)\r
+{\r
+    while (len--) {\r
+       int c = (unsigned char) *buf++;\r
+\r
+       switch (telnet->state) {\r
+         case TOP_LEVEL:\r
+         case SEENCR:\r
+           /*\r
+            * PuTTY sends Telnet's new line sequence (CR LF on\r
+            * the wire) in response to the return key. We must\r
+            * therefore treat that as equivalent to CR NUL, and\r
+            * send CR to the pty.\r
+            */\r
+           if ((c == NUL || c == '\n') && telnet->state == SEENCR)\r
+               telnet->state = TOP_LEVEL;\r
+           else if (c == IAC)\r
+               telnet->state = SEENIAC;\r
+           else {\r
+               char cc = c;\r
+               sel_write(telnet->pty, &cc, 1);\r
+\r
+               telnet->state = SEENCR;\r
+           }\r
+           break;\r
+         case SEENIAC:\r
+           if (c == DO)\r
+               telnet->state = SEENDO;\r
+           else if (c == DONT)\r
+               telnet->state = SEENDONT;\r
+           else if (c == WILL)\r
+               telnet->state = SEENWILL;\r
+           else if (c == WONT)\r
+               telnet->state = SEENWONT;\r
+           else if (c == SB)\r
+               telnet->state = SEENSB;\r
+           else if (c == DM)\r
+               telnet->state = TOP_LEVEL;\r
+           else {\r
+               /* ignore everything else; print it if it's IAC */\r
+               if (c == IAC) {\r
+                   char cc = c;\r
+                   sel_write(telnet->pty, &cc, 1);\r
+               }\r
+               telnet->state = TOP_LEVEL;\r
+           }\r
+           break;\r
+         case SEENWILL:\r
+           proc_rec_opt(telnet, WILL, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENWONT:\r
+           proc_rec_opt(telnet, WONT, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENDO:\r
+           proc_rec_opt(telnet, DO, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENDONT:\r
+           proc_rec_opt(telnet, DONT, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENSB:\r
+           telnet->sb_opt = c;\r
+           telnet->sb_len = 0;\r
+           telnet->state = SUBNEGOT;\r
+           break;\r
+         case SUBNEGOT:\r
+           if (c == IAC)\r
+               telnet->state = SUBNEG_IAC;\r
+           else {\r
+             subneg_addchar:\r
+               if (telnet->sb_len >= telnet->sb_size) {\r
+                   telnet->sb_size += SB_DELTA;\r
+                   telnet->sb_buf = sresize(telnet->sb_buf, telnet->sb_size,\r
+                                            unsigned char);\r
+               }\r
+               telnet->sb_buf[telnet->sb_len++] = c;\r
+               telnet->state = SUBNEGOT;       /* in case we came here by goto */\r
+           }\r
+           break;\r
+         case SUBNEG_IAC:\r
+           if (c != SE)\r
+               goto subneg_addchar;   /* yes, it's a hack, I know, but... */\r
+           else {\r
+               process_subneg(telnet);\r
+               telnet->state = TOP_LEVEL;\r
+           }\r
+           break;\r
+       }\r
+    }\r
+}\r
+\r
+Telnet telnet_new(sel_wfd *net, sel_wfd *pty)\r
+{\r
+    Telnet telnet;\r
+\r
+    telnet = snew(struct telnet_tag);\r
+    telnet->sb_buf = NULL;\r
+    telnet->sb_size = 0;\r
+    telnet->state = TOP_LEVEL;\r
+    telnet->net = net;\r
+    telnet->pty = pty;\r
+    telnet->shdata.envvars = NULL;\r
+    telnet->shdata.nenvvars = telnet->envvarsize = 0;\r
+    telnet->shdata.termtype = NULL;\r
+\r
+    /*\r
+     * Initialise option states.\r
+     */\r
+    {\r
+       const struct Opt *const *o;\r
+\r
+       for (o = opts; *o; o++) {\r
+           telnet->opt_states[(*o)->index] = (*o)->initial_state;\r
+           if (telnet->opt_states[(*o)->index] == REQUESTED)\r
+               send_opt(telnet, (*o)->send, (*o)->option);\r
+       }\r
+    }\r
+\r
+    telnet->old_environ_done = 1;      /* initially don't want to bother */\r
+    telnet->new_environ_done = 0;\r
+    telnet->ttype_done = 0;\r
+    telnet->shell_ok = 0;\r
+\r
+    return telnet;\r
+}\r
+\r
+void telnet_free(Telnet telnet)\r
+{\r
+    sfree(telnet->sb_buf);\r
+    sfree(telnet);\r
+}\r
+\r
+void telnet_from_pty(Telnet telnet, char *buf, int len)\r
+{\r
+    unsigned char *p, *end;\r
+    static const unsigned char iac[2] = { IAC, IAC };\r
+    static const unsigned char cr[2] = { CR, NUL };\r
+#if 0\r
+    static const unsigned char nl[2] = { CR, LF };\r
+#endif\r
+\r
+    p = (unsigned char *)buf;\r
+    end = (unsigned char *)(buf + len);\r
+    while (p < end) {\r
+       unsigned char *q = p;\r
+\r
+       while (p < end && iswritable(*p))\r
+           p++;\r
+       sel_write(telnet->net, (char *)q, p - q);\r
+\r
+       while (p < end && !iswritable(*p)) {\r
+           sel_write(telnet->net, (char *)(*p == IAC ? iac : cr), 2);\r
+           p++;\r
+       }\r
+    }\r
+}\r
+\r
+int telnet_shell_ok(Telnet telnet, struct shell_data *shdata)\r
+{\r
+    if (telnet->shell_ok)\r
+       *shdata = telnet->shdata;      /* structure copy */\r
+    return telnet->shell_ok;\r
+}\r
diff --git a/putty/CONTRIB/CYGTERMD/TELNET.H b/putty/CONTRIB/CYGTERMD/TELNET.H
new file mode 100644 (file)
index 0000000..3ddb621
--- /dev/null
@@ -0,0 +1,41 @@
+/*\r
+ * Header declaring Telnet-handling functions.\r
+ */\r
+\r
+#ifndef FIXME_TELNET_H\r
+#define FIXME_TELNET_H\r
+\r
+#include "sel.h"\r
+\r
+typedef struct telnet_tag *Telnet;\r
+\r
+struct shell_data {\r
+    char **envvars;                   /* array of "VAR=value" terms */\r
+    int nenvvars;\r
+    char *termtype;\r
+};\r
+\r
+/*\r
+ * Create and destroy a Telnet structure.\r
+ */\r
+Telnet telnet_new(sel_wfd *net, sel_wfd *pty);\r
+void telnet_free(Telnet telnet);\r
+\r
+/*\r
+ * Process data read from the pty.\r
+ */\r
+void telnet_from_pty(Telnet telnet, char *buf, int len);\r
+\r
+/*\r
+ * Process Telnet protocol data received from the network.\r
+ */\r
+void telnet_from_net(Telnet telnet, char *buf, int len);\r
+\r
+/*\r
+ * Return true if pre-shell-startup negotiations are complete and\r
+ * it's safe to start the shell subprocess now. On a true return,\r
+ * also fills in the shell_data structure.\r
+ */\r
+int telnet_shell_ok(Telnet telnet, struct shell_data *shdata);\r
+\r
+#endif /* FIXME_TELNET_H */\r
diff --git a/putty/CONTRIB/KH2REG.PY b/putty/CONTRIB/KH2REG.PY
new file mode 100644 (file)
index 0000000..fe78267
--- /dev/null
@@ -0,0 +1,157 @@
+#! /usr/bin/env python\r
+\r
+# $Id: kh2reg.py 8519 2009-04-26 23:44:28Z jacob $\r
+# Convert OpenSSH known_hosts and known_hosts2 files to "new format" PuTTY\r
+# host keys.\r
+#   usage:\r
+#     kh2reg.py [ --win ] known_hosts1 2 3 4 ... > hosts.reg\r
+#       Creates a Windows .REG file (double-click to install).\r
+#     kh2reg.py --unix    known_hosts1 2 3 4 ... > sshhostkeys\r
+#       Creates data suitable for storing in ~/.putty/sshhostkeys (Unix).\r
+# Line endings are someone else's problem as is traditional.\r
+# Developed for Python 1.5.2.\r
+\r
+import fileinput\r
+import base64\r
+import struct\r
+import string\r
+import re\r
+import sys\r
+import getopt\r
+\r
+def winmungestr(s):\r
+    "Duplicate of PuTTY's mungestr() in winstore.c:1.10 for Registry keys"\r
+    candot = 0\r
+    r = ""\r
+    for c in s:\r
+        if c in ' \*?%~' or ord(c)<ord(' ') or (c == '.' and not candot):\r
+            r = r + ("%%%02X" % ord(c))\r
+        else:\r
+            r = r + c\r
+        candot = 1\r
+    return r\r
+\r
+def strtolong(s):\r
+    "Convert arbitrary-length big-endian binary data to a Python long"\r
+    bytes = struct.unpack(">%luB" % len(s), s)\r
+    return reduce ((lambda a, b: (long(a) << 8) + long(b)), bytes)\r
+\r
+def longtohex(n):\r
+    """Convert long int to lower-case hex.\r
+\r
+    Ick, Python (at least in 1.5.2) doesn't appear to have a way to\r
+    turn a long int into an unadorned hex string -- % gets upset if the\r
+    number is too big, and raw hex() uses uppercase (sometimes), and\r
+    adds unwanted "0x...L" around it."""\r
+\r
+    plain=string.lower(re.match(r"0x([0-9A-Fa-f]*)l?$", hex(n), re.I).group(1))\r
+    return "0x" + plain\r
+\r
+output_type = 'windows'\r
+\r
+try:\r
+    optlist, args = getopt.getopt(sys.argv[1:], '', [ 'win', 'unix' ])\r
+    if filter(lambda x: x[0] == '--unix', optlist):\r
+        output_type = 'unix'\r
+except getopt.error, e:\r
+    sys.stderr.write(str(e) + "\n")\r
+    sys.exit(1)\r
+\r
+if output_type == 'windows':\r
+    # Output REG file header.\r
+    sys.stdout.write("""REGEDIT4\r
+\r
+[HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys]\r
+""")\r
+\r
+# Now process all known_hosts input.\r
+for line in fileinput.input(args):\r
+\r
+    try:\r
+        # Remove leading/trailing whitespace (should zap CR and LF)\r
+        line = string.strip (line)\r
+\r
+        # Skip blanks and comments\r
+        if line == '' or line[0] == '#':\r
+            raise "Skipping input line"\r
+\r
+        # Split line on spaces.\r
+        fields = string.split (line, ' ')\r
+\r
+        # Common fields\r
+        hostpat = fields[0]\r
+        magicnumbers = []   # placeholder\r
+        keytype = ""        # placeholder\r
+\r
+        # Grotty heuristic to distinguish known_hosts from known_hosts2:\r
+        # is second field entirely decimal digits?\r
+        if re.match (r"\d*$", fields[1]):\r
+\r
+            # Treat as SSH-1-type host key.\r
+            # Format: hostpat bits10 exp10 mod10 comment...\r
+            # (PuTTY doesn't store the number of bits.)\r
+            magicnumbers = map (long, fields[2:4])\r
+            keytype = "rsa"\r
+\r
+        else:\r
+\r
+            # Treat as SSH-2-type host key.\r
+            # Format: hostpat keytype keyblob64 comment...\r
+            sshkeytype, blob = fields[1], base64.decodestring (fields[2])\r
+\r
+            # 'blob' consists of a number of\r
+            #   uint32    N (big-endian)\r
+            #   uint8[N]  field_data\r
+            subfields = []\r
+            while blob:\r
+                sizefmt = ">L"\r
+                (size,) = struct.unpack (sizefmt, blob[0:4])\r
+                size = int(size)   # req'd for slicage\r
+                (data,) = struct.unpack (">%lus" % size, blob[4:size+4])\r
+                subfields.append(data)\r
+                blob = blob [struct.calcsize(sizefmt) + size : ]\r
+\r
+            # The first field is keytype again, and the rest we can treat as\r
+            # an opaque list of bignums (same numbers and order as stored\r
+            # by PuTTY). (currently embedded keytype is ignored entirely)\r
+            magicnumbers = map (strtolong, subfields[1:])\r
+\r
+            # Translate key type into something PuTTY can use.\r
+            if   sshkeytype == "ssh-rsa":   keytype = "rsa2"\r
+            elif sshkeytype == "ssh-dss":   keytype = "dss"\r
+            else:\r
+                raise "Unknown SSH key type", sshkeytype\r
+\r
+        # Now print out one line per host pattern, discarding wildcards.\r
+        for host in string.split (hostpat, ','):\r
+            if re.search (r"[*?!]", host):\r
+                sys.stderr.write("Skipping wildcard host pattern '%s'\n"\r
+                                 % host)\r
+                continue\r
+            elif re.match (r"\|", host):\r
+                sys.stderr.write("Skipping hashed hostname '%s'\n" % host)\r
+                continue\r
+            else:\r
+                m = re.match (r"\[([^]]*)\]:(\d*)$", host)\r
+                if m:\r
+                    (host, port) = m.group(1,2)\r
+                    port = int(port)\r
+                else:\r
+                    port = 22\r
+                # Slightly bizarre output key format: 'type@port:hostname'\r
+                # XXX: does PuTTY do anything useful with literal IP[v4]s?\r
+                key = keytype + ("@%d:%s" % (port, host))\r
+                value = string.join (map (longtohex, magicnumbers), ',')\r
+                if output_type == 'unix':\r
+                    # Unix format.\r
+                    sys.stdout.write('%s %s\n' % (key, value))\r
+                else:\r
+                    # Windows format.\r
+                    # XXX: worry about double quotes?\r
+                    sys.stdout.write("\"%s\"=\"%s\"\n"\r
+                                     % (winmungestr(key), value))\r
+\r
+    except "Unknown SSH key type", k:\r
+        sys.stderr.write("Unknown SSH key type '%s', skipping\n" % k)\r
+    except "Skipping input line":\r
+        pass\r
diff --git a/putty/CPROXY.C b/putty/CPROXY.C
new file mode 100644 (file)
index 0000000..5537fca
--- /dev/null
@@ -0,0 +1,190 @@
+/*\r
+ * Routines to do cryptographic interaction with proxies in PuTTY.\r
+ * This is in a separate module from proxy.c, so that it can be\r
+ * conveniently removed in PuTTYtel by replacing this module with\r
+ * the stub version nocproxy.c.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <ctype.h>\r
+#include <string.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "putty.h"\r
+#include "ssh.h" /* For MD5 support */\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+static void hmacmd5_chap(const unsigned char *challenge, int challen,\r
+                        const char *passwd, unsigned char *response)\r
+{\r
+    void *hmacmd5_ctx;\r
+    int pwlen;\r
+\r
+    hmacmd5_ctx = hmacmd5_make_context();\r
+\r
+    pwlen = strlen(passwd);\r
+    if (pwlen>64) {\r
+       unsigned char md5buf[16];\r
+       MD5Simple(passwd, pwlen, md5buf);\r
+       hmacmd5_key(hmacmd5_ctx, md5buf, 16);\r
+    } else {\r
+       hmacmd5_key(hmacmd5_ctx, passwd, pwlen);\r
+    }\r
+\r
+    hmacmd5_do_hmac(hmacmd5_ctx, challenge, challen, response);\r
+    hmacmd5_free_context(hmacmd5_ctx);\r
+}\r
+\r
+void proxy_socks5_offerencryptedauth(char *command, int *len)\r
+{\r
+    command[*len] = 0x03; /* CHAP */\r
+    (*len)++;\r
+}\r
+\r
+int proxy_socks5_handlechap (Proxy_Socket p)\r
+{\r
+\r
+    /* CHAP authentication reply format:\r
+     *  version number (1 bytes) = 1\r
+     *  number of commands (1 byte)\r
+     *\r
+     * For each command:\r
+     *  command identifier (1 byte)\r
+     *  data length (1 byte)\r
+     */\r
+    unsigned char data[260];\r
+    unsigned char outbuf[20];\r
+\r
+    while(p->chap_num_attributes == 0 ||\r
+         p->chap_num_attributes_processed < p->chap_num_attributes) {\r
+       if (p->chap_num_attributes == 0 ||\r
+           p->chap_current_attribute == -1) {\r
+           /* CHAP normally reads in two bytes, either at the\r
+            * beginning or for each attribute/value pair.  But if\r
+            * we're waiting for the value's data, we might not want\r
+            * to read 2 bytes.\r
+            */\r
\r
+           if (bufchain_size(&p->pending_input_data) < 2)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 2);\r
+           bufchain_consume(&p->pending_input_data, 2);\r
+       }\r
+\r
+       if (p->chap_num_attributes == 0) {\r
+           /* If there are no attributes, this is our first msg\r
+            * with the server, where we negotiate version and \r
+            * number of attributes\r
+            */\r
+           if (data[0] != 0x01) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy wants"\r
+                            " a different CHAP version",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           if (data[1] == 0x00) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy won't"\r
+                            " negotiate CHAP with us",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           p->chap_num_attributes = data[1];\r
+       } else {\r
+           if (p->chap_current_attribute == -1) {\r
+               /* We have to read in each attribute/value pair -\r
+                * those we don't understand can be ignored, but\r
+                * there are a few we'll need to handle.\r
+                */\r
+               p->chap_current_attribute = data[0];\r
+               p->chap_current_datalen = data[1];\r
+           }\r
+           if (bufchain_size(&p->pending_input_data) <\r
+               p->chap_current_datalen)\r
+               return 1;              /* not got everything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data,\r
+                          p->chap_current_datalen);\r
+\r
+           bufchain_consume(&p->pending_input_data,\r
+                            p->chap_current_datalen);\r
+\r
+           switch (p->chap_current_attribute) {\r
+             case 0x00:\r
+               /* Successful authentication */\r
+               if (data[0] == 0x00)\r
+                   p->state = 2;\r
+               else {\r
+                   plug_closing(p->plug, "Proxy error: SOCKS proxy"\r
+                                " refused CHAP authentication",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   return 1;\r
+               }\r
+             break;\r
+             case 0x03:\r
+               outbuf[0] = 0x01; /* Version */\r
+               outbuf[1] = 0x01; /* One attribute */\r
+               outbuf[2] = 0x04; /* Response */\r
+               outbuf[3] = 0x10; /* Length */\r
+               hmacmd5_chap(data, p->chap_current_datalen,\r
+                            p->cfg.proxy_password, &outbuf[4]);\r
+               sk_write(p->sub_socket, (char *)outbuf, 20);\r
+             break;\r
+             case 0x11:\r
+               /* Chose a protocol */\r
+               if (data[0] != 0x85) {\r
+                   plug_closing(p->plug, "Proxy error: Server chose "\r
+                                "CHAP of other than HMAC-MD5 but we "\r
+                                "didn't offer it!",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   return 1;\r
+               }\r
+             break;\r
+           }\r
+           p->chap_current_attribute = -1;\r
+           p->chap_num_attributes_processed++;\r
+       }\r
+       if (p->state == 8 &&\r
+           p->chap_num_attributes_processed >= p->chap_num_attributes) {\r
+           p->chap_num_attributes = 0;\r
+           p->chap_num_attributes_processed = 0;\r
+           p->chap_current_datalen = 0;\r
+       }\r
+    }\r
+    return 0;\r
+}\r
+\r
+int proxy_socks5_selectchap(Proxy_Socket p)\r
+{\r
+    if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+       char chapbuf[514];\r
+       int ulen;\r
+       chapbuf[0] = '\x01'; /* Version */\r
+       chapbuf[1] = '\x02'; /* Number of attributes sent */\r
+       chapbuf[2] = '\x11'; /* First attribute - algorithms list */\r
+       chapbuf[3] = '\x01'; /* Only one CHAP algorithm */\r
+       chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */\r
+       chapbuf[5] = '\x02'; /* Second attribute - username */\r
+\r
+       ulen = strlen(p->cfg.proxy_username);\r
+       if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;\r
+\r
+       chapbuf[6] = ulen;\r
+       memcpy(chapbuf+7, p->cfg.proxy_username, ulen);\r
+\r
+       sk_write(p->sub_socket, chapbuf, ulen + 7);\r
+       p->chap_num_attributes = 0;\r
+       p->chap_num_attributes_processed = 0;\r
+       p->chap_current_attribute = -1;\r
+       p->chap_current_datalen = 0;\r
+\r
+       p->state = 8;\r
+    } else \r
+       plug_closing(p->plug, "Proxy error: Server chose "\r
+                    "CHAP authentication but we didn't offer it!",\r
+                PROXY_ERROR_GENERAL, 0);\r
+    return 1;\r
+}\r
diff --git a/putty/DIALOG.C b/putty/DIALOG.C
new file mode 100644 (file)
index 0000000..55bece8
--- /dev/null
@@ -0,0 +1,595 @@
+/*\r
+ * dialog.c - a reasonably platform-independent mechanism for\r
+ * describing dialog boxes.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <limits.h>\r
+#include <stdarg.h>\r
+#include <stdlib.h>\r
+\r
+#define DEFINE_INTORPTR_FNS\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+\r
+int ctrl_path_elements(char *path)\r
+{\r
+    int i = 1;\r
+    while (*path) {\r
+       if (*path == '/') i++;\r
+       path++;\r
+    }\r
+    return i;\r
+}\r
+\r
+/* Return the number of matching path elements at the starts of p1 and p2,\r
+ * or INT_MAX if the paths are identical. */\r
+int ctrl_path_compare(char *p1, char *p2)\r
+{\r
+    int i = 0;\r
+    while (*p1 || *p2) {\r
+       if ((*p1 == '/' || *p1 == '\0') &&\r
+           (*p2 == '/' || *p2 == '\0'))\r
+           i++;                       /* a whole element matches, ooh */\r
+       if (*p1 != *p2)\r
+           return i;                  /* mismatch */\r
+       p1++, p2++;\r
+    }\r
+    return INT_MAX;                   /* exact match */\r
+}\r
+\r
+struct controlbox *ctrl_new_box(void)\r
+{\r
+    struct controlbox *ret = snew(struct controlbox);\r
+\r
+    ret->nctrlsets = ret->ctrlsetsize = 0;\r
+    ret->ctrlsets = NULL;\r
+    ret->nfrees = ret->freesize = 0;\r
+    ret->frees = NULL;\r
+\r
+    return ret;\r
+}\r
+\r
+void ctrl_free_box(struct controlbox *b)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < b->nctrlsets; i++) {\r
+       ctrl_free_set(b->ctrlsets[i]);\r
+    }\r
+    for (i = 0; i < b->nfrees; i++)\r
+       sfree(b->frees[i]);\r
+    sfree(b->ctrlsets);\r
+    sfree(b->frees);\r
+    sfree(b);\r
+}\r
+\r
+void ctrl_free_set(struct controlset *s)\r
+{\r
+    int i;\r
+\r
+    sfree(s->pathname);\r
+    sfree(s->boxname);\r
+    sfree(s->boxtitle);\r
+    for (i = 0; i < s->ncontrols; i++) {\r
+       ctrl_free(s->ctrls[i]);\r
+    }\r
+    sfree(s->ctrls);\r
+    sfree(s);\r
+}\r
+\r
+/*\r
+ * Find the index of first controlset in a controlbox for a given\r
+ * path. If that path doesn't exist, return the index where it\r
+ * should be inserted.\r
+ */\r
+static int ctrl_find_set(struct controlbox *b, char *path, int start)\r
+{\r
+    int i, last, thisone;\r
+\r
+    last = 0;\r
+    for (i = 0; i < b->nctrlsets; i++) {\r
+       thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname);\r
+       /*\r
+        * If `start' is true and there exists a controlset with\r
+        * exactly the path we've been given, we should return the\r
+        * index of the first such controlset we find. Otherwise,\r
+        * we should return the index of the first entry in which\r
+        * _fewer_ path elements match than they did last time.\r
+        */\r
+       if ((start && thisone == INT_MAX) || thisone < last)\r
+           return i;\r
+       last = thisone;\r
+    }\r
+    return b->nctrlsets;              /* insert at end */\r
+}\r
+\r
+/*\r
+ * Find the index of next controlset in a controlbox for a given\r
+ * path, or -1 if no such controlset exists. If -1 is passed as\r
+ * input, finds the first.\r
+ */\r
+int ctrl_find_path(struct controlbox *b, char *path, int index)\r
+{\r
+    if (index < 0)\r
+       index = ctrl_find_set(b, path, 1);\r
+    else\r
+       index++;\r
+\r
+    if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname))\r
+       return index;\r
+    else\r
+       return -1;\r
+}\r
+\r
+/* Set up a panel title. */\r
+struct controlset *ctrl_settitle(struct controlbox *b,\r
+                                char *path, char *title)\r
+{\r
+    \r
+    struct controlset *s = snew(struct controlset);\r
+    int index = ctrl_find_set(b, path, 1);\r
+    s->pathname = dupstr(path);\r
+    s->boxname = NULL;\r
+    s->boxtitle = dupstr(title);\r
+    s->ncontrols = s->ctrlsize = 0;\r
+    s->ncolumns = 0;                  /* this is a title! */\r
+    s->ctrls = NULL;\r
+    if (b->nctrlsets >= b->ctrlsetsize) {\r
+       b->ctrlsetsize = b->nctrlsets + 32;\r
+       b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);\r
+    }\r
+    if (index < b->nctrlsets)\r
+       memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],\r
+               (b->nctrlsets-index) * sizeof(*b->ctrlsets));\r
+    b->ctrlsets[index] = s;\r
+    b->nctrlsets++;\r
+    return s;\r
+}\r
+\r
+/* Retrieve a pointer to a controlset, creating it if absent. */\r
+struct controlset *ctrl_getset(struct controlbox *b,\r
+                              char *path, char *name, char *boxtitle)\r
+{\r
+    struct controlset *s;\r
+    int index = ctrl_find_set(b, path, 1);\r
+    while (index < b->nctrlsets &&\r
+          !strcmp(b->ctrlsets[index]->pathname, path)) {\r
+       if (b->ctrlsets[index]->boxname &&\r
+           !strcmp(b->ctrlsets[index]->boxname, name))\r
+           return b->ctrlsets[index];\r
+       index++;\r
+    }\r
+    s = snew(struct controlset);\r
+    s->pathname = dupstr(path);\r
+    s->boxname = dupstr(name);\r
+    s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL;\r
+    s->ncolumns = 1;\r
+    s->ncontrols = s->ctrlsize = 0;\r
+    s->ctrls = NULL;\r
+    if (b->nctrlsets >= b->ctrlsetsize) {\r
+       b->ctrlsetsize = b->nctrlsets + 32;\r
+       b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);\r
+    }\r
+    if (index < b->nctrlsets)\r
+       memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],\r
+               (b->nctrlsets-index) * sizeof(*b->ctrlsets));\r
+    b->ctrlsets[index] = s;\r
+    b->nctrlsets++;\r
+    return s;\r
+}\r
+\r
+/* Allocate some private data in a controlbox. */\r
+void *ctrl_alloc(struct controlbox *b, size_t size)\r
+{\r
+    void *p;\r
+    /*\r
+     * This is an internal allocation routine, so it's allowed to\r
+     * use smalloc directly.\r
+     */\r
+    p = smalloc(size);\r
+    if (b->nfrees >= b->freesize) {\r
+       b->freesize = b->nfrees + 32;\r
+       b->frees = sresize(b->frees, b->freesize, void *);\r
+    }\r
+    b->frees[b->nfrees++] = p;\r
+    return p;\r
+}\r
+\r
+static union control *ctrl_new(struct controlset *s, int type,\r
+                              intorptr helpctx, handler_fn handler,\r
+                              intorptr context)\r
+{\r
+    union control *c = snew(union control);\r
+    if (s->ncontrols >= s->ctrlsize) {\r
+       s->ctrlsize = s->ncontrols + 32;\r
+       s->ctrls = sresize(s->ctrls, s->ctrlsize, union control *);\r
+    }\r
+    s->ctrls[s->ncontrols++] = c;\r
+    /*\r
+     * Fill in the standard fields.\r
+     */\r
+    c->generic.type = type;\r
+    c->generic.tabdelay = 0;\r
+    c->generic.column = COLUMN_FIELD(0, s->ncolumns);\r
+    c->generic.helpctx = helpctx;\r
+    c->generic.handler = handler;\r
+    c->generic.context = context;\r
+    c->generic.label = NULL;\r
+    return c;\r
+}\r
+\r
+/* `ncolumns' is followed by that many percentages, as integers. */\r
+union control *ctrl_columns(struct controlset *s, int ncolumns, ...)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL));\r
+    assert(s->ncolumns == 1 || ncolumns == 1);\r
+    c->columns.ncols = ncolumns;\r
+    s->ncolumns = ncolumns;\r
+    if (ncolumns == 1) {\r
+       c->columns.percentages = NULL;\r
+    } else {\r
+       va_list ap;\r
+       int i;\r
+       c->columns.percentages = snewn(ncolumns, int);\r
+       va_start(ap, ncolumns);\r
+       for (i = 0; i < ncolumns; i++)\r
+           c->columns.percentages[i] = va_arg(ap, int);\r
+       va_end(ap);\r
+    }\r
+    return c;\r
+}\r
+\r
+union control *ctrl_editbox(struct controlset *s, char *label, char shortcut,\r
+                           int percentage,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context, intorptr context2)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);\r
+    c->editbox.label = label ? dupstr(label) : NULL;\r
+    c->editbox.shortcut = shortcut;\r
+    c->editbox.percentwidth = percentage;\r
+    c->editbox.password = 0;\r
+    c->editbox.has_list = 0;\r
+    c->editbox.context2 = context2;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_combobox(struct controlset *s, char *label, char shortcut,\r
+                            int percentage,\r
+                            intorptr helpctx, handler_fn handler,\r
+                            intorptr context, intorptr context2)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);\r
+    c->editbox.label = label ? dupstr(label) : NULL;\r
+    c->editbox.shortcut = shortcut;\r
+    c->editbox.percentwidth = percentage;\r
+    c->editbox.password = 0;\r
+    c->editbox.has_list = 1;\r
+    c->editbox.context2 = context2;\r
+    return c;\r
+}\r
+\r
+/*\r
+ * `ncolumns' is followed by (alternately) radio button titles and\r
+ * intorptrs, until a NULL in place of a title string is seen. Each\r
+ * title is expected to be followed by a shortcut _iff_ `shortcut'\r
+ * is NO_SHORTCUT.\r
+ */\r
+union control *ctrl_radiobuttons(struct controlset *s, char *label,\r
+                                char shortcut, int ncolumns, intorptr helpctx,\r
+                                handler_fn handler, intorptr context, ...)\r
+{\r
+    va_list ap;\r
+    int i;\r
+    union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context);\r
+    c->radio.label = label ? dupstr(label) : NULL;\r
+    c->radio.shortcut = shortcut;\r
+    c->radio.ncolumns = ncolumns;\r
+    /*\r
+     * Initial pass along variable argument list to count the\r
+     * buttons.\r
+     */\r
+    va_start(ap, context);\r
+    i = 0;\r
+    while (va_arg(ap, char *) != NULL) {\r
+       i++;\r
+       if (c->radio.shortcut == NO_SHORTCUT)\r
+           (void)va_arg(ap, int);     /* char promotes to int in arg lists */\r
+       (void)va_arg(ap, intorptr);\r
+    }\r
+    va_end(ap);\r
+    c->radio.nbuttons = i;\r
+    if (c->radio.shortcut == NO_SHORTCUT)\r
+       c->radio.shortcuts = snewn(c->radio.nbuttons, char);\r
+    else\r
+       c->radio.shortcuts = NULL;\r
+    c->radio.buttons = snewn(c->radio.nbuttons, char *);\r
+    c->radio.buttondata = snewn(c->radio.nbuttons, intorptr);\r
+    /*\r
+     * Second pass along variable argument list to actually fill in\r
+     * the structure.\r
+     */\r
+    va_start(ap, context);\r
+    for (i = 0; i < c->radio.nbuttons; i++) {\r
+       c->radio.buttons[i] = dupstr(va_arg(ap, char *));\r
+       if (c->radio.shortcut == NO_SHORTCUT)\r
+           c->radio.shortcuts[i] = va_arg(ap, int);\r
+                                      /* char promotes to int in arg lists */\r
+       c->radio.buttondata[i] = va_arg(ap, intorptr);\r
+    }\r
+    va_end(ap);\r
+    return c;\r
+}\r
+\r
+union control *ctrl_pushbutton(struct controlset *s,char *label,char shortcut,\r
+                              intorptr helpctx, handler_fn handler,\r
+                              intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context);\r
+    c->button.label = label ? dupstr(label) : NULL;\r
+    c->button.shortcut = shortcut;\r
+    c->button.isdefault = 0;\r
+    c->button.iscancel = 0;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_listbox(struct controlset *s,char *label,char shortcut,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
+    c->listbox.label = label ? dupstr(label) : NULL;\r
+    c->listbox.shortcut = shortcut;\r
+    c->listbox.height = 5;            /* *shrug* a plausible default */\r
+    c->listbox.draglist = 0;\r
+    c->listbox.multisel = 0;\r
+    c->listbox.percentwidth = 100;\r
+    c->listbox.ncols = 0;\r
+    c->listbox.percentages = NULL;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_droplist(struct controlset *s, char *label, char shortcut,\r
+                            int percentage, intorptr helpctx,\r
+                            handler_fn handler, intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
+    c->listbox.label = label ? dupstr(label) : NULL;\r
+    c->listbox.shortcut = shortcut;\r
+    c->listbox.height = 0;            /* means it's a drop-down list */\r
+    c->listbox.draglist = 0;\r
+    c->listbox.multisel = 0;\r
+    c->listbox.percentwidth = percentage;\r
+    c->listbox.ncols = 0;\r
+    c->listbox.percentages = NULL;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_draglist(struct controlset *s,char *label,char shortcut,\r
+                            intorptr helpctx, handler_fn handler,\r
+                            intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);\r
+    c->listbox.label = label ? dupstr(label) : NULL;\r
+    c->listbox.shortcut = shortcut;\r
+    c->listbox.height = 5;            /* *shrug* a plausible default */\r
+    c->listbox.draglist = 1;\r
+    c->listbox.multisel = 0;\r
+    c->listbox.percentwidth = 100;\r
+    c->listbox.ncols = 0;\r
+    c->listbox.percentages = NULL;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_filesel(struct controlset *s,char *label,char shortcut,\r
+                           char const *filter, int write, char *title,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context);\r
+    c->fileselect.label = label ? dupstr(label) : NULL;\r
+    c->fileselect.shortcut = shortcut;\r
+    c->fileselect.filter = filter;\r
+    c->fileselect.for_writing = write;\r
+    c->fileselect.title = dupstr(title);\r
+    return c;\r
+}\r
+\r
+union control *ctrl_fontsel(struct controlset *s,char *label,char shortcut,\r
+                           intorptr helpctx, handler_fn handler,\r
+                           intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context);\r
+    c->fontselect.label = label ? dupstr(label) : NULL;\r
+    c->fontselect.shortcut = shortcut;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_tabdelay(struct controlset *s, union control *ctrl)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL));\r
+    c->tabdelay.ctrl = ctrl;\r
+    return c;\r
+}\r
+\r
+union control *ctrl_text(struct controlset *s, char *text, intorptr helpctx)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL));\r
+    c->text.label = dupstr(text);\r
+    return c;\r
+}\r
+\r
+union control *ctrl_checkbox(struct controlset *s, char *label, char shortcut,\r
+                            intorptr helpctx, handler_fn handler,\r
+                            intorptr context)\r
+{\r
+    union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context);\r
+    c->checkbox.label = label ? dupstr(label) : NULL;\r
+    c->checkbox.shortcut = shortcut;\r
+    return c;\r
+}\r
+\r
+void ctrl_free(union control *ctrl)\r
+{\r
+    int i;\r
+\r
+    sfree(ctrl->generic.label);\r
+    switch (ctrl->generic.type) {\r
+      case CTRL_RADIO:\r
+       for (i = 0; i < ctrl->radio.nbuttons; i++)\r
+           sfree(ctrl->radio.buttons[i]);\r
+       sfree(ctrl->radio.buttons);\r
+       sfree(ctrl->radio.shortcuts);\r
+       sfree(ctrl->radio.buttondata);\r
+       break;\r
+      case CTRL_COLUMNS:\r
+       sfree(ctrl->columns.percentages);\r
+       break;\r
+      case CTRL_LISTBOX:\r
+       sfree(ctrl->listbox.percentages);\r
+       break;\r
+      case CTRL_FILESELECT:\r
+       sfree(ctrl->fileselect.title);\r
+       break;\r
+    }\r
+    sfree(ctrl);\r
+}\r
+\r
+void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    int button;\r
+    /*\r
+     * For a standard radio button set, the context parameter gives\r
+     * offsetof(targetfield, Config), and the extra data per button\r
+     * gives the value the target field should take if that button\r
+     * is the one selected.\r
+     */\r
+    if (event == EVENT_REFRESH) {\r
+       for (button = 0; button < ctrl->radio.nbuttons; button++)\r
+           if (*(int *)ATOFFSET(data, ctrl->radio.context.i) ==\r
+               ctrl->radio.buttondata[button].i)\r
+               break;\r
+       /* We expected that `break' to happen, in all circumstances. */\r
+       assert(button < ctrl->radio.nbuttons);\r
+       dlg_radiobutton_set(ctrl, dlg, button);\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       button = dlg_radiobutton_get(ctrl, dlg);\r
+       assert(button >= 0 && button < ctrl->radio.nbuttons);\r
+       *(int *)ATOFFSET(data, ctrl->radio.context.i) =\r
+           ctrl->radio.buttondata[button].i;\r
+    }\r
+}\r
+\r
+void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    int offset, invert;\r
+\r
+    /*\r
+     * For a standard checkbox, the context parameter gives\r
+     * offsetof(targetfield, Config), optionally ORed with\r
+     * CHECKBOX_INVERT.\r
+     */\r
+    offset = ctrl->checkbox.context.i;\r
+    if (offset & CHECKBOX_INVERT) {\r
+       offset &= ~CHECKBOX_INVERT;\r
+       invert = 1;\r
+    } else\r
+       invert = 0;\r
+\r
+    /*\r
+     * C lacks a logical XOR, so the following code uses the idiom\r
+     * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1\r
+     * iff exactly one of a and b is nonzero, otherwise 0.)\r
+     */\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_checkbox_set(ctrl,dlg, (!*(int *)ATOFFSET(data,offset) ^ !invert));\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       *(int *)ATOFFSET(data, offset) = !dlg_checkbox_get(ctrl,dlg) ^ !invert;\r
+    }\r
+}\r
+\r
+void dlg_stdeditbox_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    /*\r
+     * The standard edit-box handler expects the main `context'\r
+     * field to contain the `offsetof' a field in the structure\r
+     * pointed to by `data'. The secondary `context2' field\r
+     * indicates the type of this field:\r
+     *\r
+     *  - if context2 > 0, the field is a char array and context2\r
+     *    gives its size.\r
+     *  - if context2 == -1, the field is an int and the edit box\r
+     *    is numeric.\r
+     *  - if context2 < -1, the field is an int and the edit box is\r
+     *    _floating_, and (-context2) gives the scale. (E.g. if\r
+     *    context2 == -1000, then typing 1.2 into the box will set\r
+     *    the field to 1200.)\r
+     */\r
+    int offset = ctrl->editbox.context.i;\r
+    int length = ctrl->editbox.context2.i;\r
+\r
+    if (length > 0) {\r
+       char *field = (char *)ATOFFSET(data, offset);\r
+       if (event == EVENT_REFRESH) {\r
+           dlg_editbox_set(ctrl, dlg, field);\r
+       } else if (event == EVENT_VALCHANGE) {\r
+           dlg_editbox_get(ctrl, dlg, field, length);\r
+       }\r
+    } else if (length < 0) {\r
+       int *field = (int *)ATOFFSET(data, offset);\r
+       char data[80];\r
+       if (event == EVENT_REFRESH) {\r
+           if (length == -1)\r
+               sprintf(data, "%d", *field);\r
+           else\r
+               sprintf(data, "%g", (double)*field / (double)(-length));\r
+           dlg_editbox_set(ctrl, dlg, data);\r
+       } else if (event == EVENT_VALCHANGE) {\r
+           dlg_editbox_get(ctrl, dlg, data, lenof(data));\r
+           if (length == -1)\r
+               *field = atoi(data);\r
+           else\r
+               *field = (int)((-length) * atof(data));\r
+       }\r
+    }\r
+}\r
+\r
+void dlg_stdfilesel_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    /*\r
+     * The standard file-selector handler expects the `context'\r
+     * field to contain the `offsetof' a Filename field in the\r
+     * structure pointed to by `data'.\r
+     */\r
+    int offset = ctrl->fileselect.context.i;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_filesel_set(ctrl, dlg, *(Filename *)ATOFFSET(data, offset));\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_filesel_get(ctrl, dlg, (Filename *)ATOFFSET(data, offset));\r
+    }\r
+}\r
+\r
+void dlg_stdfontsel_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event)\r
+{\r
+    /*\r
+     * The standard file-selector handler expects the `context'\r
+     * field to contain the `offsetof' a FontSpec field in the\r
+     * structure pointed to by `data'.\r
+     */\r
+    int offset = ctrl->fontselect.context.i;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_fontsel_set(ctrl, dlg, *(FontSpec *)ATOFFSET(data, offset));\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_fontsel_get(ctrl, dlg, (FontSpec *)ATOFFSET(data, offset));\r
+    }\r
+}\r
diff --git a/putty/DIALOG.H b/putty/DIALOG.H
new file mode 100644 (file)
index 0000000..67f79ed
--- /dev/null
@@ -0,0 +1,708 @@
+/*\r
+ * Exports and types from dialog.c.\r
+ */\r
+\r
+/*\r
+ * This will come in handy for generic control handlers. Anyone\r
+ * knows how to make this more portable, let me know :-)\r
+ */\r
+#define ATOFFSET(data, offset) ( (void *) ( (char *)(data) + (offset) ) )\r
+\r
+/*\r
+ * This is the big union which defines a single control, of any\r
+ * type.\r
+ * \r
+ * General principles:\r
+ *  - _All_ pointers in this structure are expected to point to\r
+ *    dynamically allocated things, unless otherwise indicated.\r
+ *  - `char' fields giving keyboard shortcuts are expected to be\r
+ *    NO_SHORTCUT if no shortcut is desired for a particular control.\r
+ *  - The `label' field can often be NULL, which will cause the\r
+ *    control to not have a label at all. This doesn't apply to\r
+ *    checkboxes and push buttons, in which the label is not\r
+ *    separate from the control.\r
+ */\r
+\r
+#define NO_SHORTCUT '\0'\r
+\r
+enum {\r
+    CTRL_TEXT,                        /* just a static line of text */\r
+    CTRL_EDITBOX,                     /* label plus edit box */\r
+    CTRL_RADIO,                               /* label plus radio buttons */\r
+    CTRL_CHECKBOX,                    /* checkbox (contains own label) */\r
+    CTRL_BUTTON,                      /* simple push button (no label) */\r
+    CTRL_LISTBOX,                     /* label plus list box */\r
+    CTRL_COLUMNS,                     /* divide window into columns */\r
+    CTRL_FILESELECT,                  /* label plus filename selector */\r
+    CTRL_FONTSELECT,                  /* label plus font selector */\r
+    CTRL_TABDELAY                     /* see `tabdelay' below */\r
+};\r
+\r
+/*\r
+ * Many controls have `intorptr' unions for storing user data,\r
+ * since the user might reasonably want to store either an integer\r
+ * or a void * pointer. Here I define a union, and two convenience\r
+ * functions to create that union from actual integers or pointers.\r
+ * \r
+ * The convenience functions are declared as inline if possible.\r
+ * Otherwise, they're declared here and defined when this header is\r
+ * included with DEFINE_INTORPTR_FNS defined. This is a total pain,\r
+ * but such is life.\r
+ */\r
+typedef union { void *p; int i; } intorptr;\r
+\r
+#ifndef INLINE\r
+intorptr I(int i);\r
+intorptr P(void *p);\r
+#endif\r
+\r
+#if defined DEFINE_INTORPTR_FNS || defined INLINE\r
+#ifdef INLINE\r
+#define PREFIX INLINE\r
+#else\r
+#define PREFIX\r
+#endif\r
+PREFIX intorptr I(int i) { intorptr ret; ret.i = i; return ret; }\r
+PREFIX intorptr P(void *p) { intorptr ret; ret.p = p; return ret; }\r
+#undef PREFIX\r
+#endif\r
+\r
+/*\r
+ * Each control has an `int' field specifying which columns it\r
+ * occupies in a multi-column part of the dialog box. These macros\r
+ * pack and unpack that field.\r
+ * \r
+ * If a control belongs in exactly one column, just specifying the\r
+ * column number is perfectly adequate.\r
+ */\r
+#define COLUMN_FIELD(start, span) ( (((span)-1) << 16) + (start) )\r
+#define COLUMN_START(field) ( (field) & 0xFFFF )\r
+#define COLUMN_SPAN(field) ( (((field) >> 16) & 0xFFFF) + 1 )\r
+\r
+union control;\r
+\r
+/*\r
+ * The number of event types is being deliberately kept small, on\r
+ * the grounds that not all platforms might be able to report a\r
+ * large number of subtle events. We have:\r
+ *  - the special REFRESH event, called when a control's value\r
+ *    needs setting\r
+ *  - the ACTION event, called when the user does something that\r
+ *    positively requests action (double-clicking a list box item,\r
+ *    or pushing a push-button)\r
+ *  - the VALCHANGE event, called when the user alters the setting\r
+ *    of the control in a way that is usually considered to alter\r
+ *    the underlying data (toggling a checkbox or radio button,\r
+ *    moving the items around in a drag-list, editing an edit\r
+ *    control)\r
+ *  - the SELCHANGE event, called when the user alters the setting\r
+ *    of the control in a more minor way (changing the selected\r
+ *    item in a list box).\r
+ *  - the CALLBACK event, which happens after the handler routine\r
+ *    has requested a subdialog (file selector, font selector,\r
+ *    colour selector) and it has come back with information.\r
+ */\r
+enum {\r
+    EVENT_REFRESH,\r
+    EVENT_ACTION,\r
+    EVENT_VALCHANGE,\r
+    EVENT_SELCHANGE,\r
+    EVENT_CALLBACK\r
+};\r
+typedef void (*handler_fn)(union control *ctrl, void *dlg,\r
+                          void *data, int event);\r
+\r
+#define STANDARD_PREFIX \\r
+       int type; \\r
+       char *label; \\r
+       int tabdelay; \\r
+       int column; \\r
+        handler_fn handler; \\r
+       intorptr context; \\r
+        intorptr helpctx\r
+\r
+union control {\r
+    /*\r
+     * The first possibility in this union is the generic header\r
+     * shared by all the structures, which we are therefore allowed\r
+     * to access through any one of them.\r
+     */\r
+    struct {\r
+       int type;\r
+       /*\r
+        * Every control except CTRL_COLUMNS has _some_ sort of\r
+        * label. By putting it in the `generic' union as well as\r
+        * everywhere else, we avoid having to have an irritating\r
+        * switch statement when we go through and deallocate all\r
+        * the memory in a config-box structure.\r
+        * \r
+        * Yes, this does mean that any non-NULL value in this\r
+        * field is expected to be dynamically allocated and\r
+        * freeable.\r
+        * \r
+        * For CTRL_COLUMNS, this field MUST be NULL.\r
+        */\r
+       char *label;\r
+       /*\r
+        * If `tabdelay' is non-zero, it indicates that this\r
+        * particular control should not yet appear in the tab\r
+        * order. A subsequent CTRL_TABDELAY entry will place it.\r
+        */\r
+       int tabdelay;\r
+       /*\r
+        * Indicate which column(s) this control occupies. This can\r
+        * be unpacked into starting column and column span by the\r
+        * COLUMN macros above.\r
+        */\r
+       int column;\r
+       /*\r
+        * Most controls need to provide a function which gets\r
+        * called when that control's setting is changed, or when\r
+        * the control's setting needs initialising.\r
+        * \r
+        * The `data' parameter points to the writable data being\r
+        * modified as a result of the configuration activity; for\r
+        * example, the PuTTY `Config' structure, although not\r
+        * necessarily.\r
+        * \r
+        * The `dlg' parameter is passed back to the platform-\r
+        * specific routines to read and write the actual control\r
+        * state.\r
+        */\r
+       handler_fn handler;\r
+       /*\r
+        * Almost all of the above functions will find it useful to\r
+        * be able to store a piece of `void *' or `int' data.\r
+        */\r
+       intorptr context;\r
+       /*\r
+        * For any control, we also allow the storage of a piece of\r
+        * data for use by context-sensitive help. For example, on\r
+        * Windows you can click the magic question mark and then\r
+        * click a control, and help for that control should spring\r
+        * up. Hence, here is a slot in which to store per-control\r
+        * data that a particular platform-specific driver can use\r
+        * to ensure it brings up the right piece of help text.\r
+        */\r
+       intorptr helpctx;\r
+    } generic;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       union control *ctrl;\r
+    } tabdelay;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+    } text;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       char shortcut;                 /* keyboard shortcut */\r
+       /*\r
+        * Percentage of the dialog-box width used by the edit box.\r
+        * If this is set to 100, the label is on its own line;\r
+        * otherwise the label is on the same line as the box\r
+        * itself.\r
+        */\r
+       int percentwidth;\r
+       int password;                  /* details of input are hidden */\r
+       /*\r
+        * A special case of the edit box is the combo box, which\r
+        * has a drop-down list built in. (Note that a _non_-\r
+        * editable drop-down list is done as a special case of a\r
+        * list box.)\r
+        * \r
+        * Don't try setting has_list and password on the same\r
+        * control; front ends are not required to support that\r
+        * combination.\r
+        */\r
+       int has_list;\r
+       /*\r
+        * Edit boxes tend to need two items of context, so here's\r
+        * a spare.\r
+        */\r
+       intorptr context2;\r
+    } editbox;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       /*\r
+        * `shortcut' here is a single keyboard shortcut which is\r
+        * expected to select the whole group of radio buttons. It\r
+        * can be NO_SHORTCUT if required, and there is also a way\r
+        * to place individual shortcuts on each button; see below.\r
+        */\r
+       char shortcut;\r
+       /*\r
+        * There are separate fields for `ncolumns' and `nbuttons'\r
+        * for several reasons.\r
+        * \r
+        * Firstly, we sometimes want the last of a set of buttons\r
+        * to have a longer label than the rest; we achieve this by\r
+        * setting `ncolumns' higher than `nbuttons', and the\r
+        * layout code is expected to understand that the final\r
+        * button should be given all the remaining space on the\r
+        * line. This sounds like a ludicrously specific special\r
+        * case (if we're doing this sort of thing, why not have\r
+        * the general ability to have a particular button span\r
+        * more than one column whether it's the last one or not?)\r
+        * but actually it's reasonably common for the sort of\r
+        * three-way control you get a lot of in PuTTY: `yes'\r
+        * versus `no' versus `some more complex way to decide'.\r
+        * \r
+        * Secondly, setting `nbuttons' higher than `ncolumns' lets\r
+        * us have more than one line of radio buttons for a single\r
+        * setting. A very important special case of this is\r
+        * setting `ncolumns' to 1, so that each button is on its\r
+        * own line.\r
+        */\r
+       int ncolumns;\r
+       int nbuttons;\r
+       /*\r
+        * This points to a dynamically allocated array of `char *'\r
+        * pointers, each of which points to a dynamically\r
+        * allocated string.\r
+        */\r
+       char **buttons;                /* `nbuttons' button labels */\r
+       /*\r
+        * This points to a dynamically allocated array of `char'\r
+        * giving the individual keyboard shortcuts for each radio\r
+        * button. The array may be NULL if none are required.\r
+        */\r
+       char *shortcuts;               /* `nbuttons' shortcuts; may be NULL */\r
+       /*\r
+        * This points to a dynamically allocated array of\r
+        * intorptr, giving helpful data for each button.\r
+        */\r
+       intorptr *buttondata;          /* `nbuttons' entries; may be NULL */\r
+    } radio;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       char shortcut;\r
+    } checkbox;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       char shortcut;\r
+       /*\r
+        * At least Windows has the concept of a `default push\r
+        * button', which gets implicitly pressed when you hit\r
+        * Return even if it doesn't have the input focus.\r
+        */\r
+       int isdefault;\r
+       /*\r
+        * Also, the reverse of this: a default cancel-type button,\r
+        * which is implicitly pressed when you hit Escape.\r
+        */\r
+       int iscancel;\r
+    } button;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       char shortcut;                 /* keyboard shortcut */\r
+       /*\r
+        * Height of the list box, in approximate number of lines.\r
+        * If this is zero, the list is a drop-down list.\r
+        */\r
+       int height;                    /* height in lines */\r
+       /*\r
+        * If this is set, the list elements can be reordered by\r
+        * the user (by drag-and-drop or by Up and Down buttons,\r
+        * whatever the per-platform implementation feels\r
+        * comfortable with). This is not guaranteed to work on a\r
+        * drop-down list, so don't try it!\r
+        */\r
+       int draglist;\r
+       /*\r
+        * If this is non-zero, the list can have more than one\r
+        * element selected at a time. This is not guaranteed to\r
+        * work on a drop-down list, so don't try it!\r
+        * \r
+        * Different non-zero values request slightly different\r
+        * types of multi-selection (this may well be meaningful\r
+        * only in GTK, so everyone else can ignore it if they\r
+        * want). 1 means the list box expects to have individual\r
+        * items selected, whereas 2 means it expects the user to\r
+        * want to select a large contiguous range at a time.\r
+        */\r
+       int multisel;\r
+       /*\r
+        * Percentage of the dialog-box width used by the list box.\r
+        * If this is set to 100, the label is on its own line;\r
+        * otherwise the label is on the same line as the box\r
+        * itself. Setting this to anything other than 100 is not\r
+        * guaranteed to work on a _non_-drop-down list, so don't\r
+        * try it!\r
+        */\r
+       int percentwidth;\r
+       /*\r
+        * Some list boxes contain strings that contain tab\r
+        * characters. If `ncols' is greater than 0, then\r
+        * `percentages' is expected to be non-zero and to contain\r
+        * the respective widths of `ncols' columns, which together\r
+        * will exactly fit the width of the list box. Otherwise\r
+        * `percentages' must be NULL.\r
+        * \r
+        * There should never be more than one column in a\r
+        * drop-down list (one with height==0), because front ends\r
+        * may have to implement it as a special case of an\r
+        * editable combo box.\r
+        */\r
+       int ncols;                     /* number of columns */\r
+       int *percentages;              /* % width of each column */\r
+    } listbox;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       char shortcut;\r
+       /*\r
+        * `filter' dictates what type of files will be selected by\r
+        * default; for example, when selecting private key files\r
+        * the file selector would do well to only show .PPK files\r
+        * (on those systems where this is the chosen extension).\r
+        * \r
+        * The precise contents of `filter' are platform-defined,\r
+        * unfortunately. The special value NULL means `all files'\r
+        * and is always a valid fallback.\r
+        * \r
+        * Unlike almost all strings in this structure, this value\r
+        * is NOT expected to require freeing (although of course\r
+        * you can always use ctrl_alloc if you do need to create\r
+        * one on the fly). This is because the likely mode of use\r
+        * is to define string constants in a platform-specific\r
+        * header file, and directly reference those. Or worse, a\r
+        * particular platform might choose to cast integers into\r
+        * this pointer type...\r
+        */\r
+       char const *filter;\r
+       /*\r
+        * Some systems like to know whether a file selector is\r
+        * choosing a file to read or one to write (and possibly\r
+        * create).\r
+        */\r
+       int for_writing;\r
+       /*\r
+        * On at least some platforms, the file selector is a\r
+        * separate dialog box, and contains a user-settable title.\r
+        * \r
+        * This value _is_ expected to require freeing.\r
+        */\r
+       char *title;\r
+    } fileselect;\r
+    struct {\r
+       /* In this variant, `label' MUST be NULL. */\r
+       STANDARD_PREFIX;\r
+       int ncols;                     /* number of columns */\r
+       int *percentages;              /* % width of each column */\r
+       /*\r
+        * Every time this control type appears, exactly one of\r
+        * `ncols' and the previous number of columns MUST be one.\r
+        * Attempting to allow a seamless transition from a four-\r
+        * to a five-column layout, for example, would be way more\r
+        * trouble than it was worth. If you must lay things out\r
+        * like that, define eight unevenly sized columns and use\r
+        * column-spanning a lot. But better still, just don't.\r
+        * \r
+        * `percentages' may be NULL if ncols==1, to save space.\r
+        */\r
+    } columns;\r
+    struct {\r
+       STANDARD_PREFIX;\r
+       char shortcut;\r
+    } fontselect;\r
+};\r
+\r
+#undef STANDARD_PREFIX\r
+\r
+/*\r
+ * `controlset' is a container holding an array of `union control'\r
+ * structures, together with a panel name and a title for the whole\r
+ * set. In Windows and any similar-looking GUI, each `controlset'\r
+ * in the config will be a container box within a panel.\r
+ * \r
+ * Special case: if `boxname' is NULL, the control set gives an\r
+ * overall title for an entire panel of controls.\r
+ */\r
+struct controlset {\r
+    char *pathname;                   /* panel path, e.g. "SSH/Tunnels" */\r
+    char *boxname;                    /* internal short name of controlset */\r
+    char *boxtitle;                   /* title of container box */\r
+    int ncolumns;                     /* current no. of columns at bottom */\r
+    int ncontrols;                    /* number of `union control' in array */\r
+    int ctrlsize;                     /* allocated size of array */\r
+    union control **ctrls;            /* actual array */\r
+};\r
+\r
+/*\r
+ * This is the container structure which holds a complete set of\r
+ * controls.\r
+ */\r
+struct controlbox {\r
+    int nctrlsets;                    /* number of ctrlsets */\r
+    int ctrlsetsize;                  /* ctrlset size */\r
+    struct controlset **ctrlsets;      /* actual array of ctrlsets */\r
+    int nfrees;\r
+    int freesize;\r
+    void **frees;                     /* array of aux data areas to free */\r
+};\r
+\r
+struct controlbox *ctrl_new_box(void);\r
+void ctrl_free_box(struct controlbox *);\r
+\r
+/*\r
+ * Standard functions used for populating a controlbox structure.\r
+ */\r
+\r
+/* Set up a panel title. */\r
+struct controlset *ctrl_settitle(struct controlbox *,\r
+                                char *path, char *title);\r
+/* Retrieve a pointer to a controlset, creating it if absent. */\r
+struct controlset *ctrl_getset(struct controlbox *,\r
+                              char *path, char *name, char *boxtitle);\r
+void ctrl_free_set(struct controlset *);\r
+\r
+void ctrl_free(union control *);\r
+\r
+/*\r
+ * This function works like `malloc', but the memory it returns\r
+ * will be automatically freed when the controlbox is freed. Note\r
+ * that a controlbox is a dialog-box _template_, not an instance,\r
+ * and so data allocated through this function is better not used\r
+ * to hold modifiable per-instance things. It's mostly here for\r
+ * allocating structures to be passed as control handler params.\r
+ */\r
+void *ctrl_alloc(struct controlbox *b, size_t size);\r
+\r
+/*\r
+ * Individual routines to create `union control' structures in a controlset.\r
+ * \r
+ * Most of these routines allow the most common fields to be set\r
+ * directly, and put default values in the rest. Each one returns a\r
+ * pointer to the `union control' it created, so that final tweaks\r
+ * can be made.\r
+ */\r
+\r
+/* `ncolumns' is followed by that many percentages, as integers. */\r
+union control *ctrl_columns(struct controlset *, int ncolumns, ...);\r
+union control *ctrl_editbox(struct controlset *, char *label, char shortcut,\r
+                           int percentage, intorptr helpctx,\r
+                           handler_fn handler,\r
+                           intorptr context, intorptr context2);\r
+union control *ctrl_combobox(struct controlset *, char *label, char shortcut,\r
+                            int percentage, intorptr helpctx,\r
+                            handler_fn handler,\r
+                            intorptr context, intorptr context2);\r
+/*\r
+ * `ncolumns' is followed by (alternately) radio button titles and\r
+ * intorptrs, until a NULL in place of a title string is seen. Each\r
+ * title is expected to be followed by a shortcut _iff_ `shortcut'\r
+ * is NO_SHORTCUT.\r
+ */\r
+union control *ctrl_radiobuttons(struct controlset *, char *label,\r
+                                char shortcut, int ncolumns,\r
+                                intorptr helpctx,\r
+                                handler_fn handler, intorptr context, ...);\r
+union control *ctrl_pushbutton(struct controlset *,char *label,char shortcut,\r
+                              intorptr helpctx,\r
+                              handler_fn handler, intorptr context);\r
+union control *ctrl_listbox(struct controlset *,char *label,char shortcut,\r
+                           intorptr helpctx,\r
+                           handler_fn handler, intorptr context);\r
+union control *ctrl_droplist(struct controlset *, char *label, char shortcut,\r
+                            int percentage, intorptr helpctx,\r
+                            handler_fn handler, intorptr context);\r
+union control *ctrl_draglist(struct controlset *,char *label,char shortcut,\r
+                            intorptr helpctx,\r
+                            handler_fn handler, intorptr context);\r
+union control *ctrl_filesel(struct controlset *,char *label,char shortcut,\r
+                           char const *filter, int write, char *title,\r
+                           intorptr helpctx,\r
+                           handler_fn handler, intorptr context);\r
+union control *ctrl_fontsel(struct controlset *,char *label,char shortcut,\r
+                           intorptr helpctx,\r
+                           handler_fn handler, intorptr context);\r
+union control *ctrl_text(struct controlset *, char *text, intorptr helpctx);\r
+union control *ctrl_checkbox(struct controlset *, char *label, char shortcut,\r
+                            intorptr helpctx,\r
+                            handler_fn handler, intorptr context);\r
+union control *ctrl_tabdelay(struct controlset *, union control *);\r
+\r
+/*\r
+ * Standard handler routines to cover most of the common cases in\r
+ * the config box.\r
+ */\r
+/*\r
+ * The standard radio-button handler expects the main `context'\r
+ * field to contain the `offsetof' of an int field in the structure\r
+ * pointed to by `data', and expects each of the individual button\r
+ * data to give a value for that int field.\r
+ */\r
+void dlg_stdradiobutton_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event);\r
+/*\r
+ * The standard checkbox handler expects the main `context' field\r
+ * to contain the `offsetof' an int field in the structure pointed\r
+ * to by `data', optionally ORed with CHECKBOX_INVERT to indicate\r
+ * that the sense of the datum is opposite to the sense of the\r
+ * checkbox.\r
+ */\r
+#define CHECKBOX_INVERT (1<<30)\r
+void dlg_stdcheckbox_handler(union control *ctrl, void *dlg,\r
+                            void *data, int event);\r
+/*\r
+ * The standard edit-box handler expects the main `context' field\r
+ * to contain the `offsetof' a field in the structure pointed to by\r
+ * `data'. The secondary `context2' field indicates the type of\r
+ * this field:\r
+ * \r
+ *  - if context2 > 0, the field is a char array and context2 gives\r
+ *    its size.\r
+ *  - if context2 == -1, the field is an int and the edit box is\r
+ *    numeric.\r
+ *  - if context2 < -1, the field is an int and the edit box is\r
+ *    _floating_, and (-context2) gives the scale. (E.g. if\r
+ *    context2 == -1000, then typing 1.2 into the box will set the\r
+ *    field to 1200.)\r
+ */\r
+void dlg_stdeditbox_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event);\r
+/*\r
+ * The standard file-selector handler expects the main `context'\r
+ * field to contain the `offsetof' a Filename field in the\r
+ * structure pointed to by `data'.\r
+ */\r
+void dlg_stdfilesel_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event);\r
+/*\r
+ * The standard font-selector handler expects the main `context'\r
+ * field to contain the `offsetof' a Font field in the structure\r
+ * pointed to by `data'.\r
+ */\r
+void dlg_stdfontsel_handler(union control *ctrl, void *dlg,\r
+                           void *data, int event);\r
+\r
+/*\r
+ * Routines the platform-independent dialog code can call to read\r
+ * and write the values of controls.\r
+ */\r
+void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton);\r
+int dlg_radiobutton_get(union control *ctrl, void *dlg);\r
+void dlg_checkbox_set(union control *ctrl, void *dlg, int checked);\r
+int dlg_checkbox_get(union control *ctrl, void *dlg);\r
+void dlg_editbox_set(union control *ctrl, void *dlg, char const *text);\r
+void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length);\r
+/* The `listbox' functions can also apply to combo boxes. */\r
+void dlg_listbox_clear(union control *ctrl, void *dlg);\r
+void dlg_listbox_del(union control *ctrl, void *dlg, int index);\r
+void dlg_listbox_add(union control *ctrl, void *dlg, char const *text);\r
+/*\r
+ * Each listbox entry may have a numeric id associated with it.\r
+ * Note that some front ends only permit a string to be stored at\r
+ * each position, which means that _if_ you put two identical\r
+ * strings in any listbox then you MUST not assign them different\r
+ * IDs and expect to get meaningful results back.\r
+ */\r
+void dlg_listbox_addwithid(union control *ctrl, void *dlg,\r
+                          char const *text, int id);\r
+int dlg_listbox_getid(union control *ctrl, void *dlg, int index);\r
+/* dlg_listbox_index returns <0 if no single element is selected. */\r
+int dlg_listbox_index(union control *ctrl, void *dlg);\r
+int dlg_listbox_issel(union control *ctrl, void *dlg, int index);\r
+void dlg_listbox_select(union control *ctrl, void *dlg, int index);\r
+void dlg_text_set(union control *ctrl, void *dlg, char const *text);\r
+void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn);\r
+void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn);\r
+void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fn);\r
+void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fn);\r
+/*\r
+ * Bracketing a large set of updates in these two functions will\r
+ * cause the front end (if possible) to delay updating the screen\r
+ * until it's all complete, thus avoiding flicker.\r
+ */\r
+void dlg_update_start(union control *ctrl, void *dlg);\r
+void dlg_update_done(union control *ctrl, void *dlg);\r
+/*\r
+ * Set input focus into a particular control.\r
+ */\r
+void dlg_set_focus(union control *ctrl, void *dlg);\r
+/*\r
+ * Change the label text on a control.\r
+ */\r
+void dlg_label_change(union control *ctrl, void *dlg, char const *text);\r
+/*\r
+ * Return the `ctrl' structure for the most recent control that had\r
+ * the input focus apart from the one mentioned. This is NOT\r
+ * GUARANTEED to work on all platforms, so don't base any critical\r
+ * functionality on it!\r
+ */\r
+union control *dlg_last_focused(union control *ctrl, void *dlg);\r
+/*\r
+ * During event processing, you might well want to give an error\r
+ * indication to the user. dlg_beep() is a quick and easy generic\r
+ * error; dlg_error() puts up a message-box or equivalent.\r
+ */\r
+void dlg_beep(void *dlg);\r
+void dlg_error_msg(void *dlg, char *msg);\r
+/*\r
+ * This function signals to the front end that the dialog's\r
+ * processing is completed, and passes an integer value (typically\r
+ * a success status).\r
+ */\r
+void dlg_end(void *dlg, int value);\r
+\r
+/*\r
+ * Routines to manage a (per-platform) colour selector.\r
+ * dlg_coloursel_start() is called in an event handler, and\r
+ * schedules the running of a colour selector after the event\r
+ * handler returns. The colour selector will send EVENT_CALLBACK to\r
+ * the control that spawned it, when it's finished;\r
+ * dlg_coloursel_results() fetches the results, as integers from 0\r
+ * to 255; it returns nonzero on success, or zero if the colour\r
+ * selector was dismissed by hitting Cancel or similar.\r
+ * \r
+ * dlg_coloursel_start() accepts an RGB triple which is used to\r
+ * initialise the colour selector to its starting value.\r
+ */\r
+void dlg_coloursel_start(union control *ctrl, void *dlg,\r
+                        int r, int g, int b);\r
+int dlg_coloursel_results(union control *ctrl, void *dlg,\r
+                         int *r, int *g, int *b);\r
+\r
+/*\r
+ * This routine is used by the platform-independent code to\r
+ * indicate that the value of a particular control is likely to\r
+ * have changed. It triggers a call of the handler for that control\r
+ * with `event' set to EVENT_REFRESH.\r
+ * \r
+ * If `ctrl' is NULL, _all_ controls in the dialog get refreshed\r
+ * (for loading or saving entire sets of settings).\r
+ */\r
+void dlg_refresh(union control *ctrl, void *dlg);\r
+\r
+/*\r
+ * It's perfectly possible that individual controls might need to\r
+ * allocate or store per-dialog-instance data, so here's a\r
+ * mechanism.\r
+ * \r
+ * `dlg_get_privdata' and `dlg_set_privdata' allow the user to get\r
+ * and set a void * pointer associated with the control in\r
+ * question. `dlg_alloc_privdata' will allocate memory, store a\r
+ * pointer to that memory in the private data field, and arrange\r
+ * for it to be automatically deallocated on dialog cleanup.\r
+ */\r
+void *dlg_get_privdata(union control *ctrl, void *dlg);\r
+void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr);\r
+void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size);\r
+\r
+/*\r
+ * Standard helper functions for reading a controlbox structure.\r
+ */\r
+\r
+/*\r
+ * Find the index of next controlset in a controlbox for a given\r
+ * path, or -1 if no such controlset exists. If -1 is passed as\r
+ * input, finds the first. Intended usage is something like\r
+ * \r
+ *     for (index=-1; (index=ctrl_find_path(ctrlbox, index, path)) >= 0 ;) {\r
+ *          ... process this controlset ...\r
+ *      }\r
+ */\r
+int ctrl_find_path(struct controlbox *b, char *path, int index);\r
+int ctrl_path_elements(char *path);\r
+/* Return the number of matching path elements at the starts of p1 and p2,\r
+ * or INT_MAX if the paths are identical. */\r
+int ctrl_path_compare(char *p1, char *p2);\r
diff --git a/putty/DOC/BLURB.BUT b/putty/DOC/BLURB.BUT
new file mode 100644 (file)
index 0000000..f03341d
--- /dev/null
@@ -0,0 +1,36 @@
+\define{versionidblurb} \versionid $Id: blurb.but 9072 2011-01-05 12:01:00Z jacob $\r
+\r
+\title PuTTY User Manual\r
+\r
+\cfg{xhtml-leaf-level}{1}\r
+\cfg{xhtml-leaf-smallest-contents}{2}\r
+\cfg{xhtml-leaf-contains-contents}{true}\r
+\cfg{xhtml-body-end}{<p>If you want to provide feedback on this manual\r
+or on the PuTTY tools themselves, see the\r
+<a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/feedback.html">Feedback\r
+page</a>.</p>}\r
+\r
+\cfg{html-template-fragment}{%k}{%b}\r
+\r
+\cfg{info-max-file-size}{0}\r
+\r
+\cfg{xhtml-contents-filename}{index.html}\r
+\cfg{text-filename}{puttydoc.txt}\r
+\cfg{winhelp-filename}{putty.hlp}\r
+\cfg{info-filename}{putty.info}\r
+\r
+PuTTY is a free (MIT-licensed) Win32 Telnet and SSH client. This\r
+manual documents PuTTY, and its companion utilities PSCP, PSFTP,\r
+Plink, Pageant and PuTTYgen.\r
+\r
+\e{Note to Unix users:} this manual currently primarily documents the\r
+Windows versions of the PuTTY utilities. Some options are therefore\r
+mentioned that are absent from the \i{Unix version}; the Unix version has\r
+features not described here; and the \i\cw{pterm} and command-line\r
+\cw{puttygen} utilities are not described at all. The only\r
+Unix-specific documentation that currently exists is the\r
+\I{man pages for PuTTY tools}man pages.\r
+\r
+\copyright This manual is copyright 2001-2011 Simon Tatham. All\r
+rights reserved. You may distribute this documentation under the MIT\r
+licence. See \k{licence} for the licence text in full.\r
diff --git a/putty/DOC/CHM.BUT b/putty/DOC/CHM.BUT
new file mode 100644 (file)
index 0000000..ae4d402
--- /dev/null
@@ -0,0 +1,23 @@
+\# File containing the magic HTML configuration directives to create\r
+\# an MS HTML Help project. We put this on the end of the PuTTY\r
+\# docs build command line to build the HHP and friends.\r
+\r
+\cfg{html-leaf-level}{infinite}\r
+\cfg{html-leaf-contains-contents}{false}\r
+\cfg{html-suppress-navlinks}{true}\r
+\cfg{html-suppress-address}{true}\r
+\r
+\cfg{html-contents-filename}{index.html}\r
+\cfg{html-template-filename}{%k.html}\r
+\cfg{html-template-fragment}{%k}\r
+\r
+\cfg{html-mshtmlhelp-chm}{putty.chm}\r
+\cfg{html-mshtmlhelp-project}{putty.hhp}\r
+\cfg{html-mshtmlhelp-contents}{putty.hhc}\r
+\cfg{html-mshtmlhelp-index}{putty.hhk}\r
+\r
+\cfg{html-body-end}{}\r
+\r
+\cfg{html-head-end}{<link rel="stylesheet" type="text/css" href="chm.css">}\r
+\r
+\versionid $Id: chm.but 7272 2007-02-11 18:13:56Z jacob $\r
diff --git a/putty/DOC/CHM.CSS b/putty/DOC/CHM.CSS
new file mode 100644 (file)
index 0000000..6e49b94
--- /dev/null
@@ -0,0 +1,7 @@
+/* Stylesheet for a Windows .CHM help file */\r
+\r
+body { font-size: 75%; font-family: Verdana, Arial, Helvetica, Sans-Serif; }\r
+\r
+h1 { font-weight: bold; font-size: 150%; }\r
+h2 { font-weight: bold; font-size: 130%; }\r
+h3 { font-weight: bold; font-size: 120%; }\r
diff --git a/putty/DOC/CONFIG.BUT b/putty/DOC/CONFIG.BUT
new file mode 100644 (file)
index 0000000..2a87f3a
--- /dev/null
@@ -0,0 +1,3341 @@
+\define{versionidconfig} \versionid $Id: config.but 9063 2010-12-29 14:11:25Z simon $\r
+\r
+\C{config} Configuring PuTTY\r
+\r
+This chapter describes all the \i{configuration options} in PuTTY.\r
+\r
+PuTTY is configured using the control panel that comes up before you\r
+start a session. Some options can also be changed in the middle of a\r
+session, by selecting \q{Change Settings} from the window menu.\r
+\r
+\H{config-session} The Session panel\r
+\r
+The Session configuration panel contains the basic options you need\r
+to specify in order to open a session at all, and also allows you to\r
+save your settings to be reloaded later.\r
+\r
+\S{config-hostname} The \i{host name} section\r
+\r
+\cfg{winhelp-topic}{session.hostname}\r
+\r
+The top box on the Session panel, labelled \q{Specify your\r
+connection by host name}, contains the details that need to be\r
+filled in before PuTTY can open a session at all.\r
+\r
+\b The \q{Host Name} box is where you type the name, or the \i{IP\r
+address}, of the server you want to connect to.\r
+\r
+\b The \q{Connection type} radio buttons let you choose what type of\r
+connection you want to make: a \I{raw TCP connections}raw\r
+connection, a \i{Telnet} connection, an \i{Rlogin} connection, an\r
+\i{SSH} connection, or a connection to a local \i{serial line}. (See\r
+\k{which-one} for a summary of the differences between SSH, Telnet\r
+and rlogin; see \k{using-rawprot} for an explanation of \q{raw}\r
+connections; see \k{using-serial} for information about using a\r
+serial line.)\r
+\r
+\b The \q{Port} box lets you specify which \i{port number} on the\r
+server to connect to. If you select Telnet, Rlogin, or SSH, this box\r
+will be filled in automatically to the usual value, and you will\r
+only need to change it if you have an unusual server. If you select\r
+Raw mode, you will almost certainly need to fill in the \q{Port} box\r
+yourself.\r
+\r
+If you select \q{Serial} from the \q{Connection type} radio buttons,\r
+the \q{Host Name} and \q{Port} boxes are replaced by \q{Serial line}\r
+and \q{Speed}; see \k{config-serial} for more details of these.\r
+\r
+\S{config-saving} \ii{Loading and storing saved sessions}\r
+\r
+\cfg{winhelp-topic}{session.saved}\r
+\r
+The next part of the Session configuration panel allows you to save\r
+your preferred PuTTY options so they will appear automatically the\r
+next time you start PuTTY. It also allows you to create \e{saved\r
+sessions}, which contain a full set of configuration options plus a\r
+host name and protocol. A saved session contains all the information\r
+PuTTY needs to start exactly the session you want.\r
+\r
+\b To save your default settings: first set up the settings the way\r
+you want them saved. Then come back to the Session panel. Select the\r
+\q{\i{Default Settings}} entry in the saved sessions list, with a single\r
+click. Then press the \q{Save} button.\r
+\r
+If there is a specific host you want to store the details of how to\r
+connect to, you should create a saved session, which will be\r
+separate from the Default Settings.\r
+\r
+\b To save a session: first go through the rest of the configuration\r
+box setting up all the options you want. Then come back to the\r
+Session panel. Enter a name for the saved session in the \q{Saved\r
+Sessions} input box. (The server name is often a good choice for a\r
+saved session name.) Then press the \q{Save} button. Your saved\r
+session name should now appear in the list box.\r
+\r
+\lcont{\r
+You can also save settings in mid-session, from the \q{Change Settings}\r
+dialog. Settings changed since the start of the session will be saved\r
+with their current values; as well as settings changed through the\r
+dialog, this includes changes in window size, window title changes\r
+sent by the server, and so on.\r
+}\r
+\r
+\b To reload a saved session: single-click to select the session\r
+name in the list box, and then press the \q{Load} button. Your saved\r
+settings should all appear in the configuration panel.\r
+\r
+\b To modify a saved session: first load it as described above. Then\r
+make the changes you want. Come back to the Session panel, and press\r
+the \q{Save} button. The new settings will be saved over the top of\r
+the old ones.\r
+\r
+\lcont{\r
+To save the new settings under a different name, you can enter the new\r
+name in the \q{Saved Sessions} box, or single-click to select a\r
+session name in the list box to overwrite that session. To save\r
+\q{Default Settings}, you must single-click the name before saving.\r
+}\r
+\r
+\b To start a saved session immediately: double-click on the session\r
+name in the list box.\r
+\r
+\b To delete a saved session: single-click to select the session\r
+name in the list box, and then press the \q{Delete} button.\r
+\r
+Each saved session is independent of the Default Settings\r
+configuration. If you change your preferences and update Default\r
+Settings, you must also update every saved session separately.\r
+\r
+Saved sessions are stored in the \i{Registry}, at the location\r
+\r
+\c HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\r
+\r
+If you need to store them in a file, you could try the method\r
+described in \k{config-file}.\r
+\r
+\S{config-closeonexit} \q{\ii{Close Window} on Exit}\r
+\r
+\cfg{winhelp-topic}{session.coe}\r
+\r
+Finally in the Session panel, there is an option labelled \q{Close\r
+Window on Exit}. This controls whether the PuTTY \i{terminal window}\r
+disappears as soon as the session inside it terminates. If you are\r
+likely to want to copy and paste text out of the session after it\r
+has terminated, or restart the session, you should arrange for this\r
+option to be off.\r
+\r
+\q{Close Window On Exit} has three settings. \q{Always} means always\r
+close the window on exit; \q{Never} means never close on exit\r
+(always leave the window open, but \I{inactive window}inactive). The\r
+third setting, and the default one, is \q{Only on clean exit}. In this\r
+mode, a session which terminates normally will cause its window to\r
+close, but one which is aborted unexpectedly by network trouble or a\r
+confusing message from the server will leave the window up.\r
+\r
+\H{config-logging} The Logging panel\r
+\r
+\cfg{winhelp-topic}{logging.main}\r
+\r
+The Logging configuration panel allows you to save \i{log file}s of your\r
+PuTTY sessions, for debugging, analysis or future reference.\r
+\r
+The main option is a radio-button set that specifies whether PuTTY\r
+will log anything at all. The options are:\r
+\r
+\b \q{None}. This is the default option; in this mode PuTTY will not\r
+create a log file at all.\r
+\r
+\b \q{Printable output}. In this mode, a log file will be\r
+created and written to, but only printable text will be saved into\r
+it. The various terminal control codes that are typically sent down\r
+an interactive session alongside the printable text will be omitted.\r
+This might be a useful mode if you want to read a log file in a text\r
+editor and hope to be able to make sense of it.\r
+\r
+\b \q{All session output}. In this mode, \e{everything} sent by\r
+the server into your terminal session is logged. If you view the log\r
+file in a text editor, therefore, you may well find it full of\r
+strange control characters. This is a particularly useful mode if\r
+you are experiencing problems with PuTTY's terminal handling: you\r
+can record everything that went to the terminal, so that someone\r
+else can replay the session later in slow motion and watch to see\r
+what went wrong.\r
+\r
+\b \I{SSH packet log}\q{SSH packets}. In this mode (which is only used\r
+by SSH connections), the SSH message packets sent over the encrypted\r
+connection are written to the log file (as well as \i{Event Log}\r
+entries). You might need this to debug a network-level problem, or\r
+more likely to send to the PuTTY authors as part of a bug report.\r
+\e{BE WARNED} that if you log in using a password, the password can\r
+appear in the log file; see \k{config-logssh} for options that may\r
+help to remove sensitive material from the log file before you send it\r
+to anyone else.\r
+\r
+\b \q{SSH packets and raw data}. In this mode, as well as the\r
+decrypted packets (as in the previous mode), the \e{raw} (encrypted,\r
+compressed, etc) packets are \e{also} logged. This could be useful to\r
+diagnose corruption in transit. (The same caveats as the previous mode\r
+apply, of course.)\r
+\r
+Note that the non-SSH logging options (\q{Printable output} and\r
+\q{All session output}) only work with PuTTY proper; in programs\r
+without terminal emulation (such as Plink), they will have no effect,\r
+even if enabled via saved settings.\r
+\r
+\S{config-logfilename} \q{Log file name}\r
+\r
+\cfg{winhelp-topic}{logging.filename}\r
+\r
+In this edit box you enter the name of the file you want to log the\r
+session to. The \q{Browse} button will let you look around your file\r
+system to find the right place to put the file; or if you already\r
+know exactly where you want it to go, you can just type a pathname\r
+into the edit box.\r
+\r
+There are a few special features in this box. If you use the \c{&}\r
+character in the file name box, PuTTY will insert details of the\r
+current session in the name of the file it actually opens. The\r
+precise replacements it will do are:\r
+\r
+\b \c{&Y} will be replaced by the current year, as four digits.\r
+\r
+\b \c{&M} will be replaced by the current month, as two digits.\r
+\r
+\b \c{&D} will be replaced by the current day of the month, as two\r
+digits.\r
+\r
+\b \c{&T} will be replaced by the current time, as six digits\r
+(HHMMSS) with no punctuation.\r
+\r
+\b \c{&H} will be replaced by the host name you are connecting to.\r
+\r
+For example, if you enter the host name\r
+\c{c:\\puttylogs\\log-&h-&y&m&d-&t.dat}, you will end up with files looking\r
+like\r
+\r
+\c log-server1.example.com-20010528-110859.dat\r
+\c log-unixbox.somewhere.org-20010611-221001.dat\r
+\r
+\S{config-logfileexists} \q{What to do if the log file already exists}\r
+\r
+\cfg{winhelp-topic}{logging.exists}\r
+\r
+This control allows you to specify what PuTTY should do if it tries\r
+to start writing to a log file and it finds the file already exists.\r
+You might want to automatically destroy the existing log file and\r
+start a new one with the same name. Alternatively, you might want to\r
+open the existing log file and add data to the \e{end} of it.\r
+Finally (the default option), you might not want to have any\r
+automatic behaviour, but to ask the user every time the problem\r
+comes up.\r
+\r
+\S{config-logflush} \I{log file, flushing}\q{Flush log file frequently}\r
+\r
+\cfg{winhelp-topic}{logging.flush}\r
+\r
+This option allows you to control how frequently logged data is\r
+flushed to disc. By default, PuTTY will flush data as soon as it is\r
+displayed, so that if you view the log file while a session is still\r
+open, it will be up to date; and if the client system crashes, there's\r
+a greater chance that the data will be preserved.\r
+\r
+However, this can incur a performance penalty. If PuTTY is running\r
+slowly with logging enabled, you could try unchecking this option. Be\r
+warned that the log file may not always be up to date as a result\r
+(although it will of course be flushed when it is closed, for instance\r
+at the end of a session).\r
+\r
+\S{config-logssh} Options specific to \i{SSH packet log}ging\r
+\r
+These options only apply if SSH packet data is being logged.\r
+\r
+The following options allow particularly sensitive portions of\r
+unencrypted packets to be automatically left out of the log file.\r
+They are only intended to deter casual nosiness; an attacker could\r
+glean a lot of useful information from even these obfuscated logs\r
+(e.g., length of password).\r
+\r
+\S2{config-logssh-omitpw} \q{Omit known password fields}\r
+\r
+\cfg{winhelp-topic}{logging.ssh.omitpassword}\r
+\r
+When checked, decrypted password fields are removed from the log of\r
+transmitted packets. (This includes any user responses to\r
+challenge-response authentication methods such as\r
+\q{keyboard-interactive}.) This does not include X11 authentication\r
+data if using X11 forwarding.\r
+\r
+Note that this will only omit data that PuTTY \e{knows} to be a\r
+password. However, if you start another login session within your\r
+PuTTY session, for instance, any password used will appear in the\r
+clear in the packet log. The next option may be of use to protect\r
+against this.\r
+\r
+This option is enabled by default.\r
+\r
+\S2{config-logssh-omitdata} \q{Omit session data}\r
+\r
+\cfg{winhelp-topic}{logging.ssh.omitdata}\r
+\r
+When checked, all decrypted \q{session data} is omitted; this is\r
+defined as data in terminal sessions and in forwarded channels (TCP,\r
+X11, and authentication agent). This will usually substantially reduce\r
+the size of the resulting log file.\r
+\r
+This option is disabled by default.\r
+\r
+\H{config-terminal} The Terminal panel\r
+\r
+The Terminal configuration panel allows you to control the behaviour\r
+of PuTTY's \i{terminal emulation}.\r
+\r
+\S{config-autowrap} \q{Auto wrap mode initially on}\r
+\r
+\cfg{winhelp-topic}{terminal.autowrap}\r
+\r
+\ii{Auto wrap mode} controls what happens when text printed in a PuTTY\r
+window reaches the right-hand edge of the window.\r
+\r
+With auto wrap mode on, if a long line of text reaches the\r
+right-hand edge, it will wrap over on to the next line so you can\r
+still see all the text. With auto wrap mode off, the cursor will\r
+stay at the right-hand edge of the screen, and all the characters in\r
+the line will be printed on top of each other.\r
+\r
+If you are running a full-screen application and you occasionally\r
+find the screen scrolling up when it looks as if it shouldn't, you\r
+could try turning this option off.\r
+\r
+Auto wrap mode can be turned on and off by \i{control sequence}s sent by\r
+the server. This configuration option controls the \e{default}\r
+state, which will be restored when you reset the terminal (see\r
+\k{reset-terminal}). However, if you modify this option in\r
+mid-session using \q{Change Settings}, it will take effect\r
+immediately.\r
+\r
+\S{config-decom} \q{DEC Origin Mode initially on}\r
+\r
+\cfg{winhelp-topic}{terminal.decom}\r
+\r
+\i{DEC Origin Mode} is a minor option which controls how PuTTY\r
+interprets cursor-position \i{control sequence}s sent by the server.\r
+\r
+The server can send a control sequence that restricts the \i{scrolling\r
+region} of the display. For example, in an editor, the server might\r
+reserve a line at the top of the screen and a line at the bottom,\r
+and might send a control sequence that causes scrolling operations\r
+to affect only the remaining lines.\r
+\r
+With DEC Origin Mode on, \i{cursor coordinates} are counted from the top\r
+of the scrolling region. With it turned off, cursor coordinates are\r
+counted from the top of the whole screen regardless of the scrolling\r
+region.\r
+\r
+It is unlikely you would need to change this option, but if you find\r
+a full-screen application is displaying pieces of text in what looks\r
+like the wrong part of the screen, you could try turning DEC Origin\r
+Mode on to see whether that helps.\r
+\r
+DEC Origin Mode can be turned on and off by control sequences sent\r
+by the server. This configuration option controls the \e{default}\r
+state, which will be restored when you reset the terminal (see\r
+\k{reset-terminal}). However, if you modify this option in\r
+mid-session using \q{Change Settings}, it will take effect\r
+immediately.\r
+\r
+\S{config-crlf} \q{Implicit CR in every LF}\r
+\r
+\cfg{winhelp-topic}{terminal.lfhascr}\r
+\r
+Most servers send two control characters, \i{CR} and \i{LF}, to start a\r
+\i{new line} of the screen. The CR character makes the cursor return to the\r
+left-hand side of the screen. The LF character makes the cursor move\r
+one line down (and might make the screen scroll).\r
+\r
+Some servers only send LF, and expect the terminal to move the\r
+cursor over to the left automatically. If you come across a server\r
+that does this, you will see a \I{stair-stepping}stepped effect on the\r
+screen, like this:\r
+\r
+\c First line of text\r
+\c                   Second line\r
+\c                              Third line\r
+\r
+If this happens to you, try enabling the \q{Implicit CR in every LF}\r
+option, and things might go back to normal:\r
+\r
+\c First line of text\r
+\c Second line\r
+\c Third line\r
+\r
+\S{config-lfcr} \q{Implicit LF in every CR}\r
+\r
+\cfg{winhelp-topic}{terminal.crhaslf}\r
+\r
+Most servers send two control characters, \i{CR} and \i{LF}, to start a\r
+\i{new line} of the screen. The CR character makes the cursor return to the\r
+left-hand side of the screen. The LF character makes the cursor move\r
+one line down (and might make the screen scroll).\r
+\r
+Some servers only send CR, and so the newly \r
+written line is overwritten by the following line. This option causes \r
+a line feed so that all lines are displayed.\r
+\r
+\S{config-erase} \q{Use \i{background colour} to erase screen}\r
+\r
+\cfg{winhelp-topic}{terminal.bce}\r
+\r
+Not all terminals agree on what colour to turn the screen when the\r
+server sends a \q{\i{clear screen}} sequence. Some terminals believe the\r
+screen should always be cleared to the \e{default} background\r
+colour. Others believe the screen should be cleared to whatever the\r
+server has selected as a background colour.\r
+\r
+There exist applications that expect both kinds of behaviour.\r
+Therefore, PuTTY can be configured to do either.\r
+\r
+With this option disabled, screen clearing is always done in the\r
+default background colour. With this option enabled, it is done in\r
+the \e{current} background colour.\r
+\r
+Background-colour erase can be turned on and off by \i{control\r
+sequences} sent by the server. This configuration option controls the\r
+\e{default} state, which will be restored when you reset the\r
+terminal (see \k{reset-terminal}). However, if you modify this\r
+option in mid-session using \q{Change Settings}, it will take effect\r
+immediately.\r
+\r
+\S{config-blink} \q{Enable \i{blinking text}}\r
+\r
+\cfg{winhelp-topic}{terminal.blink}\r
+\r
+The server can ask PuTTY to display text that blinks on and off.\r
+This is very distracting, so PuTTY allows you to turn blinking text\r
+off completely.\r
+\r
+When blinking text is disabled and the server attempts to make some\r
+text blink, PuTTY will instead display the text with a \I{background\r
+colour, bright}bolded background colour.\r
+\r
+Blinking text can be turned on and off by \i{control sequence}s sent by\r
+the server. This configuration option controls the \e{default}\r
+state, which will be restored when you reset the terminal (see\r
+\k{reset-terminal}). However, if you modify this option in\r
+mid-session using \q{Change Settings}, it will take effect\r
+immediately.\r
+\r
+\S{config-answerback} \q{\ii{Answerback} to ^E}\r
+\r
+\cfg{winhelp-topic}{terminal.answerback}\r
+\r
+This option controls what PuTTY will send back to the server if the\r
+server sends it the ^E \i{enquiry character}. Normally it just sends\r
+the string \q{PuTTY}.\r
+\r
+If you accidentally write the contents of a binary file to your\r
+terminal, you will probably find that it contains more than one ^E\r
+character, and as a result your next command line will probably read\r
+\q{PuTTYPuTTYPuTTY...} as if you had typed the answerback string\r
+multiple times at the keyboard. If you set the answerback string to\r
+be empty, this problem should go away, but doing so might cause\r
+other problems.\r
+\r
+Note that this is \e{not} the feature of PuTTY which the server will\r
+typically use to determine your terminal type. That feature is the\r
+\q{\ii{Terminal-type} string} in the Connection panel; see\r
+\k{config-termtype} for details.\r
+\r
+You can include control characters in the answerback string using\r
+\c{^C} notation. (Use \c{^~} to get a literal \c{^}.)\r
+\r
+\S{config-localecho} \q{\ii{Local echo}}\r
+\r
+\cfg{winhelp-topic}{terminal.localecho}\r
+\r
+With local echo disabled, characters you type into the PuTTY window\r
+are not echoed in the window \e{by PuTTY}. They are simply sent to\r
+the server. (The \e{server} might choose to \I{remote echo}echo them\r
+back to you; this can't be controlled from the PuTTY control panel.)\r
+\r
+Some types of session need local echo, and many do not. In its\r
+default mode, PuTTY will automatically attempt to deduce whether or\r
+not local echo is appropriate for the session you are working in. If\r
+you find it has made the wrong decision, you can use this\r
+configuration option to override its choice: you can force local\r
+echo to be turned on, or force it to be turned off, instead of\r
+relying on the automatic detection.\r
+\r
+\S{config-localedit} \q{\ii{Local line editing}}\r
+\r
+\cfg{winhelp-topic}{terminal.localedit}\r
+\r
+Normally, every character you type into the PuTTY window is sent\r
+immediately to the server the moment you type it.\r
+\r
+If you enable local line editing, this changes. PuTTY will let you\r
+edit a whole line at a time locally, and the line will only be sent\r
+to the server when you press Return. If you make a mistake, you can\r
+use the Backspace key to correct it before you press Return, and the\r
+server will never see the mistake.\r
+\r
+Since it is hard to edit a line locally without being able to see\r
+it, local line editing is mostly used in conjunction with \i{local echo}\r
+(\k{config-localecho}). This makes it ideal for use in raw mode\r
+\#{FIXME} or when connecting to \i{MUD}s or \i{talker}s. (Although some more\r
+advanced MUDs do occasionally turn local line editing on and turn\r
+local echo off, in order to accept a password from the user.)\r
+\r
+Some types of session need local line editing, and many do not. In\r
+its default mode, PuTTY will automatically attempt to deduce whether\r
+or not local line editing is appropriate for the session you are\r
+working in. If you find it has made the wrong decision, you can use\r
+this configuration option to override its choice: you can force\r
+local line editing to be turned on, or force it to be turned off,\r
+instead of relying on the automatic detection.\r
+\r
+\S{config-printing} \ii{Remote-controlled printing}\r
+\r
+\cfg{winhelp-topic}{terminal.printing}\r
+\r
+A lot of VT100-compatible terminals support printing under control\r
+of the remote server. PuTTY supports this feature as well, but it is\r
+turned off by default.\r
+\r
+To enable remote-controlled printing, choose a printer from the\r
+\q{Printer to send ANSI printer output to} drop-down list box. This\r
+should allow you to select from all the printers you have installed\r
+drivers for on your computer. Alternatively, you can type the\r
+network name of a networked printer (for example,\r
+\c{\\\\printserver\\printer1}) even if you haven't already\r
+installed a driver for it on your own machine.\r
+\r
+When the remote server attempts to print some data, PuTTY will send\r
+that data to the printer \e{raw} - without translating it,\r
+attempting to format it, or doing anything else to it. It is up to\r
+you to ensure your remote server knows what type of printer it is\r
+talking to.\r
+\r
+Since PuTTY sends data to the printer raw, it cannot offer options\r
+such as portrait versus landscape, print quality, or paper tray\r
+selection. All these things would be done by your PC printer driver\r
+(which PuTTY bypasses); if you need them done, you will have to find\r
+a way to configure your remote server to do them.\r
+\r
+To disable remote printing again, choose \q{None (printing\r
+disabled)} from the printer selection list. This is the default\r
+state.\r
+\r
+\H{config-keyboard} The Keyboard panel\r
+\r
+The Keyboard configuration panel allows you to control the behaviour\r
+of the \i{keyboard} in PuTTY.  The correct state for many of these\r
+settings depends on what the server to which PuTTY is connecting\r
+expects.  With a \i{Unix} server, this is likely to depend on the\r
+\i\c{termcap} or \i\c{terminfo} entry it uses, which in turn is likely to\r
+be controlled by the \q{\ii{Terminal-type} string} setting in the Connection\r
+panel; see \k{config-termtype} for details.  If none of the settings here\r
+seems to help, you may find \k{faq-keyboard} to be useful.\r
+\r
+\S{config-backspace} Changing the action of the \ii{Backspace key}\r
+\r
+\cfg{winhelp-topic}{keyboard.backspace}\r
+\r
+Some terminals believe that the Backspace key should send the same\r
+thing to the server as \i{Control-H} (ASCII code 8). Other terminals\r
+believe that the Backspace key should send ASCII code 127 (usually\r
+known as \i{Control-?}) so that it can be distinguished from Control-H.\r
+This option allows you to choose which code PuTTY generates when you\r
+press Backspace.\r
+\r
+If you are connecting over SSH, PuTTY by default tells the server\r
+the value of this option (see \k{config-ttymodes}), so you may find\r
+that the Backspace key does the right thing either way. Similarly,\r
+if you are connecting to a \i{Unix} system, you will probably find that\r
+the Unix \i\c{stty} command lets you configure which the server\r
+expects to see, so again you might not need to change which one PuTTY\r
+generates. On other systems, the server's expectation might be fixed\r
+and you might have no choice but to configure PuTTY.\r
+\r
+If you do have the choice, we recommend configuring PuTTY to\r
+generate Control-? and configuring the server to expect it, because\r
+that allows applications such as \c{emacs} to use Control-H for\r
+help.\r
+\r
+(Typing \i{Shift-Backspace} will cause PuTTY to send whichever code\r
+isn't configured here as the default.)\r
+\r
+\S{config-homeend} Changing the action of the \i{Home and End keys}\r
+\r
+\cfg{winhelp-topic}{keyboard.homeend}\r
+\r
+The Unix terminal emulator \i\c{rxvt} disagrees with the rest of the\r
+world about what character sequences should be sent to the server by\r
+the Home and End keys.\r
+\r
+\i\c{xterm}, and other terminals, send \c{ESC [1~} for the Home key,\r
+and \c{ESC [4~} for the End key. \c{rxvt} sends \c{ESC [H} for the\r
+Home key and \c{ESC [Ow} for the End key.\r
+\r
+If you find an application on which the Home and End keys aren't\r
+working, you could try switching this option to see if it helps.\r
+\r
+\S{config-funkeys} Changing the action of the \i{function keys} and\r
+\i{keypad}\r
+\r
+\cfg{winhelp-topic}{keyboard.funkeys}\r
+\r
+This option affects the function keys (F1 to F12) and the top row of\r
+the numeric keypad.\r
+\r
+\b In the default mode, labelled \c{ESC [n~}, the function keys\r
+generate sequences like \c{ESC [11~}, \c{ESC [12~} and so on. This\r
+matches the general behaviour of Digital's terminals.\r
+\r
+\b In Linux mode, F6 to F12 behave just like the default mode, but\r
+F1 to F5 generate \c{ESC [[A} through to \c{ESC [[E}. This mimics the\r
+\i{Linux virtual console}.\r
+\r
+\b In \I{xterm}Xterm R6 mode, F5 to F12 behave like the default mode, but F1\r
+to F4 generate \c{ESC OP} through to \c{ESC OS}, which are the\r
+sequences produced by the top row of the \e{keypad} on Digital's\r
+terminals.\r
+\r
+\b In \i{VT400} mode, all the function keys behave like the default\r
+mode, but the actual top row of the numeric keypad generates \c{ESC\r
+OP} through to \c{ESC OS}.\r
+\r
+\b In \i{VT100+} mode, the function keys generate \c{ESC OP} through to\r
+\c{ESC O[}\r
+\r
+\b In \i{SCO} mode, the function keys F1 to F12 generate \c{ESC [M}\r
+through to \c{ESC [X}.  Together with shift, they generate \c{ESC [Y}\r
+through to \c{ESC [j}.  With control they generate \c{ESC [k} through\r
+to \c{ESC [v}, and with shift and control together they generate\r
+\c{ESC [w} through to \c{ESC [\{}.\r
+\r
+If you don't know what any of this means, you probably don't need to\r
+fiddle with it.\r
+\r
+\S{config-appcursor} Controlling \i{Application Cursor Keys} mode\r
+\r
+\cfg{winhelp-topic}{keyboard.appcursor}\r
+\r
+Application Cursor Keys mode is a way for the server to change the\r
+control sequences sent by the arrow keys. In normal mode, the arrow\r
+keys send \c{ESC [A} through to \c{ESC [D}. In application mode,\r
+they send \c{ESC OA} through to \c{ESC OD}.\r
+\r
+Application Cursor Keys mode can be turned on and off by the server,\r
+depending on the application. PuTTY allows you to configure the\r
+initial state.\r
+\r
+You can also disable application cursor keys mode completely, using\r
+the \q{Features} configuration panel; see\r
+\k{config-features-application}.\r
+\r
+\S{config-appkeypad} Controlling \i{Application Keypad} mode\r
+\r
+\cfg{winhelp-topic}{keyboard.appkeypad}\r
+\r
+Application Keypad mode is a way for the server to change the\r
+behaviour of the numeric keypad.\r
+\r
+In normal mode, the keypad behaves like a normal Windows keypad:\r
+with \i{NumLock} on, the number keys generate numbers, and with NumLock\r
+off they act like the arrow keys and Home, End etc.\r
+\r
+In application mode, all the keypad keys send special control\r
+sequences, \e{including} Num Lock. Num Lock stops behaving like Num\r
+Lock and becomes another function key.\r
+\r
+Depending on which version of Windows you run, you may find the Num\r
+Lock light still flashes on and off every time you press Num Lock,\r
+even when application mode is active and Num Lock is acting like a\r
+function key. This is unavoidable.\r
+\r
+Application keypad mode can be turned on and off by the server,\r
+depending on the application. PuTTY allows you to configure the\r
+initial state.\r
+\r
+You can also disable application keypad mode completely, using the\r
+\q{Features} configuration panel; see\r
+\k{config-features-application}.\r
+\r
+\S{config-nethack} Using \i{NetHack keypad mode}\r
+\r
+\cfg{winhelp-topic}{keyboard.nethack}\r
+\r
+PuTTY has a special mode for playing NetHack. You can enable it by\r
+selecting \q{NetHack} in the \q{Initial state of numeric keypad}\r
+control.\r
+\r
+In this mode, the numeric keypad keys 1-9 generate the NetHack\r
+movement commands (\cw{hjklyubn}). The 5 key generates the \c{.}\r
+command (do nothing).\r
+\r
+In addition, pressing Shift or Ctrl with the keypad keys generate\r
+the Shift- or Ctrl-keys you would expect (e.g. keypad-7 generates\r
+\cq{y}, so Shift-keypad-7 generates \cq{Y} and Ctrl-keypad-7\r
+generates Ctrl-Y); these commands tell NetHack to keep moving you in\r
+the same direction until you encounter something interesting.\r
+\r
+For some reason, this feature only works properly when \i{Num Lock} is\r
+on. We don't know why.\r
+\r
+\S{config-compose} Enabling a DEC-like \ii{Compose key}\r
+\r
+\cfg{winhelp-topic}{keyboard.compose}\r
+\r
+DEC terminals have a Compose key, which provides an easy-to-remember\r
+way of typing \i{accented characters}. You press Compose and then type\r
+two more characters. The two characters are \q{combined} to produce\r
+an accented character. The choices of character are designed to be\r
+easy to remember; for example, composing \q{e} and \q{`} produces\r
+the \q{\u00e8{e-grave}} character.\r
+\r
+If your keyboard has a Windows \i{Application key}, it acts as a Compose\r
+key in PuTTY. Alternatively, if you enable the \q{\i{AltGr} acts as\r
+Compose key} option, the AltGr key will become a Compose key.\r
+\r
+\S{config-ctrlalt} \q{Control-Alt is different from \i{AltGr}}\r
+\r
+\cfg{winhelp-topic}{keyboard.ctrlalt}\r
+\r
+Some old keyboards do not have an AltGr key, which can make it\r
+difficult to type some characters. PuTTY can be configured to treat\r
+the key combination Ctrl + Left Alt the same way as the AltGr key.\r
+\r
+By default, this checkbox is checked, and the key combination Ctrl +\r
+Left Alt does something completely different. PuTTY's usual handling\r
+of the left Alt key is to prefix the Escape (Control-\cw{[})\r
+character to whatever character sequence the rest of the keypress\r
+would generate. For example, Alt-A generates Escape followed by\r
+\c{a}. So Alt-Ctrl-A would generate Escape, followed by Control-A.\r
+\r
+If you uncheck this box, Ctrl-Alt will become a synonym for AltGr,\r
+so you can use it to type extra graphic characters if your keyboard\r
+has any.\r
+\r
+(However, Ctrl-Alt will never act as a Compose key, regardless of the\r
+setting of \q{AltGr acts as Compose key} described in\r
+\k{config-compose}.)\r
+\r
+\H{config-bell} The Bell panel\r
+\r
+The Bell panel controls the \i{terminal bell} feature: the server's\r
+ability to cause PuTTY to beep at you.\r
+\r
+In the default configuration, when the server sends the character\r
+with ASCII code 7 (Control-G), PuTTY will play the \i{Windows Default\r
+Beep} sound. This is not always what you want the terminal bell\r
+feature to do; the Bell panel allows you to configure alternative\r
+actions.\r
+\r
+\S{config-bellstyle} \q{Set the style of bell}\r
+\r
+\cfg{winhelp-topic}{bell.style}\r
+\r
+This control allows you to select various different actions to occur\r
+on a terminal bell:\r
+\r
+\b Selecting \q{None} \I{terminal bell, disabling}disables the bell\r
+completely. In this mode, the server can send as many Control-G\r
+characters as it likes and nothing at all will happen.\r
+\r
+\b \q{Make default system alert sound} is the default setting. It\r
+causes the Windows \q{Default Beep} sound to be played. To change\r
+what this sound is, or to test it if nothing seems to be happening,\r
+use the Sound configurer in the Windows Control Panel.\r
+\r
+\b \q{\ii{Visual bell}} is a silent alternative to a beeping computer. In\r
+this mode, when the server sends a Control-G, the whole PuTTY window\r
+will flash white for a fraction of a second.\r
+\r
+\b \q{Beep using the \i{PC speaker}} is self-explanatory.\r
+\r
+\b \q{Play a custom \i{sound file}} allows you to specify a particular\r
+sound file to be used by PuTTY alone, or even by a particular\r
+individual PuTTY session. This allows you to distinguish your PuTTY\r
+beeps from any other beeps on the system. If you select this option,\r
+you will also need to enter the name of your sound file in the edit\r
+control \q{Custom sound file to play as a bell}.\r
+\r
+\S{config-belltaskbar} \q{\ii{Taskbar}/\I{window caption}caption\r
+indication on bell}\r
+\r
+\cfg{winhelp-topic}{bell.taskbar}\r
+\r
+This feature controls what happens to the PuTTY window's entry in\r
+the Windows Taskbar if a bell occurs while the window does not have\r
+the input focus.\r
+\r
+In the default state (\q{Disabled}) nothing unusual happens.\r
+\r
+If you select \q{Steady}, then when a bell occurs and the window is\r
+not in focus, the window's Taskbar entry and its title bar will\r
+change colour to let you know that PuTTY session is asking for your\r
+attention. The change of colour will persist until you select the\r
+window, so you can leave several PuTTY windows minimised in your\r
+terminal, go away from your keyboard, and be sure not to have missed\r
+any important beeps when you get back.\r
+\r
+\q{Flashing} is even more eye-catching: the Taskbar entry will\r
+continuously flash on and off until you select the window.\r
+\r
+\S{config-bellovl} \q{Control the \i{bell overload} behaviour}\r
+\r
+\cfg{winhelp-topic}{bell.overload}\r
+\r
+A common user error in a terminal session is to accidentally run the\r
+Unix command \c{cat} (or equivalent) on an inappropriate file type,\r
+such as an executable, image file, or ZIP file. This produces a huge\r
+stream of non-text characters sent to the terminal, which typically\r
+includes a lot of bell characters. As a result of this the terminal\r
+often doesn't stop beeping for ten minutes, and everybody else in\r
+the office gets annoyed.\r
+\r
+To try to avoid this behaviour, or any other cause of excessive\r
+beeping, PuTTY includes a bell overload management feature. In the\r
+default configuration, receiving more than five bell characters in a\r
+two-second period will cause the overload feature to activate. Once\r
+the overload feature is active, further bells will \I{terminal bell,\r
+disabling} have no effect at all, so the rest of your binary file\r
+will be sent to the screen in silence. After a period of five seconds\r
+during which no further bells are received, the overload feature will\r
+turn itself off again and bells will be re-enabled.\r
+\r
+If you want this feature completely disabled, you can turn it off\r
+using the checkbox \q{Bell is temporarily disabled when over-used}.\r
+\r
+Alternatively, if you like the bell overload feature but don't agree\r
+with the settings, you can configure the details: how many bells\r
+constitute an overload, how short a time period they have to arrive\r
+in to do so, and how much silent time is required before the\r
+overload feature will deactivate itself.\r
+\r
+Bell overload mode is always deactivated by any keypress in the\r
+terminal. This means it can respond to large unexpected streams of\r
+data, but does not interfere with ordinary command-line activities\r
+that generate beeps (such as filename completion).\r
+\r
+\H{config-features} The Features panel\r
+\r
+PuTTY's \i{terminal emulation} is very highly featured, and can do a lot\r
+of things under remote server control. Some of these features can\r
+cause problems due to buggy or strangely configured server\r
+applications.\r
+\r
+The Features configuration panel allows you to disable some of\r
+PuTTY's more advanced terminal features, in case they cause trouble.\r
+\r
+\S{config-features-application} Disabling application keypad and cursor keys\r
+\r
+\cfg{winhelp-topic}{features.application}\r
+\r
+\I{Application Keypad}Application keypad mode (see\r
+\k{config-appkeypad}) and \I{Application Cursor Keys}application\r
+cursor keys mode (see \k{config-appcursor}) alter the behaviour of\r
+the keypad and cursor keys. Some applications enable these modes but\r
+then do not deal correctly with the modified keys. You can force\r
+these modes to be permanently disabled no matter what the server\r
+tries to do.\r
+\r
+\S{config-features-mouse} Disabling \cw{xterm}-style \i{mouse reporting}\r
+\r
+\cfg{winhelp-topic}{features.mouse}\r
+\r
+PuTTY allows the server to send \i{control codes} that let it take over\r
+the mouse and use it for purposes other than \i{copy and paste}.\r
+Applications which use this feature include the text-mode web\r
+browser \i\c{links}, the Usenet newsreader \i\c{trn} version 4, and the\r
+file manager \i\c{mc} (Midnight Commander).\r
+\r
+If you find this feature inconvenient, you can disable it using the\r
+\q{Disable xterm-style mouse reporting} control. With this box\r
+ticked, the mouse will \e{always} do copy and paste in the normal\r
+way.\r
+\r
+Note that even if the application takes over the mouse, you can\r
+still manage PuTTY's copy and paste by holding down the Shift key\r
+while you select and paste, unless you have deliberately turned this\r
+feature off (see \k{config-mouseshift}).\r
+\r
+\S{config-features-resize} Disabling remote \i{terminal resizing}\r
+\r
+\cfg{winhelp-topic}{features.resize}\r
+\r
+PuTTY has the ability to change the terminal's size and position in\r
+response to commands from the server. If you find PuTTY is doing\r
+this unexpectedly or inconveniently, you can tell PuTTY not to\r
+respond to those server commands.\r
+\r
+\S{config-features-altscreen} Disabling switching to the \i{alternate screen}\r
+\r
+\cfg{winhelp-topic}{features.altscreen}\r
+\r
+Many terminals, including PuTTY, support an \q{alternate screen}.\r
+This is the same size as the ordinary terminal screen, but separate.\r
+Typically a screen-based program such as a text editor might switch\r
+the terminal to the alternate screen before starting up. Then at the\r
+end of the run, it switches back to the primary screen, and you see\r
+the screen contents just as they were before starting the editor.\r
+\r
+Some people prefer this not to happen. If you want your editor to\r
+run in the same screen as the rest of your terminal activity, you\r
+can disable the alternate screen feature completely.\r
+\r
+\S{config-features-retitle} Disabling remote \i{window title} changing\r
+\r
+\cfg{winhelp-topic}{features.retitle}\r
+\r
+PuTTY has the ability to change the window title in response to\r
+commands from the server. If you find PuTTY is doing this\r
+unexpectedly or inconveniently, you can tell PuTTY not to respond to\r
+those server commands.\r
+\r
+\S{config-features-qtitle} Response to remote \i{window title} querying\r
+\r
+\cfg{winhelp-topic}{features.qtitle}\r
+\r
+PuTTY can optionally provide the xterm service of allowing server\r
+applications to find out the local window title. This feature is\r
+disabled by default, but you can turn it on if you really want it.\r
+\r
+NOTE that this feature is a \e{potential \i{security hazard}}. If a\r
+malicious application can write data to your terminal (for example,\r
+if you merely \c{cat} a file owned by someone else on the server\r
+machine), it can change your window title (unless you have disabled\r
+this as mentioned in \k{config-features-retitle}) and then use this\r
+service to have the new window title sent back to the server as if\r
+typed at the keyboard. This allows an attacker to fake keypresses\r
+and potentially cause your server-side applications to do things you\r
+didn't want. Therefore this feature is disabled by default, and we\r
+recommend you do not set it to \q{Window title} unless you \e{really}\r
+know what you are doing.\r
+\r
+There are three settings for this option:\r
+\r
+\dt \q{None}\r
+\r
+\dd PuTTY makes no response whatsoever to the relevant escape\r
+sequence. This may upset server-side software that is expecting some\r
+sort of response.\r
+\r
+\dt \q{Empty string}\r
+\r
+\dd PuTTY makes a well-formed response, but leaves it blank. Thus,\r
+server-side software that expects a response is kept happy, but an\r
+attacker cannot influence the response string. This is probably the\r
+setting you want if you have no better ideas.\r
+\r
+\dt \q{Window title}\r
+\r
+\dd PuTTY responds with the actual window title. This is dangerous for\r
+the reasons described above.\r
+\r
+\S{config-features-dbackspace} Disabling \i{destructive backspace}\r
+\r
+\cfg{winhelp-topic}{features.dbackspace}\r
+\r
+Normally, when PuTTY receives character 127 (^?) from the server, it\r
+will perform a \q{destructive backspace}: move the cursor one space\r
+left and delete the character under it. This can apparently cause\r
+problems in some applications, so PuTTY provides the ability to\r
+configure character 127 to perform a normal backspace (without\r
+deleting a character) instead.\r
+\r
+\S{config-features-charset} Disabling remote \i{character set}\r
+configuration\r
+\r
+\cfg{winhelp-topic}{features.charset}\r
+\r
+PuTTY has the ability to change its character set configuration in\r
+response to commands from the server. Some programs send these\r
+commands unexpectedly or inconveniently. In particular, \i{BitchX} (an\r
+IRC client) seems to have a habit of reconfiguring the character set\r
+to something other than the user intended.\r
+\r
+If you find that accented characters are not showing up the way you\r
+expect them to, particularly if you're running BitchX, you could try\r
+disabling the remote character set configuration commands.\r
+\r
+\S{config-features-shaping} Disabling \i{Arabic text shaping}\r
+\r
+\cfg{winhelp-topic}{features.arabicshaping}\r
+\r
+PuTTY supports shaping of Arabic text, which means that if your\r
+server sends text written in the basic \i{Unicode} Arabic alphabet then\r
+it will convert it to the correct display forms before printing it\r
+on the screen.\r
+\r
+If you are using full-screen software which was not expecting this\r
+to happen (especially if you are not an Arabic speaker and you\r
+unexpectedly find yourself dealing with Arabic text files in\r
+applications which are not Arabic-aware), you might find that the\r
+\i{display becomes corrupted}. By ticking this box, you can disable\r
+Arabic text shaping so that PuTTY displays precisely the characters\r
+it is told to display.\r
+\r
+You may also find you need to disable bidirectional text display;\r
+see \k{config-features-bidi}.\r
+\r
+\S{config-features-bidi} Disabling \i{bidirectional text} display\r
+\r
+\cfg{winhelp-topic}{features.bidi}\r
+\r
+PuTTY supports bidirectional text display, which means that if your\r
+server sends text written in a language which is usually displayed\r
+from right to left (such as \i{Arabic} or \i{Hebrew}) then PuTTY will\r
+automatically flip it round so that it is displayed in the right\r
+direction on the screen.\r
+\r
+If you are using full-screen software which was not expecting this\r
+to happen (especially if you are not an Arabic speaker and you\r
+unexpectedly find yourself dealing with Arabic text files in\r
+applications which are not Arabic-aware), you might find that the\r
+\i{display becomes corrupted}. By ticking this box, you can disable\r
+bidirectional text display, so that PuTTY displays text from left to\r
+right in all situations.\r
+\r
+You may also find you need to disable Arabic text shaping;\r
+see \k{config-features-shaping}.\r
+\r
+\H{config-window} The Window panel\r
+\r
+The Window configuration panel allows you to control aspects of the\r
+\i{PuTTY window}.\r
+\r
+\S{config-winsize} Setting the \I{window size}size of the PuTTY window\r
+\r
+\cfg{winhelp-topic}{window.size}\r
+\r
+The \q{\ii{Columns}} and \q{\ii{Rows}} boxes let you set the PuTTY\r
+window to a precise size. Of course you can also \I{window resizing}drag\r
+the window to a new size while a session is running.\r
+\r
+\S{config-winsizelock} What to do when the window is resized\r
+\r
+\cfg{winhelp-topic}{window.resize}\r
+\r
+These options allow you to control what happens when the user tries\r
+to \I{window resizing}resize the PuTTY window using its window furniture.\r
+\r
+There are four options here:\r
+\r
+\b \q{Change the number of rows and columns}: the font size will not\r
+change. (This is the default.)\r
+\r
+\b \q{Change the size of the font}: the number of rows and columns in\r
+the terminal will stay the same, and the \i{font size} will change.\r
+\r
+\b \q{Change font size when maximised}: when the window is resized,\r
+the number of rows and columns will change, \e{except} when the window\r
+is \i{maximise}d (or restored), when the font size will change. (In\r
+this mode, holding down the Alt key while resizing will also cause the\r
+font size to change.)\r
+\r
+\b \q{Forbid resizing completely}: the terminal will refuse to be\r
+resized at all.\r
+\r
+\S{config-scrollback} Controlling \i{scrollback}\r
+\r
+\cfg{winhelp-topic}{window.scrollback}\r
+\r
+These options let you configure the way PuTTY keeps text after it\r
+scrolls off the top of the screen (see \k{using-scrollback}).\r
+\r
+The \q{Lines of scrollback} box lets you configure how many lines of\r
+text PuTTY keeps. The \q{Display scrollbar} options allow you to\r
+hide the \i{scrollbar} (although you can still view the scrollback using\r
+the keyboard as described in \k{using-scrollback}). You can separately\r
+configure whether the scrollbar is shown in \i{full-screen} mode and in\r
+normal modes.\r
+\r
+If you are viewing part of the scrollback when the server sends more\r
+text to PuTTY, the screen will revert to showing the current\r
+terminal contents. You can disable this behaviour by turning off\r
+\q{Reset scrollback on display activity}. You can also make the\r
+screen revert when you press a key, by turning on \q{Reset\r
+scrollback on keypress}.\r
+\r
+\S{config-erasetoscrollback} \q{Push erased text into scrollback}\r
+\r
+\cfg{winhelp-topic}{window.erased}\r
+\r
+When this option is enabled, the contents of the terminal screen\r
+will be pushed into the scrollback when a server-side application\r
+clears the screen, so that your scrollback will contain a better\r
+record of what was on your screen in the past.\r
+\r
+If the application switches to the \i{alternate screen} (see\r
+\k{config-features-altscreen} for more about this), then the\r
+contents of the primary screen will be visible in the scrollback\r
+until the application switches back again.\r
+\r
+This option is enabled by default.\r
+\r
+\H{config-appearance} The Appearance panel\r
+\r
+The Appearance configuration panel allows you to control aspects of\r
+the appearance of \I{PuTTY window}PuTTY's window.\r
+\r
+\S{config-cursor} Controlling the appearance of the \i{cursor}\r
+\r
+\cfg{winhelp-topic}{appearance.cursor}\r
+\r
+The \q{Cursor appearance} option lets you configure the cursor to be\r
+a block, an underline, or a vertical line. A block cursor becomes an\r
+empty box when the window loses focus; an underline or a vertical\r
+line becomes dotted.\r
+\r
+The \q{\ii{Cursor blinks}} option makes the cursor blink on and off. This\r
+works in any of the cursor modes.\r
+\r
+\S{config-font} Controlling the \i{font} used in the terminal window\r
+\r
+\cfg{winhelp-topic}{appearance.font}\r
+\r
+This option allows you to choose what font, in what \I{font size}size,\r
+the PuTTY terminal window uses to display the text in the session.\r
+\r
+By default, you will be offered a choice from all the fixed-width\r
+fonts installed on the system, since VT100-style terminal handling\r
+expects a fixed-width font. If you tick the box marked \q{Allow\r
+selection of variable-pitch fonts}, however, PuTTY will offer\r
+variable-width fonts as well: if you select one of these, the font\r
+will be coerced into fixed-size character cells, which will probably\r
+not look very good (but can work OK with some fonts).\r
+\r
+\S{config-mouseptr} \q{Hide \i{mouse pointer} when typing in window}\r
+\r
+\cfg{winhelp-topic}{appearance.hidemouse}\r
+\r
+If you enable this option, the mouse pointer will disappear if the\r
+PuTTY window is selected and you press a key. This way, it will not\r
+obscure any of the text in the window while you work in your\r
+session. As soon as you move the mouse, the pointer will reappear.\r
+\r
+This option is disabled by default, so the mouse pointer remains\r
+visible at all times.\r
+\r
+\S{config-winborder} Controlling the \i{window border}\r
+\r
+\cfg{winhelp-topic}{appearance.border}\r
+\r
+PuTTY allows you to configure the appearance of the window border to\r
+some extent.\r
+\r
+The checkbox marked \q{Sunken-edge border} changes the appearance of\r
+the window border to something more like a DOS box: the inside edge\r
+of the border is highlighted as if it sank down to meet the surface\r
+inside the window. This makes the border a little bit thicker as\r
+well. It's hard to describe well. Try it and see if you like it.\r
+\r
+You can also configure a completely blank gap between the text in\r
+the window and the border, using the \q{Gap between text and window\r
+edge} control. By default this is set at one pixel. You can reduce\r
+it to zero, or increase it further.\r
+\r
+\H{config-behaviour} The Behaviour panel\r
+\r
+The Behaviour configuration panel allows you to control aspects of\r
+the behaviour of \I{PuTTY window}PuTTY's window.\r
+\r
+\S{config-title} Controlling the \i{window title}\r
+\r
+\cfg{winhelp-topic}{appearance.title}\r
+\r
+The \q{Window title} edit box allows you to set the title of the\r
+PuTTY window. By default the window title will contain the \i{host name}\r
+followed by \q{PuTTY}, for example \c{server1.example.com - PuTTY}.\r
+If you want a different window title, this is where to set it.\r
+\r
+PuTTY allows the server to send \c{xterm} \i{control sequence}s which\r
+modify the title of the window in mid-session (unless this is disabled -\r
+see \k{config-features-retitle}); the title string set here\r
+is therefore only the \e{initial} window title.\r
+\r
+As well as the \e{window} title, there is also an \c{xterm}\r
+sequence to modify the \I{icon title}title of the window's \e{icon}.\r
+This makes sense in a windowing system where the window becomes an\r
+icon when minimised, such as Windows 3.1 or most X Window System\r
+setups; but in the Windows 95-like user interface it isn't as\r
+applicable.\r
+\r
+By default, PuTTY only uses the server-supplied \e{window} title, and\r
+ignores the icon title entirely. If for some reason you want to see\r
+both titles, check the box marked \q{Separate window and icon titles}.\r
+If you do this, PuTTY's window title and Taskbar \I{window caption}caption will\r
+change into the server-supplied icon title if you \i{minimise} the PuTTY\r
+window, and change back to the server-supplied window title if you\r
+restore it. (If the server has not bothered to supply a window or\r
+icon title, none of this will happen.)\r
+\r
+\S{config-warnonclose} \q{Warn before \i{closing window}}\r
+\r
+\cfg{winhelp-topic}{behaviour.closewarn}\r
+\r
+If you press the \i{Close button} in a PuTTY window that contains a\r
+running session, PuTTY will put up a warning window asking if you\r
+really meant to close the window. A window whose session has already\r
+terminated can always be closed without a warning.\r
+\r
+If you want to be able to close a window quickly, you can disable\r
+the \q{Warn before closing window} option.\r
+\r
+\S{config-altf4} \q{Window closes on \i{ALT-F4}}\r
+\r
+\cfg{winhelp-topic}{behaviour.altf4}\r
+\r
+By default, pressing ALT-F4 causes the \I{closing window}window to\r
+close (or a warning box to appear; see \k{config-warnonclose}). If you\r
+disable the \q{Window closes on ALT-F4} option, then pressing ALT-F4\r
+will simply send a key sequence to the server.\r
+\r
+\S{config-altspace} \q{\ii{System menu} appears on \i{ALT-Space}}\r
+\r
+\cfg{winhelp-topic}{behaviour.altspace}\r
+\r
+If this option is enabled, then pressing ALT-Space will bring up the\r
+PuTTY window's menu, like clicking on the top left corner. If it is\r
+disabled, then pressing ALT-Space will just send \c{ESC SPACE} to\r
+the server.\r
+\r
+Some \i{accessibility} programs for Windows may need this option\r
+enabling to be able to control PuTTY's window successfully. For\r
+instance, \i{Dragon NaturallySpeaking} requires it both to open the\r
+system menu via voice, and to close, minimise, maximise and restore\r
+the window.\r
+\r
+\S{config-altonly} \q{\ii{System menu} appears on \i{Alt} alone}\r
+\r
+\cfg{winhelp-topic}{behaviour.altonly}\r
+\r
+If this option is enabled, then pressing and releasing ALT will\r
+bring up the PuTTY window's menu, like clicking on the top left\r
+corner. If it is disabled, then pressing and releasing ALT will have\r
+no effect.\r
+\r
+\S{config-alwaysontop} \q{Ensure window is \i{always on top}}\r
+\r
+\cfg{winhelp-topic}{behaviour.alwaysontop}\r
+\r
+If this option is enabled, the PuTTY window will stay on top of all\r
+other windows.\r
+\r
+\S{config-fullscreen} \q{\ii{Full screen} on Alt-Enter}\r
+\r
+\cfg{winhelp-topic}{behaviour.altenter}\r
+\r
+If this option is enabled, then pressing Alt-Enter will cause the\r
+PuTTY window to become full-screen. Pressing Alt-Enter again will\r
+restore the previous window size.\r
+\r
+The full-screen feature is also available from the \ii{System menu}, even\r
+when it is configured not to be available on the Alt-Enter key. See\r
+\k{using-fullscreen}.\r
+\r
+\H{config-translation} The Translation panel\r
+\r
+The Translation configuration panel allows you to control the\r
+translation between the \i{character set} understood by the server and\r
+the character set understood by PuTTY.\r
+\r
+\S{config-charset} Controlling character set translation\r
+\r
+\cfg{winhelp-topic}{translation.codepage}\r
+\r
+During an interactive session, PuTTY receives a stream of 8-bit\r
+bytes from the server, and in order to display them on the screen it\r
+needs to know what character set to interpret them in. Similarly,\r
+PuTTY needs to know how to translate your keystrokes into the encoding\r
+the server expects. Unfortunately, there is no satisfactory\r
+mechanism for PuTTY and the server to communicate this information,\r
+so it must usually be manually configured.\r
+\r
+There are a lot of character sets to choose from. The \q{Remote\r
+character set} option lets you select one. By default PuTTY will\r
+attempt to choose a character set that is right for your \i{locale} as\r
+reported by Windows; if it gets it wrong, you can select a different\r
+one using this control.\r
+\r
+A few notable character sets are:\r
+\r
+\b The \i{ISO-8859} series are all standard character sets that include\r
+various accented characters appropriate for different sets of\r
+languages.\r
+\r
+\b The \i{Win125x} series are defined by Microsoft, for similar\r
+purposes. In particular Win1252 is almost equivalent to ISO-8859-1,\r
+but contains a few extra characters such as matched quotes and the\r
+Euro symbol.\r
+\r
+\b If you want the old IBM PC character set with block graphics and\r
+line-drawing characters, you can select \q{\i{CP437}}.\r
+\r
+\b PuTTY also supports \i{Unicode} mode, in which the data coming from\r
+the server is interpreted as being in the \i{UTF-8} encoding of Unicode,\r
+and keystrokes are sent UTF-8 encoded. If you select \q{UTF-8} as a\r
+character set you can use this mode. Not all server-side applications\r
+will support it.\r
+\r
+If you need support for a numeric \i{code page} which is not listed in\r
+the drop-down list, such as code page 866, then you can try entering\r
+its name manually (\c{\i{CP866}} for example) in the list box. If the\r
+underlying version of Windows has the appropriate translation table\r
+installed, PuTTY will use it.\r
+\r
+\S{config-cjk-ambig-wide} \q{Treat \i{CJK} ambiguous characters as wide}\r
+\r
+\cfg{winhelp-topic}{translation.cjkambigwide}\r
+\r
+There are \I{East Asian Ambiguous characters}some Unicode characters\r
+whose \I{character width}width is not well-defined. In most contexts, such\r
+characters should be treated as single-width for the purposes of \I{wrapping,\r
+terminal}wrapping and so on; however, in some CJK contexts, they are better\r
+treated as double-width for historical reasons, and some server-side\r
+applications may expect them to be displayed as such. Setting this option\r
+will cause PuTTY to take the double-width interpretation. \r
+\r
+If you use legacy CJK applications, and you find your lines are\r
+wrapping in the wrong places, or you are having other display\r
+problems, you might want to play with this setting.\r
+\r
+This option only has any effect in \i{UTF-8} mode (see \k{config-charset}).\r
+\r
+\S{config-cyr} \q{\i{Caps Lock} acts as \i{Cyrillic} switch}\r
+\r
+\cfg{winhelp-topic}{translation.cyrillic}\r
+\r
+This feature allows you to switch between a US/UK keyboard layout\r
+and a Cyrillic keyboard layout by using the Caps Lock key, if you\r
+need to type (for example) \i{Russian} and English side by side in the\r
+same document.\r
+\r
+Currently this feature is not expected to work properly if your\r
+native keyboard layout is not US or UK.\r
+\r
+\S{config-linedraw} Controlling display of \i{line-drawing characters}\r
+\r
+\cfg{winhelp-topic}{translation.linedraw}\r
+\r
+VT100-series terminals allow the server to send \i{control sequence}s that\r
+shift temporarily into a separate character set for drawing simple\r
+lines and boxes. However, there are a variety of ways in which PuTTY\r
+can attempt to find appropriate characters, and the right one to use\r
+depends on the locally configured \i{font}. In general you should probably\r
+try lots of options until you find one that your particular font\r
+supports.\r
+\r
+\b \q{Use Unicode line drawing code points} tries to use the box\r
+characters that are present in \i{Unicode}. For good Unicode-supporting\r
+fonts this is probably the most reliable and functional option.\r
+\r
+\b \q{Poor man's line drawing} assumes that the font \e{cannot}\r
+generate the line and box characters at all, so it will use the\r
+\c{+}, \c{-} and \c{|} characters to draw approximations to boxes.\r
+You should use this option if none of the other options works.\r
+\r
+\b \q{Font has XWindows encoding} is for use with fonts that have a\r
+special encoding, where the lowest 32 character positions (below the\r
+ASCII printable range) contain the line-drawing characters. This is\r
+unlikely to be the case with any standard Windows font; it will\r
+probably only apply to custom-built fonts or fonts that have been\r
+automatically converted from the X Window System.\r
+\r
+\b \q{Use font in both ANSI and OEM modes} tries to use the same\r
+font in two different character sets, to obtain a wider range of\r
+characters. This doesn't always work; some fonts claim to be a\r
+different size depending on which character set you try to use.\r
+\r
+\b \q{Use font in OEM mode only} is more reliable than that, but can\r
+miss out other characters from the main character set.\r
+\r
+\S{config-linedrawpaste} Controlling \i{copy and paste} of line drawing\r
+characters\r
+\r
+\cfg{winhelp-topic}{selection.linedraw}\r
+\r
+By default, when you copy and paste a piece of the PuTTY screen that\r
+contains VT100 line and box drawing characters, PuTTY will paste\r
+them in the form they appear on the screen: either \i{Unicode} line\r
+drawing code points, or the \q{poor man's} line-drawing characters\r
+\c{+}, \c{-} and \c{|}. The checkbox \q{Copy and paste VT100 line\r
+drawing chars as lqqqk} disables this feature, so line-drawing\r
+characters will be pasted as the \i{ASCII} characters that were printed\r
+to produce them. This will typically mean they come out mostly as\r
+\c{q} and \c{x}, with a scattering of \c{jklmntuvw} at the corners.\r
+This might be useful if you were trying to recreate the same box\r
+layout in another program, for example.\r
+\r
+Note that this option only applies to line-drawing characters which\r
+\e{were} printed by using the VT100 mechanism. Line-drawing\r
+characters that were received as Unicode code points will paste as\r
+Unicode always.\r
+\r
+\H{config-selection} The Selection panel\r
+\r
+The Selection panel allows you to control the way \i{copy and paste}\r
+work in the PuTTY window.\r
+\r
+\S{config-rtfpaste} Pasting in \i{Rich Text Format}\r
+\r
+\cfg{winhelp-topic}{selection.rtf}\r
+\r
+If you enable \q{Paste to clipboard in RTF as well as plain text},\r
+PuTTY will write formatting information to the clipboard as well as\r
+the actual text you copy. The effect of this is\r
+that if you paste into (say) a word processor, the text will appear\r
+in the word processor in the same \i{font}, \i{colour}, and style \r
+(e.g. bold, underline) PuTTY was using to display it.\r
+\r
+This option can easily be inconvenient, so by default it is\r
+disabled.\r
+\r
+\S{config-mouse} Changing the actions of the mouse buttons\r
+\r
+\cfg{winhelp-topic}{selection.buttons}\r
+\r
+PuTTY's copy and paste mechanism is by default modelled on the Unix\r
+\c{xterm} application. The X Window System uses a three-button mouse,\r
+and the convention is that the \i{left button} \I{selecting text}selects,\r
+the \i{right button} extends an existing selection, and the\r
+\i{middle button} pastes.\r
+\r
+Windows often only has two mouse buttons, so in PuTTY's default\r
+configuration (\q{Compromise}), the \e{right} button pastes, and the\r
+\e{middle} button (if you have one) \I{adjusting a selection}extends\r
+a selection.\r
+\r
+If you have a \i{three-button mouse} and you are already used to the\r
+\c{xterm} arrangement, you can select it using the \q{Action of\r
+mouse buttons} control.\r
+\r
+Alternatively, with the \q{Windows} option selected, the middle\r
+button extends, and the right button brings up a \i{context menu} (on\r
+which one of the options is \q{Paste}). (This context menu is always\r
+available by holding down Ctrl and right-clicking, regardless of the\r
+setting of this option.)\r
+\r
+\S{config-mouseshift} \q{Shift overrides application's use of mouse}\r
+\r
+\cfg{winhelp-topic}{selection.shiftdrag}\r
+\r
+PuTTY allows the server to send \i{control codes} that let it\r
+\I{mouse reporting}take over the mouse and use it for purposes other\r
+than \i{copy and paste}.\r
+Applications which use this feature include the text-mode web\r
+browser \c{links}, the Usenet newsreader \c{trn} version 4, and the\r
+file manager \c{mc} (Midnight Commander).\r
+\r
+When running one of these applications, pressing the mouse buttons\r
+no longer performs copy and paste. If you do need to copy and paste,\r
+you can still do so if you hold down Shift while you do your mouse\r
+clicks.\r
+\r
+However, it is possible in theory for applications to even detect\r
+and make use of Shift + mouse clicks. We don't know of any\r
+applications that do this, but in case someone ever writes one,\r
+unchecking the \q{Shift overrides application's use of mouse}\r
+checkbox will cause Shift + mouse clicks to go to the server as well\r
+(so that mouse-driven copy and paste will be completely disabled).\r
+\r
+If you want to prevent the application from taking over the mouse at\r
+all, you can do this using the Features control panel; see\r
+\k{config-features-mouse}.\r
+\r
+\S{config-rectselect} Default selection mode\r
+\r
+\cfg{winhelp-topic}{selection.rect}\r
+\r
+As described in \k{using-selection}, PuTTY has two modes of\r
+selecting text to be copied to the clipboard. In the default mode\r
+(\q{Normal}), dragging the mouse from point A to point B selects to\r
+the end of the line containing A, all the lines in between, and from\r
+the very beginning of the line containing B. In the other mode\r
+(\q{Rectangular block}), dragging the mouse between two points\r
+defines a rectangle, and everything within that rectangle is copied.\r
+\r
+Normally, you have to hold down Alt while dragging the mouse to\r
+select a rectangular block. Using the \q{Default selection mode}\r
+control, you can set \i{rectangular selection} as the default, and then\r
+you have to hold down Alt to get the \e{normal} behaviour.\r
+\r
+\S{config-charclasses} Configuring \i{word-by-word selection}\r
+\r
+\cfg{winhelp-topic}{selection.charclasses}\r
+\r
+PuTTY will select a word at a time in the terminal window if you\r
+\i{double-click} to begin the drag. This panel allows you to control\r
+precisely what is considered to be a word.\r
+\r
+Each character is given a \e{class}, which is a small number\r
+(typically 0, 1 or 2). PuTTY considers a single word to be any\r
+number of adjacent characters in the same class. So by modifying the\r
+assignment of characters to classes, you can modify the word-by-word\r
+selection behaviour.\r
+\r
+In the default configuration, the \i{character classes} are:\r
+\r
+\b Class 0 contains \i{white space} and control characters.\r
+\r
+\b Class 1 contains most \i{punctuation}.\r
+\r
+\b Class 2 contains letters, numbers and a few pieces of punctuation\r
+(the double quote, minus sign, period, forward slash and\r
+underscore).\r
+\r
+So, for example, if you assign the \c{@} symbol into character class\r
+2, you will be able to select an e-mail address with just a double\r
+click.\r
+\r
+In order to adjust these assignments, you start by selecting a group\r
+of characters in the list box. Then enter a class number in the edit\r
+box below, and press the \q{Set} button.\r
+\r
+This mechanism currently only covers ASCII characters, because it\r
+isn't feasible to expand the list to cover the whole of Unicode.\r
+\r
+Character class definitions can be modified by \i{control sequence}s\r
+sent by the server. This configuration option controls the\r
+\e{default} state, which will be restored when you reset the\r
+terminal (see \k{reset-terminal}). However, if you modify this\r
+option in mid-session using \q{Change Settings}, it will take effect\r
+immediately.\r
+\r
+\H{config-colours} The Colours panel\r
+\r
+The Colours panel allows you to control PuTTY's use of \i{colour}.\r
+\r
+\S{config-ansicolour} \q{Allow terminal to specify \i{ANSI colours}}\r
+\r
+\cfg{winhelp-topic}{colours.ansi}\r
+\r
+This option is enabled by default. If it is disabled, PuTTY will\r
+ignore any \i{control sequence}s sent by the server to request coloured\r
+text.\r
+\r
+If you have a particularly garish application, you might want to\r
+turn this option off and make PuTTY only use the default foreground\r
+and background colours.\r
+\r
+\S{config-xtermcolour} \q{Allow terminal to use xterm \i{256-colour mode}}\r
+\r
+\cfg{winhelp-topic}{colours.xterm256}\r
+\r
+This option is enabled by default. If it is disabled, PuTTY will\r
+ignore any control sequences sent by the server which use the\r
+extended 256-colour mode supported by recent versions of \cw{xterm}.\r
+\r
+If you have an application which is supposed to use 256-colour mode\r
+and it isn't working, you may find you need to tell your server that\r
+your terminal supports 256 colours. On Unix, you do this by ensuring\r
+that the setting of \i\cw{TERM} describes a 256-colour-capable\r
+terminal. You can check this using a command such as \c{infocmp}:\r
+\r
+\c $ infocmp | grep colors\r
+\c         colors#256, cols#80, it#8, lines#24, pairs#256,\r
+\e         bbbbbbbbbb\r
+\r
+If you do not see \cq{colors#256} in the output, you may need to\r
+change your terminal setting. On modern Linux machines, you could\r
+try \cq{xterm-256color}.\r
+\r
+\S{config-boldcolour} \q{Bolded text is a different colour}\r
+\r
+\cfg{winhelp-topic}{colours.bold}\r
+\r
+When the server sends a \i{control sequence} indicating that some text\r
+should be displayed in \i{bold}, PuTTY can handle this two ways. It can\r
+either change the \i{font} for a bold version, or use the same font in a\r
+brighter colour. This control lets you choose which.\r
+\r
+By default the box is checked, so non-bold text is displayed in\r
+light grey and bold text is displayed in bright white (and similarly\r
+in other colours). If you uncheck the box, bold and non-bold text\r
+will be displayed in the same colour, and instead the font will\r
+change to indicate the difference.\r
+\r
+\S{config-logpalette} \q{Attempt to use \i{logical palettes}}\r
+\r
+\cfg{winhelp-topic}{colours.logpal}\r
+\r
+Logical palettes are a mechanism by which a Windows application\r
+running on an \i{8-bit colour} display can select precisely the colours\r
+it wants instead of going with the Windows standard defaults.\r
+\r
+If you are not getting the colours you ask for on an 8-bit display,\r
+you can try enabling this option. However, be warned that it's never\r
+worked very well.\r
+\r
+\S{config-syscolour} \q{Use \i{system colours}}\r
+\r
+\cfg{winhelp-topic}{colours.system}\r
+\r
+Enabling this option will cause PuTTY to ignore the configured colours\r
+for \I{default background}\I{default foreground}\q{Default\r
+Background/Foreground} and \I{cursor colour}\q{Cursor Colour/Text} (see\r
+\k{config-colourcfg}), instead going with the system-wide defaults.\r
+\r
+Note that non-bold and \i{bold text} will be the same colour if this\r
+option is enabled. You might want to change to indicating bold text\r
+by font changes (see \k{config-boldcolour}).\r
+\r
+\S{config-colourcfg} Adjusting the colours in the \i{terminal window}\r
+\r
+\cfg{winhelp-topic}{colours.config}\r
+\r
+The main colour control allows you to specify exactly what colours\r
+things should be displayed in. To modify one of the PuTTY colours,\r
+use the list box to select which colour you want to modify. The \i{RGB\r
+values} for that colour will appear on the right-hand side of the\r
+list box. Now, if you press the \q{Modify} button, you will be\r
+presented with a colour selector, in which you can choose a new\r
+colour to go in place of the old one. (You may also edit the RGB\r
+values directly in the edit boxes, if you wish; each value is an\r
+integer from 0 to 255.)\r
+\r
+PuTTY allows you to set the \i{cursor colour}, the \i{default foreground}\r
+and \I{default background}background, and the precise shades of all the\r
+\I{ANSI colours}ANSI configurable colours (black, red, green, yellow, blue,\r
+magenta, cyan, and white). You can also modify the precise shades used for\r
+the \i{bold} versions of these colours; these are used to display bold text\r
+if you have selected \q{Bolded text is a different colour}, and can also be\r
+used if the server asks specifically to use them. (Note that \q{Default\r
+Bold Background} is \e{not} the background colour used for bold text;\r
+it is only used if the server specifically asks for a bold\r
+background.)\r
+\r
+\H{config-connection} The Connection panel\r
+\r
+The Connection panel allows you to configure options that apply to\r
+more than one type of \i{connection}.\r
+\r
+\S{config-keepalive} Using \i{keepalives} to prevent disconnection\r
+\r
+\cfg{winhelp-topic}{connection.keepalive}\r
+\r
+If you find your sessions are closing unexpectedly (most often with\r
+\q{Connection reset by peer}) after they have been idle for a while,\r
+you might want to try using this option.\r
+\r
+Some network \i{routers} and \i{firewalls} need to keep track of all\r
+connections through them. Usually, these firewalls will assume a\r
+connection is dead if no data is transferred in either direction\r
+after a certain time interval. This can cause PuTTY sessions to be\r
+unexpectedly closed by the firewall if no traffic is seen in the\r
+session for some time.\r
+\r
+The keepalive option (\q{Seconds between keepalives}) allows you to\r
+configure PuTTY to send data through the session at regular\r
+intervals, in a way that does not disrupt the actual terminal\r
+session. If you find your firewall is cutting \i{idle connections} off,\r
+you can try entering a non-zero value in this field. The value is\r
+measured in seconds; so, for example, if your firewall cuts\r
+connections off after ten minutes then you might want to enter 300\r
+seconds (5 minutes) in the box.\r
+\r
+Note that keepalives are not always helpful. They help if you have a\r
+firewall which drops your connection after an idle period; but if\r
+the network between you and the server suffers from \i{breaks in\r
+connectivity} then keepalives can actually make things worse. If a\r
+session is idle, and connectivity is temporarily lost between the\r
+endpoints, but the connectivity is restored before either side tries\r
+to send anything, then there will be no problem - neither endpoint\r
+will notice that anything was wrong. However, if one side does send\r
+something during the break, it will repeatedly try to re-send, and\r
+eventually give up and abandon the connection. Then when\r
+connectivity is restored, the other side will find that the first\r
+side doesn't believe there is an open connection any more.\r
+Keepalives can make this sort of problem worse, because they\r
+increase the probability that PuTTY will attempt to send data during\r
+a break in connectivity. (Other types of periodic network activity\r
+can cause this behaviour; in particular, SSH-2 re-keys can have\r
+this effect. See \k{config-ssh-kex-rekey}.)\r
+\r
+Therefore, you might find that keepalives help\r
+connection loss, or you might find they make it worse, depending on\r
+what \e{kind} of network problems you have between you and the\r
+server.\r
+\r
+Keepalives are only supported in Telnet and SSH; the Rlogin and Raw\r
+protocols offer no way of implementing them. (For an alternative, see\r
+\k{config-tcp-keepalives}.)\r
+\r
+Note that if you are using \i{SSH-1} and the server has a bug that makes\r
+it unable to deal with SSH-1 ignore messages (see\r
+\k{config-ssh-bug-ignore1}), enabling keepalives will have no effect.\r
+\r
+\S{config-nodelay} \q{Disable \i{Nagle's algorithm}}\r
+\r
+\cfg{winhelp-topic}{connection.nodelay}\r
+\r
+Nagle's algorithm is a detail of TCP/IP implementations that tries\r
+to minimise the number of small data packets sent down a network\r
+connection. With Nagle's algorithm enabled, PuTTY's \i{bandwidth} usage\r
+will be slightly more efficient; with it disabled, you may find you\r
+get a faster response to your keystrokes when connecting to some\r
+types of server.\r
+\r
+The Nagle algorithm is disabled by default for \i{interactive connections}.\r
+\r
+\S{config-tcp-keepalives} \q{Enable \i{TCP keepalives}}\r
+\r
+\cfg{winhelp-topic}{connection.tcpkeepalive}\r
+\r
+\e{NOTE:} TCP keepalives should not be confused with the\r
+application-level keepalives described in \k{config-keepalive}. If in\r
+doubt, you probably want application-level keepalives; TCP keepalives\r
+are provided for completeness.\r
+\r
+The idea of TCP keepalives is similar to application-level keepalives,\r
+and the same caveats apply. The main differences are:\r
+\r
+\b TCP keepalives are available on \e{all} connection types, including\r
+Raw and Rlogin.\r
+\r
+\b The interval between TCP keepalives is usually much longer,\r
+typically two hours; this is set by the operating system, and cannot\r
+be configured within PuTTY.\r
+\r
+\b If the operating system does not receive a response to a keepalive,\r
+it may send out more in quick succession and terminate the connection\r
+if no response is received.\r
+\r
+TCP keepalives may be more useful for ensuring that \i{half-open connections}\r
+are terminated than for keeping a connection alive.\r
+\r
+TCP keepalives are disabled by default.\r
+\r
+\S{config-address-family} \I{Internet protocol version}\q{Internet protocol}\r
+\r
+\cfg{winhelp-topic}{connection.ipversion}\r
+\r
+This option allows the user to select between the old and new\r
+Internet protocols and addressing schemes (\i{IPv4} and \i{IPv6}).\r
+The selected protocol will be used for most outgoing network\r
+connections (including connections to \I{proxy}proxies); however,\r
+tunnels have their own configuration, for which see\r
+\k{config-ssh-portfwd-address-family}.\r
+\r
+The default setting is \q{Auto}, which means PuTTY will do something\r
+sensible and try to guess which protocol you wanted. (If you specify\r
+a literal \i{Internet address}, it will use whichever protocol that\r
+address implies. If you provide a \i{hostname}, it will see what kinds\r
+of address exist for that hostname; it will use IPv6 if there is an\r
+IPv6 address available, and fall back to IPv4 if not.)\r
+\r
+If you need to force PuTTY to use a particular protocol, you can\r
+explicitly set this to \q{IPv4} or \q{IPv6}.\r
+\r
+\S{config-loghost} \I{logical host name}\q{Logical name of remote host}\r
+\r
+\cfg{winhelp-topic}{connection.loghost}\r
+\r
+This allows you to tell PuTTY that the host it will really end up\r
+connecting to is different from where it thinks it is making a\r
+network connection.\r
+\r
+You might use this, for instance, if you had set up an SSH port\r
+forwarding in one PuTTY session so that connections to some\r
+arbitrary port (say, \cw{localhost} port 10022) were forwarded to a\r
+second machine's SSH port (say, \cw{foovax} port 22), and then\r
+started a second PuTTY connecting to the forwarded port.\r
+\r
+In normal usage, the second PuTTY will access the host key cache\r
+under the host name and port it actually connected to (i.e.\r
+\cw{localhost} port 10022 in this example). Using the logical host\r
+name option, however, you can configure the second PuTTY to cache\r
+the host key under the name of the host \e{you} know that it's\r
+\e{really} going to end up talking to (here \c{foovax}).\r
+\r
+This can be useful if you expect to connect to the same actual\r
+server through many different channels (perhaps because your port\r
+forwarding arrangements keep changing): by consistently setting the\r
+logical host name, you can arrange that PuTTY will not keep asking\r
+you to reconfirm its host key. Conversely, if you expect to use the\r
+same local port number for port forwardings to lots of different\r
+servers, you probably didn't want any particular server's host key\r
+cached under that local port number.\r
+\r
+If you just enter a host name for this option, PuTTY will cache the\r
+SSH host key under the default SSH port for that host, irrespective\r
+of the port you really connected to (since the typical scenario is\r
+like the above example: you connect to a silly real port number and\r
+your connection ends up forwarded to the normal port-22 SSH server\r
+of some other machine). To override this, you can append a port\r
+number to the logical host name, separated by a colon. E.g. entering\r
+\cq{foovax:2200} as the logical host name will cause the host key to\r
+be cached as if you had connected to port 2200 of \c{foovax}.\r
+\r
+If you provide a host name using this option, it is also displayed\r
+in other locations which contain the remote host name, such as the\r
+default window title and the default SSH password prompt. This\r
+reflects the fact that this is the host you're \e{really} connecting\r
+to, which is more important than the mere means you happen to be\r
+using to contact that host. (This applies even if you're using a\r
+protocol other than SSH.)\r
+\r
+\H{config-data} The Data panel\r
+\r
+The Data panel allows you to configure various pieces of data which\r
+can be sent to the server to affect your connection at the far end.\r
+\r
+Each option on this panel applies to more than one protocol.\r
+Options which apply to only one protocol appear on that protocol's\r
+configuration panels.\r
+\r
+\S{config-username} \q{\ii{Auto-login username}}\r
+\r
+\cfg{winhelp-topic}{connection.username}\r
+\r
+All three of the SSH, Telnet and Rlogin protocols allow you to\r
+specify what user name you want to log in as, without having to type\r
+it explicitly every time. (Some Telnet servers don't support this.)\r
+\r
+In this box you can type that user name.\r
+\r
+\S{config-username-from-env} Use of system username\r
+\r
+\cfg{winhelp-topic}{connection.usernamefromenv}\r
+\r
+When the previous box (\k{config-username}) is left blank, by default,\r
+PuTTY will prompt for a username at the time you make a connection.\r
+\r
+In some environments, such as the networks of large organisations\r
+implementing \i{single sign-on}, a more sensible default may be to use\r
+the name of the user logged in to the local operating system (if any);\r
+this is particularly likely to be useful with \i{GSSAPI} authentication\r
+(see \k{config-ssh-auth-gssapi}). This control allows you to change\r
+the default behaviour.\r
+\r
+The current system username is displayed in the dialog as a\r
+convenience. It is not saved in the configuration; if a saved session\r
+is later used by a different user, that user's name will be used.\r
+\r
+\S{config-termtype} \q{\ii{Terminal-type} string}\r
+\r
+\cfg{winhelp-topic}{connection.termtype}\r
+\r
+Most servers you might connect to with PuTTY are designed to be\r
+connected to from lots of different types of terminal. In order to\r
+send the right \i{control sequence}s to each one, the server will need\r
+to know what type of terminal it is dealing with. Therefore, each of\r
+the SSH, Telnet and Rlogin protocols allow a text string to be sent\r
+down the connection describing the terminal.  On a \i{Unix} server,\r
+this selects an entry from the \i\c{termcap} or \i\c{terminfo} database\r
+that tells applications what \i{control sequences} to send to the\r
+terminal, and what character sequences to expect the \i{keyboard}\r
+to generate.\r
+\r
+PuTTY attempts to emulate the Unix \i\c{xterm} program, and by default\r
+it reflects this by sending \c{xterm} as a terminal-type string. If\r
+you find this is not doing what you want - perhaps the remote\r
+system reports \q{Unknown terminal type} - you could try setting\r
+this to something different, such as \i\c{vt220}.\r
+\r
+If you're not sure whether a problem is due to the terminal type\r
+setting or not, you probably need to consult the manual for your\r
+application or your server.\r
+\r
+\S{config-termspeed} \q{\ii{Terminal speed}s}\r
+\r
+\cfg{winhelp-topic}{connection.termspeed}\r
+\r
+The Telnet, Rlogin, and SSH protocols allow the client to specify\r
+terminal speeds to the server.\r
+\r
+This parameter does \e{not} affect the actual speed of the connection,\r
+which is always \q{as fast as possible}; it is just a hint that is\r
+sometimes used by server software to modify its behaviour. For\r
+instance, if a slow speed is indicated, the server may switch to a\r
+less \i{bandwidth}-hungry display mode.\r
+\r
+The value is usually meaningless in a network environment, but\r
+PuTTY lets you configure it, in case you find the server is reacting\r
+badly to the default value.\r
+\r
+The format is a pair of numbers separated by a comma, for instance,\r
+\c{38400,38400}. The first number represents the output speed\r
+(\e{from} the server) in bits per second, and the second is the input\r
+speed (\e{to} the server). (Only the first is used in the Rlogin\r
+protocol.)\r
+\r
+This option has no effect on Raw connections.\r
+\r
+\S{config-environ} Setting \i{environment variables} on the server\r
+\r
+\cfg{winhelp-topic}{telnet.environ}\r
+\r
+The Telnet protocol provides a means for the client to pass\r
+environment variables to the server. Many Telnet servers have\r
+stopped supporting this feature due to security flaws, but PuTTY\r
+still supports it for the benefit of any servers which have found\r
+other ways around the security problems than just disabling the\r
+whole mechanism.\r
+\r
+Version 2 of the SSH protocol also provides a similar mechanism,\r
+which is easier to implement without security flaws. Newer \i{SSH-2}\r
+servers are more likely to support it than older ones.\r
+\r
+This configuration data is not used in the SSH-1, rlogin or raw\r
+protocols.\r
+\r
+To add an environment variable to the list transmitted down the\r
+connection, you enter the variable name in the \q{Variable} box,\r
+enter its value in the \q{Value} box, and press the \q{Add} button.\r
+To remove one from the list, select it in the list box and press\r
+\q{Remove}.\r
+\r
+\H{config-proxy} The Proxy panel\r
+\r
+\cfg{winhelp-topic}{proxy.main}\r
+\r
+The \ii{Proxy} panel allows you to configure PuTTY to use various types\r
+of proxy in order to make its network connections. The settings in\r
+this panel affect the primary network connection forming your PuTTY\r
+session, and also any extra connections made as a result of SSH \i{port\r
+forwarding} (see \k{using-port-forwarding}).\r
+\r
+Note that unlike some software (such as web browsers), PuTTY does not\r
+attempt to automatically determine whether to use a proxy and (if so)\r
+which one to use for a given destination. If you need to use a proxy,\r
+it must always be explicitly configured.\r
+\r
+\S{config-proxy-type} Setting the proxy type\r
+\r
+\cfg{winhelp-topic}{proxy.type}\r
+\r
+The \q{Proxy type} radio buttons allow you to configure what type of\r
+proxy you want PuTTY to use for its network connections. The default\r
+setting is \q{None}; in this mode no proxy is used for any\r
+connection.\r
+\r
+\b Selecting \I{HTTP proxy}\q{HTTP} allows you to proxy your connections\r
+through a web server supporting the HTTP \cw{CONNECT} command, as documented\r
+in \W{http://www.ietf.org/rfc/rfc2817.txt}{RFC 2817}.\r
+\r
+\b Selecting \q{SOCKS 4} or \q{SOCKS 5} allows you to proxy your\r
+connections through a \i{SOCKS server}.\r
+\r
+\b Many firewalls implement a less formal type of proxy in which a\r
+user can make a Telnet connection directly to the firewall machine\r
+and enter a command such as \c{connect myhost.com 22} to connect\r
+through to an external host. Selecting \I{Telnet proxy}\q{Telnet}\r
+allows you to tell PuTTY to use this type of proxy.\r
+\r
+\b Selecting \I{Local proxy}\q{Local} allows you to specify an arbitrary\r
+command on the local machine to act as a proxy. When the session is\r
+started, instead of creating a TCP connection, PuTTY runs the command\r
+(specified in \k{config-proxy-command}), and uses its standard input and\r
+output streams.\r
+\r
+\lcont{\r
+This could be used, for instance, to talk to some kind of network proxy\r
+that PuTTY does not natively support; or you could tunnel a connection\r
+over something other than TCP/IP entirely.\r
+\r
+If you want your local proxy command to make a secondary SSH\r
+connection to a proxy host and then tunnel the primary connection\r
+over that, you might well want the \c{-nc} command-line option in\r
+Plink. See \k{using-cmdline-ncmode} for more information.\r
+}\r
+\r
+\S{config-proxy-exclude} Excluding parts of the network from proxying\r
+\r
+\cfg{winhelp-topic}{proxy.exclude}\r
+\r
+Typically you will only need to use a proxy to connect to non-local\r
+parts of your network; for example, your proxy might be required for\r
+connections outside your company's internal network. In the\r
+\q{Exclude Hosts/IPs} box you can enter ranges of IP addresses, or\r
+ranges of DNS names, for which PuTTY will avoid using the proxy and\r
+make a direct connection instead.\r
+\r
+The \q{Exclude Hosts/IPs} box may contain more than one exclusion\r
+range, separated by commas. Each range can be an IP address or a DNS\r
+name, with a \c{*} character allowing wildcards. For example:\r
+\r
+\c *.example.com\r
+\r
+This excludes any host with a name ending in \c{.example.com} from\r
+proxying.\r
+\r
+\c 192.168.88.*\r
+\r
+This excludes any host with an IP address starting with 192.168.88\r
+from proxying.\r
+\r
+\c 192.168.88.*,*.example.com\r
+\r
+This excludes both of the above ranges at once.\r
+\r
+Connections to the local host (the host name \i\c{localhost}, and any\r
+\i{loopback IP address}) are never proxied, even if the proxy exclude\r
+list does not explicitly contain them. It is very unlikely that this\r
+behaviour would ever cause problems, but if it does you can change\r
+it by enabling \q{Consider proxying local host connections}.\r
+\r
+Note that if you are doing \I{proxy DNS}DNS at the proxy (see\r
+\k{config-proxy-dns}), you should make sure that your proxy\r
+exclusion settings do not depend on knowing the IP address of a\r
+host. If the name is passed on to the proxy without PuTTY looking it\r
+up, it will never know the IP address and cannot check it against\r
+your list.\r
+\r
+\S{config-proxy-dns} \I{proxy DNS}\ii{Name resolution} when using a proxy\r
+\r
+\cfg{winhelp-topic}{proxy.dns}\r
+\r
+If you are using a proxy to access a private network, it can make a\r
+difference whether \i{DNS} name resolution is performed by PuTTY itself\r
+(on the client machine) or performed by the proxy.\r
+\r
+The \q{Do DNS name lookup at proxy end} configuration option allows\r
+you to control this. If you set it to \q{No}, PuTTY will always do\r
+its own DNS, and will always pass an IP address to the proxy. If you\r
+set it to \q{Yes}, PuTTY will always pass host names straight to the\r
+proxy without trying to look them up first.\r
+\r
+If you set this option to \q{Auto} (the default), PuTTY will do\r
+something it considers appropriate for each type of proxy. Telnet,\r
+HTTP, and SOCKS5 proxies will have host names passed straight to\r
+them; SOCKS4 proxies will not.\r
+\r
+Note that if you are doing DNS at the proxy, you should make sure\r
+that your proxy exclusion settings (see \k{config-proxy-exclude}) do\r
+not depend on knowing the IP address of a host. If the name is\r
+passed on to the proxy without PuTTY looking it up, it will never\r
+know the IP address and cannot check it against your list.\r
+\r
+The original SOCKS 4 protocol does not support proxy-side DNS. There\r
+is a protocol extension (SOCKS 4A) which does support it, but not\r
+all SOCKS 4 servers provide this extension. If you enable proxy DNS\r
+and your SOCKS 4 server cannot deal with it, this might be why.\r
+\r
+\S{config-proxy-auth} \I{proxy username}Username and \I{proxy password}password\r
+\r
+\cfg{winhelp-topic}{proxy.auth}\r
+\r
+If your proxy requires \I{proxy authentication}authentication, you can\r
+enter a username and a password in the \q{Username} and \q{Password} boxes.\r
+\r
+\I{security hazard}Note that if you save your session, the proxy\r
+password will be saved in plain text, so anyone who can access your PuTTY\r
+configuration data will be able to discover it.\r
+\r
+Authentication is not fully supported for all forms of proxy:\r
+\r
+\b Username and password authentication is supported for HTTP\r
+proxies and SOCKS 5 proxies.\r
+\r
+\lcont{\r
+\r
+\b With SOCKS 5, authentication is via \i{CHAP} if the proxy\r
+supports it (this is not supported in \i{PuTTYtel}); otherwise the\r
+password is sent to the proxy in \I{plaintext password}plain text.\r
+\r
+\b With HTTP proxying, the only currently supported authentication\r
+method is \I{HTTP basic}\q{basic}, where the password is sent to the proxy\r
+in \I{plaintext password}plain text.\r
+\r
+}\r
+\r
+\b SOCKS 4 can use the \q{Username} field, but does not support\r
+passwords.\r
+\r
+\b You can specify a way to include a username and password in the\r
+Telnet/Local proxy command (see \k{config-proxy-command}).\r
+\r
+\S{config-proxy-command} Specifying the Telnet or Local proxy command\r
+\r
+\cfg{winhelp-topic}{proxy.command}\r
+\r
+If you are using the \i{Telnet proxy} type, the usual command required\r
+by the firewall's Telnet server is \c{connect}, followed by a host\r
+name and a port number. If your proxy needs a different command,\r
+you can enter an alternative here.\r
+\r
+If you are using the \i{Local proxy} type, the local command to run\r
+is specified here.\r
+\r
+In this string, you can use \c{\\n} to represent a new-line, \c{\\r}\r
+to represent a carriage return, \c{\\t} to represent a tab\r
+character, and \c{\\x} followed by two hex digits to represent any\r
+other character. \c{\\\\} is used to encode the \c{\\} character\r
+itself.\r
+\r
+Also, the special strings \c{%host} and \c{%port} will be replaced\r
+by the host name and port number you want to connect to. The strings\r
+\c{%user} and \c{%pass} will be replaced by the proxy username and \r
+password you specify. The strings \c{%proxyhost} and \c{%proxyport}\r
+will be replaced by the host details specified on the \e{Proxy} panel,\r
+if any (this is most likely to be useful for the Local proxy type).\r
+To get a literal \c{%} sign, enter \c{%%}.\r
+\r
+If a Telnet proxy server prompts for a username and password\r
+before commands can be sent, you can use a command such as:\r
+\r
+\c %user\n%pass\nconnect %host %port\n\r
+\r
+This will send your username and password as the first two lines to \r
+the proxy, followed by a command to connect to the desired host and \r
+port. Note that if you do not include the \c{%user} or \c{%pass}\r
+tokens in the Telnet command, then the \q{Username} and \q{Password}\r
+configuration fields will be ignored.\r
+\r
+\H{config-telnet} The \i{Telnet} panel\r
+\r
+The Telnet panel allows you to configure options that only apply to\r
+Telnet sessions.\r
+\r
+\S{config-oldenviron} \q{Handling of OLD_ENVIRON ambiguity}\r
+\r
+\cfg{winhelp-topic}{telnet.oldenviron}\r
+\r
+The original Telnet mechanism for passing \i{environment variables} was\r
+badly specified. At the time the standard (RFC 1408) was written,\r
+BSD telnet implementations were already supporting the feature, and\r
+the intention of the standard was to describe the behaviour the BSD\r
+implementations were already using.\r
+\r
+Sadly there was a typing error in the standard when it was issued,\r
+and two vital function codes were specified the wrong way round. BSD\r
+implementations did not change, and the standard was not corrected.\r
+Therefore, it's possible you might find either \i{BSD} or \i{RFC}-compliant\r
+implementations out there. This switch allows you to choose which\r
+one PuTTY claims to be.\r
+\r
+The problem was solved by issuing a second standard, defining a new\r
+Telnet mechanism called \i\cw{NEW_ENVIRON}, which behaved exactly like\r
+the original \i\cw{OLD_ENVIRON} but was not encumbered by existing\r
+implementations. Most Telnet servers now support this, and it's\r
+unambiguous. This feature should only be needed if you have trouble\r
+passing environment variables to quite an old server.\r
+\r
+\S{config-ptelnet} Passive and active \i{Telnet negotiation} modes\r
+\r
+\cfg{winhelp-topic}{telnet.passive}\r
+\r
+In a Telnet connection, there are two types of data passed between\r
+the client and the server: actual text, and \e{negotiations} about\r
+which Telnet extra features to use.\r
+\r
+PuTTY can use two different strategies for negotiation:\r
+\r
+\b In \I{active Telnet negotiation}\e{active} mode, PuTTY starts to send\r
+negotiations as soon as the connection is opened.\r
+\r
+\b In \I{passive Telnet negotiation}\e{passive} mode, PuTTY will wait to\r
+negotiate until it sees a negotiation from the server.\r
+\r
+The obvious disadvantage of passive mode is that if the server is\r
+also operating in a passive mode, then negotiation will never begin\r
+at all. For this reason PuTTY defaults to active mode.\r
+\r
+However, sometimes passive mode is required in order to successfully\r
+get through certain types of firewall and \i{Telnet proxy} server. If\r
+you have confusing trouble with a \i{firewall}, you could try enabling\r
+passive mode to see if it helps.\r
+\r
+\S{config-telnetkey} \q{Keyboard sends \i{Telnet special commands}}\r
+\r
+\cfg{winhelp-topic}{telnet.specialkeys}\r
+\r
+If this box is checked, several key sequences will have their normal\r
+actions modified:\r
+\r
+\b the Backspace key on the keyboard will send the \I{Erase Character,\r
+Telnet special command}Telnet special backspace code;\r
+\r
+\b Control-C will send the Telnet special \I{Interrupt Process, Telnet\r
+special command}Interrupt Process code;\r
+\r
+\b Control-Z will send the Telnet special \I{Suspend Process, Telnet\r
+special command}Suspend Process code.\r
+\r
+You probably shouldn't enable this\r
+unless you know what you're doing.\r
+\r
+\S{config-telnetnl} \q{Return key sends \i{Telnet New Line} instead of ^M}\r
+\r
+\cfg{winhelp-topic}{telnet.newline}\r
+\r
+Unlike most other remote login protocols, the Telnet protocol has a\r
+special \q{\i{new line}} code that is not the same as the usual line\r
+endings of Control-M or Control-J. By default, PuTTY sends the\r
+Telnet New Line code when you press Return, instead of sending\r
+Control-M as it does in most other protocols.\r
+\r
+Most Unix-style Telnet servers don't mind whether they receive\r
+Telnet New Line or Control-M; some servers do expect New Line, and\r
+some servers prefer to see ^M. If you are seeing surprising\r
+behaviour when you press Return in a Telnet session, you might try\r
+turning this option off to see if it helps.\r
+\r
+\H{config-rlogin} The Rlogin panel\r
+\r
+The \i{Rlogin} panel allows you to configure options that only apply to\r
+Rlogin sessions.\r
+\r
+\S{config-rlogin-localuser} \I{local username in Rlogin}\q{Local username}\r
+\r
+\cfg{winhelp-topic}{rlogin.localuser}\r
+\r
+Rlogin allows an automated (password-free) form of login by means of\r
+a file called \i\c{.rhosts} on the server. You put a line in your\r
+\c{.rhosts} file saying something like \c{jbloggs@pc1.example.com},\r
+and then when you make an Rlogin connection the client transmits the\r
+username of the user running the Rlogin client. The server checks\r
+the username and hostname against \c{.rhosts}, and if they match it\r
+\I{passwordless login}does not ask for a password.\r
+\r
+This only works because Unix systems contain a safeguard to stop a\r
+user from pretending to be another user in an Rlogin connection.\r
+Rlogin connections have to come from \I{privileged port}port numbers below\r
+1024, and Unix systems prohibit this to unprivileged processes; so when the\r
+server sees a connection from a low-numbered port, it assumes the\r
+client end of the connection is held by a privileged (and therefore\r
+trusted) process, so it believes the claim of who the user is.\r
+\r
+Windows does not have this restriction: \e{any} user can initiate an\r
+outgoing connection from a low-numbered port. Hence, the Rlogin\r
+\c{.rhosts} mechanism is completely useless for securely\r
+distinguishing several different users on a Windows machine. If you\r
+have a \c{.rhosts} entry pointing at a Windows PC, you should assume\r
+that \e{anyone} using that PC can \i{spoof} your username in\r
+an Rlogin connection and access your account on the server.\r
+\r
+The \q{Local username} control allows you to specify what user name\r
+PuTTY should claim you have, in case it doesn't match your \i{Windows\r
+user name} (or in case you didn't bother to set up a Windows user\r
+name).\r
+\r
+\H{config-ssh} The SSH panel\r
+\r
+The \i{SSH} panel allows you to configure options that only apply to\r
+SSH sessions.\r
+\r
+\S{config-command} Executing a specific command on the server\r
+\r
+\cfg{winhelp-topic}{ssh.command}\r
+\r
+In SSH, you don't have to run a general shell session on the server.\r
+Instead, you can choose to run a single specific command (such as a\r
+mail user agent, for example). If you want to do this, enter the\r
+command in the \q{\ii{Remote command}} box.\r
+\r
+Note that most servers will close the session after executing the\r
+command.\r
+\r
+\S{config-ssh-noshell} \q{Don't start a \I{remote shell}shell or\r
+\I{remote command}command at all}\r
+\r
+\cfg{winhelp-topic}{ssh.noshell}\r
+\r
+If you tick this box, PuTTY will not attempt to run a shell or\r
+command after connecting to the remote server. You might want to use\r
+this option if you are only using the SSH connection for \i{port\r
+forwarding}, and your user account on the server does not have the\r
+ability to run a shell.\r
+\r
+This feature is only available in \i{SSH protocol version 2} (since the\r
+version 1 protocol assumes you will always want to run a shell).\r
+\r
+This feature can also be enabled using the \c{-N} command-line\r
+option; see \k{using-cmdline-noshell}.\r
+\r
+If you use this feature in Plink, you will not be able to terminate\r
+the Plink process by any graceful means; the only way to kill it\r
+will be by pressing Control-C or sending a kill signal from another\r
+program.\r
+\r
+\S{config-ssh-comp} \q{Enable \i{compression}}\r
+\r
+\cfg{winhelp-topic}{ssh.compress}\r
+\r
+This enables data compression in the SSH connection: data sent by\r
+the server is compressed before sending, and decompressed at the\r
+client end. Likewise, data sent by PuTTY to the server is compressed\r
+first and the server decompresses it at the other end. This can help\r
+make the most of a low-\i{bandwidth} connection.\r
+\r
+\S{config-ssh-prot} \q{Preferred \i{SSH protocol version}}\r
+\r
+\cfg{winhelp-topic}{ssh.protocol}\r
+\r
+This allows you to select whether you would like to use \i{SSH protocol\r
+version 1} or \I{SSH-2}version 2. \#{FIXME: say something about this elsewhere?}\r
+\r
+PuTTY will attempt to use protocol 1 if the server you connect to\r
+does not offer protocol 2, and vice versa.\r
+\r
+If you select \q{1 only} or \q{2 only} here, PuTTY will only connect\r
+if the server you connect to offers the SSH protocol version you\r
+have specified.\r
+\r
+\S{config-ssh-encryption} \ii{Encryption} algorithm selection\r
+\r
+\cfg{winhelp-topic}{ssh.ciphers}\r
+\r
+PuTTY supports a variety of different \i{encryption algorithm}s, and\r
+allows you to choose which one you prefer to use. You can do this by\r
+dragging the algorithms up and down in the list box (or moving them\r
+using the Up and Down buttons) to specify a preference order. When\r
+you make an SSH connection, PuTTY will search down the list from the\r
+top until it finds an algorithm supported by the server, and then\r
+use that.\r
+\r
+PuTTY currently supports the following algorithms:\r
+\r
+\b \i{AES} (Rijndael) - 256, 192, or 128-bit SDCTR or CBC (SSH-2 only)\r
+\r
+\b \i{Arcfour} (RC4) - 256 or 128-bit stream cipher (SSH-2 only)\r
+\r
+\b \i{Blowfish} - 256-bit SDCTR (SSH-2 only) or 128-bit CBC\r
+\r
+\b \ii{Triple-DES} - 168-bit SDCTR (SSH-2 only) or CBC\r
+\r
+\b \ii{Single-DES} - 56-bit CBC (see below for SSH-2)\r
+\r
+If the algorithm PuTTY finds is below the \q{warn below here} line,\r
+you will see a warning box when you make the connection:\r
+\r
+\c The first cipher supported by the server\r
+\c is single-DES, which is below the configured\r
+\c warning threshold.\r
+\c Do you want to continue with this connection?\r
+\r
+This warns you that the first available encryption is not a very\r
+secure one. Typically you would put the \q{warn below here} line\r
+between the encryptions you consider secure and the ones you\r
+consider substandard. By default, PuTTY supplies a preference order\r
+intended to reflect a reasonable preference in terms of security and\r
+speed.\r
+\r
+In SSH-2, the encryption algorithm is negotiated independently for\r
+each direction of the connection, although PuTTY does not support\r
+separate configuration of the preference orders. As a result you may\r
+get two warnings similar to the one above, possibly with different\r
+encryptions.\r
+\r
+Single-DES is not recommended in the SSH-2 protocol\r
+standards, but one or two server implementations do support it.\r
+PuTTY can use single-DES to interoperate with\r
+these servers if you enable the \q{Enable legacy use of single-DES in\r
+SSH-2} option; by default this is disabled and PuTTY will stick to\r
+recommended ciphers.\r
+\r
+\H{config-ssh-kex} The Kex panel\r
+\r
+\# FIXME: This whole section is draft. Feel free to revise.\r
+\r
+The Kex panel (short for \q{\i{key exchange}}) allows you to configure\r
+options related to SSH-2 key exchange.\r
+\r
+Key exchange occurs at the start of an SSH connection (and\r
+occasionally thereafter); it establishes a \i{shared secret} that is used\r
+as the basis for all of SSH's security features. It is therefore very\r
+important for the security of the connection that the key exchange is\r
+secure.\r
+\r
+Key exchange is a cryptographically intensive process; if either the\r
+client or the server is a relatively slow machine, the slower methods\r
+may take several tens of seconds to complete.\r
+\r
+If connection startup is too slow, or the connection hangs\r
+periodically, you may want to try changing these settings.\r
+\r
+If you don't understand what any of this means, it's safe to leave\r
+these settings alone.\r
+\r
+This entire panel is only relevant to SSH protocol version 2; none of\r
+these settings affect SSH-1 at all.\r
+\r
+\S{config-ssh-kex-order} \ii{Key exchange algorithm} selection\r
+\r
+\cfg{winhelp-topic}{ssh.kex.order}\r
+\r
+PuTTY supports a variety of SSH-2 key exchange methods, and allows you\r
+to choose which one you prefer to use; configuration is similar to\r
+cipher selection (see \k{config-ssh-encryption}).\r
+\r
+PuTTY currently supports the following varieties of \i{Diffie-Hellman key\r
+exchange}:\r
+\r
+\b \q{Group 14}: a well-known 2048-bit group.\r
+\r
+\b \q{Group 1}: a well-known 1024-bit group. This is less secure\r
+\#{FIXME better words} than group 14, but may be faster with slow\r
+client or server machines, and may be the only method supported by\r
+older server software.\r
+\r
+\b \q{\ii{Group exchange}}: with this method, instead of using a fixed\r
+group, PuTTY requests that the server suggest a group to use for key\r
+exchange; the server can avoid groups known to be weak, and possibly\r
+invent new ones over time, without any changes required to PuTTY's\r
+configuration. We recommend use of this method, if possible.\r
+\r
+In addition, PuTTY supports \i{RSA key exchange}, which requires much less\r
+computational effort on the part of the client, and somewhat less on\r
+the part of the server, than Diffie-Hellman key exchange.\r
+\r
+If the first algorithm PuTTY finds is below the \q{warn below here}\r
+line, you will see a warning box when you make the connection, similar\r
+to that for cipher selection (see \k{config-ssh-encryption}).\r
+\r
+\S{config-ssh-kex-rekey} \ii{Repeat key exchange}\r
+\r
+\cfg{winhelp-topic}{ssh.kex.repeat}\r
+\r
+If the session key negotiated at connection startup is used too much\r
+or for too long, it may become feasible to mount attacks against the\r
+SSH connection. Therefore, the SSH-2 protocol specifies that a new key\r
+exchange should take place every so often; this can be initiated by\r
+either the client or the server.\r
+\r
+While this renegotiation is taking place, no data can pass through\r
+the SSH connection, so it may appear to \q{freeze}. (The occurrence of\r
+repeat key exchange is noted in the Event Log; see\r
+\k{using-eventlog}.) Usually the same algorithm is used as at the\r
+start of the connection, with a similar overhead.\r
+\r
+These options control how often PuTTY will initiate a repeat key\r
+exchange (\q{rekey}). You can also force a key exchange at any time\r
+from the Special Commands menu (see \k{using-specials}).\r
+\r
+\# FIXME: do we have any additions to the SSH-2 specs' advice on\r
+these values? Do we want to enforce any limits?\r
+\r
+\b \q{Max minutes before rekey} specifies the amount of time that is\r
+allowed to elapse before a rekey is initiated. If this is set to zero,\r
+PuTTY will not rekey due to elapsed time. The SSH-2 protocol\r
+specification recommends a timeout of at most 60 minutes.\r
+\r
+You might have a need to disable time-based rekeys completely for the same\r
+reasons that \i{keepalives} aren't always helpful. If you anticipate\r
+suffering a network dropout of several hours in the middle of an SSH\r
+connection, but were not actually planning to send \e{data} down\r
+that connection during those hours, then an attempted rekey in the\r
+middle of the dropout will probably cause the connection to be\r
+abandoned, whereas if rekeys are disabled then the connection should\r
+in principle survive (in the absence of interfering \i{firewalls}). See\r
+\k{config-keepalive} for more discussion of these issues; for these\r
+purposes, rekeys have much the same properties as keepalives.\r
+(Except that rekeys have cryptographic value in themselves, so you\r
+should bear that in mind when deciding whether to turn them off.)\r
+Note, however, the the SSH \e{server} can still initiate rekeys.\r
+\r
+\b \q{Max data before rekey} specifies the amount of data (in bytes)\r
+that is permitted to flow in either direction before a rekey is\r
+initiated. If this is set to zero, PuTTY will not rekey due to\r
+transferred data. The SSH-2 protocol specification recommends a limit\r
+of at most 1 gigabyte.\r
+\r
+\lcont{\r
+\r
+As well as specifying a value in bytes, the following shorthand can be\r
+used:\r
+\r
+\b \cq{1k} specifies 1 kilobyte (1024 bytes).\r
+\r
+\b \cq{1M} specifies 1 megabyte (1024 kilobytes).\r
+\r
+\b \cq{1G} specifies 1 gigabyte (1024 megabytes).\r
+\r
+}\r
+\r
+Disabling data-based rekeys entirely is a bad idea.  The \i{integrity},\r
+and to a lesser extent, \i{confidentiality} of the SSH-2 protocol depend\r
+in part on rekeys occuring before a 32-bit packet sequence number\r
+wraps around.  Unlike time-based rekeys, data-based rekeys won't occur\r
+when the SSH connection is idle, so they shouldn't cause the same\r
+problems.  The SSH-1 protocol, incidentally, has even weaker integrity\r
+protection than SSH-2 without rekeys.\r
+\r
+\H{config-ssh-auth} The Auth panel\r
+\r
+The Auth panel allows you to configure \i{authentication} options for\r
+SSH sessions.\r
+\r
+\S{config-ssh-noauth} \q{Bypass authentication entirely}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.bypass}\r
+\r
+In SSH-2, it is possible to establish a connection without using SSH's\r
+mechanisms to identify or authenticate oneself to the server. Some\r
+servers may prefer to handle authentication in the data channel, for\r
+instance, or may simply require no authentication whatsoever.\r
+\r
+By default, PuTTY assumes the server requires authentication (most\r
+do), and thus must provide a username. If you find you are getting\r
+unwanted username prompts, you could try checking this option.\r
+\r
+This option only affects SSH-2 connections. SSH-1 connections always\r
+require an authentication step.\r
+\r
+\S{config-ssh-banner} \q{Display pre-authentication banner}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.banner}\r
+\r
+SSH-2 servers can provide a message for clients to display to the\r
+prospective user before the user logs in; this is sometimes known as a\r
+pre-authentication \q{\i{banner}}. Typically this is used to provide\r
+information about the server and legal notices.\r
+\r
+By default, PuTTY displays this message before prompting for a\r
+password or similar credentials (although, unfortunately, not before\r
+prompting for a login name, due to the nature of the protocol design).\r
+By unchecking this option, display of the banner can be suppressed\r
+entirely.\r
+\r
+\S{config-ssh-tryagent} \q{Attempt authentication using Pageant}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.pageant}\r
+\r
+If this option is enabled, then PuTTY will look for Pageant (the SSH\r
+private-key storage agent) and attempt to authenticate with any\r
+suitable public keys Pageant currently holds.\r
+\r
+This behaviour is almost always desirable, and is therefore enabled\r
+by default. In rare cases you might need to turn it off in order to\r
+force authentication by some non-public-key method such as\r
+passwords.\r
+\r
+This option can also be controlled using the \c{-noagent}\r
+command-line option. See \k{using-cmdline-agentauth}.\r
+\r
+See \k{pageant} for more information about Pageant in general.\r
+\r
+\S{config-ssh-tis} \q{Attempt \I{TIS authentication}TIS or\r
+\i{CryptoCard authentication}}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.tis}\r
+\r
+TIS and CryptoCard authentication are (despite their names) generic\r
+forms of simple \I{challenge/response authentication}challenge/response\r
+authentication available in SSH protocol version 1 only. You might use\r
+them if you were using \i{S/Key} \i{one-time passwords}, for example,\r
+or if you had a physical \i{security token} that generated responses\r
+to authentication challenges.  They can even be used to prompt for\r
+simple passwords.\r
+\r
+With this switch enabled, PuTTY will attempt these forms of\r
+authentication if the server is willing to try them. You will be\r
+presented with a challenge string (which may be different every\r
+time) and must supply the correct response in order to log in. If\r
+your server supports this, you should talk to your system\r
+administrator about precisely what form these challenges and\r
+responses take.\r
+\r
+\S{config-ssh-ki} \q{Attempt \i{keyboard-interactive authentication}}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.ki}\r
+\r
+The SSH-2 equivalent of TIS authentication is called\r
+\q{keyboard-interactive}. It is a flexible authentication method\r
+using an arbitrary sequence of requests and responses; so it is not\r
+only useful for \I{challenge/response authentication}challenge/response\r
+mechanisms such as \i{S/Key}, but it can also be used for (for example)\r
+asking the user for a \I{password expiry}new password when the old one\r
+has expired.\r
+\r
+PuTTY leaves this option enabled by default, but supplies a switch\r
+to turn it off in case you should have trouble with it.\r
+\r
+\S{config-ssh-agentfwd} \q{Allow \i{agent forwarding}}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.agentfwd}\r
+\r
+This option allows the SSH server to open forwarded connections back\r
+to your local copy of \i{Pageant}. If you are not running Pageant, this\r
+option will do nothing.\r
+\r
+See \k{pageant} for general information on Pageant, and\r
+\k{pageant-forward} for information on agent forwarding. Note that\r
+there is a security risk involved with enabling this option; see\r
+\k{pageant-security} for details.\r
+\r
+\S{config-ssh-changeuser} \q{Allow attempted \i{changes of username} in SSH-2}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.changeuser}\r
+\r
+In the SSH-1 protocol, it is impossible to change username after\r
+failing to authenticate. So if you mis-type your username at the\r
+PuTTY \q{login as:} prompt, you will not be able to change it except\r
+by restarting PuTTY.\r
+\r
+The SSH-2 protocol \e{does} allow changes of username, in principle,\r
+but does not make it mandatory for SSH-2 servers to accept them. In\r
+particular, \i{OpenSSH} does not accept a change of username; once you\r
+have sent one username, it will reject attempts to try to\r
+authenticate as another user. (Depending on the version of OpenSSH,\r
+it may quietly return failure for all login attempts, or it may send\r
+an error message.)\r
+\r
+For this reason, PuTTY will by default not prompt you for your\r
+username more than once, in case the server complains. If you know\r
+your server can cope with it, you can enable the \q{Allow attempted\r
+changes of username} option to modify PuTTY's behaviour.\r
+\r
+\S{config-ssh-privkey} \q{\ii{Private key} file for authentication}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.privkey}\r
+\r
+This box is where you enter the name of your private key file if you\r
+are using \i{public key authentication}. See \k{pubkey} for information\r
+about public key authentication in SSH.\r
+\r
+This key must be in PuTTY's native format (\c{*.\i{PPK}}). If you have a\r
+private key in another format that you want to use with PuTTY, see\r
+\k{puttygen-conversions}.\r
+\r
+If a key file is specified here, and \i{Pageant} is running (see\r
+\k{pageant}), PuTTY will first try asking Pageant to authenticate with\r
+that key, and ignore any other keys Pageant may have. If that fails,\r
+PuTTY will ask for a passphrase as normal.\r
+\r
+\H{config-ssh-auth-gssapi} The \i{GSSAPI} panel\r
+\r
+\cfg{winhelp-topic}{ssh.auth.gssapi}\r
+\r
+The \q{GSSAPI} subpanel of the \q{Auth} panel controls the use of\r
+GSSAPI authentication. This is a mechanism which delegates the\r
+authentication exchange to a library elsewhere on the client\r
+machine, which in principle can authenticate in many different ways\r
+but in practice is usually used with the \i{Kerberos} \i{single sign-on}\r
+protocol.\r
+\r
+GSSAPI is only available in the SSH-2 protocol.\r
+\r
+The topmost control on the GSSAPI subpanel is the checkbox labelled\r
+\q{Attempt GSSAPI authentication}. If this is disabled, GSSAPI will\r
+not be attempted at all and the rest of this panel is unused. If it\r
+is enabled, GSSAPI authentication will be attempted, and (typically)\r
+if your client machine has valid Kerberos credentials loaded, then\r
+PuTTY should be able to authenticate automatically to servers that\r
+support Kerberos logins.\r
+\r
+\S{config-ssh-auth-gssapi-delegation} \q{Allow GSSAPI credential\r
+delegation}\r
+\r
+\cfg{winhelp-topic}{ssh.auth.gssapi.delegation}\r
+\r
+\i{GSSAPI credential delegation} is a mechanism for passing on your\r
+Kerberos (or other) identity to the session on the SSH server. If\r
+you enable this option, then not only will PuTTY be able to log in\r
+automatically to a server that accepts your Kerberos credentials,\r
+but also you will be able to connect out from that server to other\r
+Kerberos-supporting services and use the same credentials just as\r
+automatically.\r
+\r
+(This option is the Kerberos analogue of SSH agent forwarding; see\r
+\k{pageant-forward} for some information on that.)\r
+\r
+Note that, like SSH agent forwarding, there is a security\r
+implication in the use of this option: the administrator of the\r
+server you connect to, or anyone else who has cracked the\r
+administrator account on that server, could fake your identity when\r
+connecting to further Kerberos-supporting services. However,\r
+Kerberos sites are typically run by a central authority, so the\r
+administrator of one server is likely to already have access to the\r
+other services too; so this would typically be less of a risk than\r
+SSH agent forwarding.\r
+\r
+\S{config-ssh-auth-gssapi-libraries} Preference order for GSSAPI\r
+libraries\r
+\r
+\cfg{winhelp-topic}{ssh.auth.gssapi.libraries}\r
+\r
+GSSAPI is a mechanism which allows more than one authentication\r
+method to be accessed through the same interface. Therefore, more\r
+than one authentication library may exist on your system which can\r
+be accessed using GSSAPI.\r
+\r
+PuTTY contains native support for a few well-known such libraries,\r
+and will look for all of them on your system and use whichever it\r
+finds. If more than one exists on your system and you need to use a\r
+specific one, you can adjust the order in which it will search using\r
+this preference list control.\r
+\r
+One of the options in the preference list is to use a user-specified\r
+GSSAPI library. If the library you want to use is not mentioned by\r
+name in PuTTY's list of options, you can enter its full pathname in\r
+the \q{User-supplied GSSAPI library path} field, and move the\r
+\q{User-supplied GSSAPI library} option in the preference list to\r
+make sure it is selected before anything else.\r
+\r
+\H{config-ssh-tty} The TTY panel\r
+\r
+The TTY panel lets you configure the remote pseudo-terminal.\r
+\r
+\S{config-ssh-pty} \I{pseudo-terminal allocation}\q{Don't allocate\r
+a pseudo-terminal}\r
+\r
+\cfg{winhelp-topic}{ssh.nopty}\r
+\r
+When connecting to a \i{Unix} system, most \I{interactive\r
+connections}interactive shell sessions are run in a \e{pseudo-terminal},\r
+which allows the Unix system to pretend it's talking to a real physical\r
+terminal device but allows the SSH server to catch all the data coming\r
+from that fake device and send it back to the client.\r
+\r
+Occasionally you might find you have a need to run a session \e{not}\r
+in a pseudo-terminal. In PuTTY, this is generally only useful for\r
+very specialist purposes; although in Plink (see \k{plink}) it is\r
+the usual way of working.\r
+\r
+\S{config-ttymodes} Sending \i{terminal modes}\r
+\r
+\cfg{winhelp-topic}{ssh.ttymodes}\r
+\r
+The SSH protocol allows the client to send \q{terminal modes} for\r
+the remote pseudo-terminal. These usually control the server's\r
+expectation of the local terminal's behaviour.\r
+\r
+If your server does not have sensible defaults for these modes, you\r
+may find that changing them here helps. If you don't understand any of\r
+this, it's safe to leave these settings alone.\r
+\r
+(None of these settings will have any effect if no pseudo-terminal\r
+is requested or allocated.)\r
+\r
+You can add or modify a mode by selecting it from the drop-down list,\r
+choosing whether it's set automatically or to a specific value with\r
+the radio buttons and edit box, and hitting \q{Add}. A mode (or\r
+several) can be removed from the list by selecting them and hitting\r
+\q{Remove}. The effect of the mode list is as follows:\r
+\r
+\b If a mode is not on the list, it will not be specified to the\r
+server under any circumstances.\r
+\r
+\b If a mode is on the list:\r
+\r
+\lcont{\r
+\r
+\b If the \q{Auto} option is selected, the PuTTY tools will decide\r
+whether to specify that mode to the server, and if so, will send\r
+a sensible value.\r
+\r
+\lcont{\r
+\r
+PuTTY proper will send modes that it has an opinion on (currently only\r
+the code for the Backspace key, \cw{ERASE}). Plink on Unix\r
+will propagate appropriate modes from the local terminal, if any.\r
+\r
+}\r
+\r
+\b If a value is specified, it will be sent to the server under all\r
+circumstances. The precise syntax of the value box depends on the\r
+mode.\r
+\r
+}\r
+\r
+By default, all of the available modes are listed as \q{Auto},\r
+which should do the right thing in most circumstances.\r
+\r
+The precise effect of each setting, if any, is up to the server. Their\r
+names come from \i{POSIX} and other Unix systems, and they are most\r
+likely to have a useful effect on such systems. (These are the same\r
+settings that can usually be changed using the \i\c{stty} command once\r
+logged in to such servers.)\r
+\r
+Some notable modes are described below; for fuller explanations, see\r
+your server documentation.\r
+\r
+\b \I{ERASE special character}\cw{ERASE} is the character that when typed\r
+by the user will delete one space to the left. When set to \q{Auto}\r
+(the default setting), this follows the setting of the local Backspace\r
+key in PuTTY (see \k{config-backspace}).\r
+\r
+\lcont{\r
+This and other \i{special character}s are specified using \c{^C} notation\r
+for Ctrl-C, and so on. Use \c{^<27>} or \c{^<0x1B>} to specify a\r
+character numerically, and \c{^~} to get a literal \c{^}. Other\r
+non-control characters are denoted by themselves. Leaving the box\r
+entirely blank indicates that \e{no} character should be assigned to\r
+the specified function, although this may not be supported by all\r
+servers.\r
+}\r
+\r
+\b \I{QUIT special character}\cw{QUIT} is a special character that\r
+usually forcefully ends the current process on the server\r
+(\cw{SIGQUIT}). On many servers its default setting is Ctrl-backslash\r
+(\c{^\\}), which is easy to accidentally invoke on many keyboards. If\r
+this is getting in your way, you may want to change it to another\r
+character or turn it off entirely.\r
+\r
+\b Boolean modes such as \cw{ECHO} and \cw{ICANON} can be specified in\r
+PuTTY in a variety of ways, such as \cw{true}/\cw{false},\r
+\cw{yes}/\cw{no}, and \cw{0}/\cw{1}.\r
+\r
+\b Terminal speeds are configured elsewhere; see \k{config-termspeed}.\r
+\r
+\H{config-ssh-x11} The X11 panel\r
+\r
+\cfg{winhelp-topic}{ssh.tunnels.x11}\r
+\r
+The X11 panel allows you to configure \i{forwarding of X11} over an\r
+SSH connection.\r
+\r
+If your server lets you run X Window System applications, X11\r
+forwarding allows you to securely give those applications access to\r
+a local X display on your PC.\r
+\r
+To enable X11 forwarding, check the \q{Enable X11 forwarding} box.\r
+If your X display is somewhere unusual, you will need to enter its\r
+location in the \q{X display location} box; if this is left blank,\r
+PuTTY will try to find a sensible default in the environment, or use the\r
+primary local display (\c{:0}) if that fails.\r
+\r
+See \k{using-x-forwarding} for more information about X11\r
+forwarding.\r
+\r
+\S{config-ssh-x11auth} Remote \i{X11 authentication}\r
+\r
+\cfg{winhelp-topic}{ssh.tunnels.x11auth}\r
+\r
+If you are using X11 forwarding, the virtual X server created on the\r
+SSH server machine will be protected by authorisation data. This\r
+data is invented, and checked, by PuTTY.\r
+\r
+The usual authorisation method used for this is called\r
+\i\cw{MIT-MAGIC-COOKIE-1}. This is a simple password-style protocol:\r
+the X client sends some cookie data to the server, and the server\r
+checks that it matches the real cookie. The cookie data is sent over\r
+an unencrypted X11 connection; so if you allow a client on a third\r
+machine to access the virtual X server, then the cookie will be sent\r
+in the clear.\r
+\r
+PuTTY offers the alternative protocol \i\cw{XDM-AUTHORIZATION-1}. This\r
+is a cryptographically authenticated protocol: the data sent by the\r
+X client is different every time, and it depends on the IP address\r
+and port of the client's end of the connection and is also stamped\r
+with the current time. So an eavesdropper who captures an\r
+\cw{XDM-AUTHORIZATION-1} string cannot immediately re-use it for\r
+their own X connection.\r
+\r
+PuTTY's support for \cw{XDM-AUTHORIZATION-1} is a somewhat\r
+experimental feature, and may encounter several problems:\r
+\r
+\b Some X clients probably do not even support\r
+\cw{XDM-AUTHORIZATION-1}, so they will not know what to do with the\r
+data PuTTY has provided.\r
+\r
+\b This authentication mechanism will only work in SSH-2. In SSH-1,\r
+the SSH server does not tell the client the source address of\r
+a forwarded connection in a machine-readable format, so it's\r
+impossible to verify the \cw{XDM-AUTHORIZATION-1} data.\r
+\r
+\b You may find this feature causes problems with some SSH servers,\r
+which will not clean up \cw{XDM-AUTHORIZATION-1} data after a\r
+session, so that if you then connect to the same server using\r
+a client which only does \cw{MIT-MAGIC-COOKIE-1} and are allocated\r
+the same remote display number, you might find that out-of-date\r
+authentication data is still present on your server and your X\r
+connections fail.\r
+\r
+PuTTY's default is \cw{MIT-MAGIC-COOKIE-1}. If you change it, you\r
+should be sure you know what you're doing.\r
+\r
+\S{config-ssh-xauthority} X authority file for local display\r
+\r
+\cfg{winhelp-topic}{ssh.tunnels.xauthority}\r
+\r
+If you are using X11 forwarding, the local X server to which your\r
+forwarded connections are eventually directed may itself require\r
+authorisation.\r
+\r
+Some Windows X servers do not require this: they do authorisation by\r
+simpler means, such as accepting any connection from the local\r
+machine but not from anywhere else. However, if your X server does\r
+require authorisation, then PuTTY needs to know what authorisation\r
+is required.\r
+\r
+One way in which this data might be made available is for the X\r
+server to store it somewhere in a file which has the same format\r
+as the Unix \c{.Xauthority} file. If this is how your Windows X\r
+server works, then you can tell PuTTY where to find this file by\r
+configuring this option. By default, PuTTY will not attempt to find\r
+any authorisation for your local display.\r
+\r
+\H{config-ssh-portfwd} \I{port forwarding}The Tunnels panel\r
+\r
+\cfg{winhelp-topic}{ssh.tunnels.portfwd}\r
+\r
+The Tunnels panel allows you to configure tunnelling of arbitrary\r
+connection types through an SSH connection.\r
+\r
+Port forwarding allows you to tunnel other types of \i{network\r
+connection} down an SSH session. See \k{using-port-forwarding} for a\r
+general discussion of port forwarding and how it works.\r
+\r
+The port forwarding section in the Tunnels panel shows a list of all\r
+the port forwardings that PuTTY will try to set up when it connects\r
+to the server. By default no port forwardings are set up, so this\r
+list is empty.\r
+\r
+To add a port forwarding:\r
+\r
+\b Set one of the \q{Local} or \q{Remote} radio buttons, depending\r
+on whether you want to \I{local port forwarding}forward a local port\r
+to a remote destination (\q{Local}) or \I{remote port forwarding}forward\r
+a remote port to a local destination (\q{Remote}). Alternatively,\r
+select \q{Dynamic} if you want PuTTY to \I{dynamic port forwarding}provide\r
+a local SOCKS 4/4A/5 proxy on a local port (note that this proxy only\r
+supports TCP connections; the SSH protocol does not support forwarding\r
+\i{UDP}).\r
+\r
+\b Enter a source \i{port number} into the \q{Source port} box. For\r
+local forwardings, PuTTY will listen on this port of your PC. For\r
+remote forwardings, your SSH server will listen on this port of the\r
+remote machine. Note that most servers will not allow you to listen\r
+on \I{privileged port}port numbers less than 1024.\r
+\r
+\b If you have selected \q{Local} or \q{Remote} (this step is not\r
+needed with \q{Dynamic}), enter a hostname and port number separated\r
+by a colon, in the \q{Destination} box. Connections received on the\r
+source port will be directed to this destination. For example, to\r
+connect to a POP-3 server, you might enter\r
+\c{popserver.example.com:110}.\r
+\r
+\b Click the \q{Add} button. Your forwarding details should appear\r
+in the list box.\r
+\r
+To remove a port forwarding, simply select its details in the list\r
+box, and click the \q{Remove} button.\r
+\r
+In the \q{Source port} box, you can also optionally enter an \I{listen\r
+address}IP address to listen on, by specifying (for instance)\r
+\c{127.0.0.5:79}.\r
+See \k{using-port-forwarding} for more information on how this\r
+works and its restrictions.\r
+\r
+In place of port numbers, you can enter \i{service names}, if they are\r
+known to the local system. For instance, in the \q{Destination} box,\r
+you could enter \c{popserver.example.com:pop3}.\r
+\r
+You can \I{port forwarding, changing mid-session}modify the currently\r
+active set of port forwardings in mid-session using \q{Change\r
+Settings} (see \k{using-changesettings}). If you delete a local or\r
+dynamic port forwarding in mid-session, PuTTY will stop listening for\r
+connections on that port, so it can be re-used by another program. If\r
+you delete a remote port forwarding, note that:\r
+\r
+\b The SSH-1 protocol contains no mechanism for asking the server to\r
+stop listening on a remote port.\r
+\r
+\b The SSH-2 protocol does contain such a mechanism, but not all SSH\r
+servers support it. (In particular, \i{OpenSSH} does not support it in\r
+any version earlier than 3.9.)\r
+\r
+If you ask to delete a remote port forwarding and PuTTY cannot make\r
+the server actually stop listening on the port, it will instead just\r
+start refusing incoming connections on that port. Therefore,\r
+although the port cannot be reused by another program, you can at\r
+least be reasonably sure that server-side programs can no longer\r
+access the service at your end of the port forwarding.\r
+\r
+If you delete a forwarding, any existing connections established using\r
+that forwarding remain open. Similarly, changes to global settings\r
+such as \q{Local ports accept connections from other hosts} only take\r
+effect on new forwardings.\r
+\r
+If the connection you are forwarding over SSH is itself a second SSH\r
+connection made by another copy of PuTTY, you might find the\r
+\q{logical host name} configuration option useful to warn PuTTY of\r
+which host key it should be expecting. See \k{config-loghost} for\r
+details of this.\r
+\r
+\S{config-ssh-portfwd-localhost} Controlling the visibility of\r
+forwarded ports\r
+\r
+\cfg{winhelp-topic}{ssh.tunnels.portfwd.localhost}\r
+\r
+The source port for a forwarded connection usually does not accept\r
+connections from any machine except the \I{localhost}SSH client or\r
+server machine itself (for local and remote forwardings respectively).\r
+There are controls in the Tunnels panel to change this:\r
+\r
+\b The \q{Local ports accept connections from other hosts} option\r
+allows you to set up local-to-remote port forwardings in such a way\r
+that machines other than your client PC can connect to the forwarded\r
+port. (This also applies to dynamic SOCKS forwarding.)\r
+\r
+\b The \q{Remote ports do the same} option does the same thing for\r
+remote-to-local port forwardings (so that machines other than the\r
+SSH server machine can connect to the forwarded port.) Note that\r
+this feature is only available in the SSH-2 protocol, and not all\r
+SSH-2 servers support it (\i{OpenSSH} 3.0 does not, for example).\r
+\r
+\S{config-ssh-portfwd-address-family} Selecting \i{Internet protocol\r
+version} for forwarded ports\r
+\r
+\cfg{winhelp-topic}{ssh.tunnels.portfwd.ipversion}\r
+\r
+This switch allows you to select a specific Internet protocol (\i{IPv4}\r
+or \i{IPv6}) for the local end of a forwarded port. By default, it is\r
+set on \q{Auto}, which means that:\r
+\r
+\b for a local-to-remote port forwarding, PuTTY will listen for\r
+incoming connections in both IPv4 and (if available) IPv6\r
+\r
+\b for a remote-to-local port forwarding, PuTTY will choose a\r
+sensible protocol for the outgoing connection.\r
+\r
+This overrides the general Internet protocol version preference\r
+on the Connection panel (see \k{config-address-family}).\r
+\r
+Note that some operating systems may listen for incoming connections\r
+in IPv4 even if you specifically asked for IPv6, because their IPv4\r
+and IPv6 protocol stacks are linked together. Apparently \i{Linux} does\r
+this, and Windows does not. So if you're running PuTTY on Windows\r
+and you tick \q{IPv6} for a local or dynamic port forwarding, it\r
+will \e{only} be usable by connecting to it using IPv6; whereas if\r
+you do the same on Linux, you can also use it with IPv4. However,\r
+ticking \q{Auto} should always give you a port which you can connect\r
+to using either protocol.\r
+\r
+\H{config-ssh-bugs} \I{SSH server bugs}The Bugs panel\r
+\r
+Not all SSH servers work properly. Various existing servers have\r
+bugs in them, which can make it impossible for a client to talk to\r
+them unless it knows about the bug and works around it.\r
+\r
+Since most servers announce their software version number at the\r
+beginning of the SSH connection, PuTTY will attempt to detect which\r
+bugs it can expect to see in the server and automatically enable\r
+workarounds. However, sometimes it will make mistakes; if the server\r
+has been deliberately configured to conceal its version number, or\r
+if the server is a version which PuTTY's bug database does not know\r
+about, then PuTTY will not know what bugs to expect.\r
+\r
+The Bugs panel allows you to manually configure the bugs PuTTY\r
+expects to see in the server. Each bug can be configured in three\r
+states:\r
+\r
+\b \q{Off}: PuTTY will assume the server does not have the bug.\r
+\r
+\b \q{On}: PuTTY will assume the server \e{does} have the bug.\r
+\r
+\b \q{Auto}: PuTTY will use the server's version number announcement\r
+to try to guess whether or not the server has the bug.\r
+\r
+\S{config-ssh-bug-ignore1} \q{Chokes on SSH-1 \i{ignore message}s}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.ignore1}\r
+\r
+An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol\r
+which can be sent from the client to the server, or from the server\r
+to the client, at any time. Either side is required to ignore the\r
+message whenever it receives it. PuTTY uses ignore messages to\r
+\I{password camouflage}hide the password packet in SSH-1, so that\r
+a listener cannot tell the length of the user's password; it also\r
+uses ignore messages for connection \i{keepalives} (see\r
+\k{config-keepalive}).\r
+\r
+If this bug is detected, PuTTY will stop using ignore messages. This\r
+means that keepalives will stop working, and PuTTY will have to fall\r
+back to a secondary defence against SSH-1 password-length\r
+eavesdropping. See \k{config-ssh-bug-plainpw1}. If this bug is\r
+enabled when talking to a correct server, the session will succeed,\r
+but keepalives will not work and the session might be more\r
+vulnerable to eavesdroppers than it could be.\r
+\r
+\S{config-ssh-bug-plainpw1} \q{Refuses all SSH-1 \i{password camouflage}}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.plainpw1}\r
+\r
+When talking to an SSH-1 server which cannot deal with ignore\r
+messages (see \k{config-ssh-bug-ignore1}), PuTTY will attempt to\r
+disguise the length of the user's password by sending additional\r
+padding \e{within} the password packet. This is technically a\r
+violation of the SSH-1 specification, and so PuTTY will only do it\r
+when it cannot use standards-compliant ignore messages as\r
+camouflage. In this sense, for a server to refuse to accept a padded\r
+password packet is not really a bug, but it does make life\r
+inconvenient if the server can also not handle ignore messages.\r
+\r
+If this \q{bug} is detected, PuTTY will assume that neither ignore\r
+messages nor padding are acceptable, and that it thus has no choice\r
+but to send the user's password with no form of camouflage, so that\r
+an eavesdropping user will be easily able to find out the exact length\r
+of the password. If this bug is enabled when talking to a correct\r
+server, the session will succeed, but will be more vulnerable to\r
+eavesdroppers than it could be.\r
+\r
+This is an SSH-1-specific bug. SSH-2 is secure against this type of\r
+attack.\r
+\r
+\S{config-ssh-bug-rsa1} \q{Chokes on SSH-1 \i{RSA} authentication}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.rsa1}\r
+\r
+Some SSH-1 servers cannot deal with RSA authentication messages at\r
+all. If \i{Pageant} is running and contains any SSH-1 keys, PuTTY will\r
+normally automatically try RSA authentication before falling back to\r
+passwords, so these servers will crash when they see the RSA attempt.\r
+\r
+If this bug is detected, PuTTY will go straight to password\r
+authentication. If this bug is enabled when talking to a correct\r
+server, the session will succeed, but of course RSA authentication\r
+will be impossible.\r
+\r
+This is an SSH-1-specific bug.\r
+\r
+\S{config-ssh-bug-ignore2} \q{Chokes on SSH-2 \i{ignore message}s}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.ignore2}\r
+\r
+An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol\r
+which can be sent from the client to the server, or from the server\r
+to the client, at any time. Either side is required to ignore the\r
+message whenever it receives it. PuTTY uses ignore messages in SSH-2\r
+to confuse the encrypted data stream and make it harder to\r
+cryptanalyse. It also uses ignore messages for connection\r
+\i{keepalives} (see \k{config-keepalive}).\r
+\r
+If it believes the server to have this bug, PuTTY will stop using\r
+ignore messages. If this bug is enabled when talking to a correct\r
+server, the session will succeed, but keepalives will not work and\r
+the session might be less cryptographically secure than it could be.\r
+\r
+\S{config-ssh-bug-hmac2} \q{Miscomputes SSH-2 HMAC keys}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.hmac2}\r
+\r
+Versions 2.3.0 and below of the SSH server software from\r
+\cw{ssh.com} compute the keys for their \i{HMAC} \i{message authentication\r
+code}s incorrectly. A typical symptom of this problem is that PuTTY\r
+dies unexpectedly at the beginning of the session, saying\r
+\q{Incorrect MAC received on packet}.\r
+\r
+If this bug is detected, PuTTY will compute its HMAC keys in the\r
+same way as the buggy server, so that communication will still be\r
+possible. If this bug is enabled when talking to a correct server,\r
+communication will fail.\r
+\r
+This is an SSH-2-specific bug.\r
+\r
+\S{config-ssh-bug-derivekey2} \q{Miscomputes SSH-2 \i{encryption} keys}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.derivekey2}\r
+\r
+Versions below 2.0.11 of the SSH server software from \i\cw{ssh.com}\r
+compute the keys for the session encryption incorrectly. This\r
+problem can cause various error messages, such as \q{Incoming packet\r
+was garbled on decryption}, or possibly even \q{Out of memory}.\r
+\r
+If this bug is detected, PuTTY will compute its encryption keys in\r
+the same way as the buggy server, so that communication will still\r
+be possible. If this bug is enabled when talking to a correct\r
+server, communication will fail.\r
+\r
+This is an SSH-2-specific bug.\r
+\r
+\S{config-ssh-bug-sig} \q{Requires padding on SSH-2 \i{RSA} \i{signatures}}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.rsapad2}\r
+\r
+Versions below 3.3 of \i{OpenSSH} require SSH-2 RSA signatures to be\r
+padded with zero bytes to the same length as the RSA key modulus.\r
+The SSH-2 specification says that an unpadded signature MUST be\r
+accepted, so this is a bug. A typical symptom of this problem is\r
+that PuTTY mysteriously fails RSA authentication once in every few\r
+hundred attempts, and falls back to passwords.\r
+\r
+If this bug is detected, PuTTY will pad its signatures in the way\r
+OpenSSH expects. If this bug is enabled when talking to a correct\r
+server, it is likely that no damage will be done, since correct\r
+servers usually still accept padded signatures because they're used\r
+to talking to OpenSSH.\r
+\r
+This is an SSH-2-specific bug.\r
+\r
+\S{config-ssh-bug-pksessid2} \q{Misuses the \i{session ID} in SSH-2 PK auth}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.pksessid2}\r
+\r
+Versions below 2.3 of \i{OpenSSH} require SSH-2 \i{public-key authentication}\r
+to be done slightly differently: the data to be signed by the client\r
+contains the session ID formatted in a different way. If public-key\r
+authentication mysteriously does not work but the Event Log (see\r
+\k{using-eventlog}) thinks it has successfully sent a signature, it\r
+might be worth enabling the workaround for this bug to see if it\r
+helps.\r
+\r
+If this bug is detected, PuTTY will sign data in the way OpenSSH\r
+expects. If this bug is enabled when talking to a correct server,\r
+SSH-2 public-key authentication will fail.\r
+\r
+This is an SSH-2-specific bug.\r
+\r
+\S{config-ssh-bug-rekey} \q{Handles SSH-2 key re-exchange badly}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.rekey2}\r
+\r
+Some SSH servers cannot cope with \i{repeat key exchange} at\r
+all, and will ignore attempts by the client to start one. Since\r
+PuTTY pauses the session while performing a repeat key exchange, the\r
+effect of this would be to cause the session to hang after an hour\r
+(unless you have your rekey timeout set differently; see\r
+\k{config-ssh-kex-rekey} for more about rekeys).\r
+Other, very old, SSH servers handle repeat key exchange even more\r
+badly, and disconnect upon receiving a repeat key exchange request.\r
+\r
+If this bug is detected, PuTTY will never initiate a repeat key\r
+exchange. If this bug is enabled when talking to a correct server,\r
+the session should still function, but may be less secure than you\r
+would expect.\r
+\r
+This is an SSH-2-specific bug.\r
+\r
+\S{config-ssh-bug-maxpkt2} \q{Ignores SSH-2 \i{maximum packet size}}\r
+\r
+\cfg{winhelp-topic}{ssh.bugs.maxpkt2}\r
+\r
+When an SSH-2 channel is set up, each end announces the maximum size\r
+of data packet that it is willing to receive for that channel.  Some\r
+servers ignore PuTTY's announcement and send packets larger than PuTTY\r
+is willing to accept, causing it to report \q{Incoming packet was\r
+garbled on decryption}.\r
+\r
+If this bug is detected, PuTTY never allows the channel's\r
+\i{flow-control window} to grow large enough to allow the server to\r
+send an over-sized packet.  If this bug is enabled when talking to a\r
+correct server, the session will work correctly, but download\r
+performance will be less than it could be.\r
+\r
+\H{config-serial} The Serial panel\r
+\r
+The \i{Serial} panel allows you to configure options that only apply\r
+when PuTTY is connecting to a local \I{serial port}\i{serial line}.\r
+\r
+\S{config-serial-line} Selecting a serial line to connect to\r
+\r
+\cfg{winhelp-topic}{serial.line}\r
+\r
+The \q{Serial line to connect to} box allows you to choose which\r
+serial line you want PuTTY to talk to, if your computer has more\r
+than one serial port.\r
+\r
+On Windows, the first serial line is called \i\cw{COM1}, and if there\r
+is a second it is called \cw{COM2}, and so on.\r
+\r
+This configuration setting is also visible on the Session panel,\r
+where it replaces the \q{Host Name} box (see \k{config-hostname}) if\r
+the connection type is set to \q{Serial}.\r
+\r
+\S{config-serial-speed} Selecting the speed of your serial line\r
+\r
+\cfg{winhelp-topic}{serial.speed}\r
+\r
+The \q{Speed} box allows you to choose the speed (or \q{baud rate})\r
+at which to talk to the serial line. Typical values might be 9600,\r
+19200, 38400 or 57600. Which one you need will depend on the device\r
+at the other end of the serial cable; consult the manual for that\r
+device if you are in doubt.\r
+\r
+This configuration setting is also visible on the Session panel,\r
+where it replaces the \q{Port} box (see \k{config-hostname}) if the\r
+connection type is set to \q{Serial}.\r
+\r
+\S{config-serial-databits} Selecting the number of data bits\r
+\r
+\cfg{winhelp-topic}{serial.databits}\r
+\r
+The \q{Data bits} box allows you to choose how many data bits are\r
+transmitted in each byte sent or received through the serial line.\r
+Typical values are 7 or 8.\r
+\r
+\S{config-serial-stopbits} Selecting the number of stop bits\r
+\r
+\cfg{winhelp-topic}{serial.stopbits}\r
+\r
+The \q{Stop bits} box allows you to choose how many stop bits are\r
+used in the serial line protocol. Typical values are 1, 1.5 or 2.\r
+\r
+\S{config-serial-parity} Selecting the serial parity checking scheme\r
+\r
+\cfg{winhelp-topic}{serial.parity}\r
+\r
+The \q{Parity} box allows you to choose what type of parity checking\r
+is used on the serial line. The settings are:\r
+\r
+\b \q{None}: no parity bit is sent at all.\r
+\r
+\b \q{Odd}: an extra parity bit is sent alongside each byte, and\r
+arranged so that the total number of 1 bits is odd.\r
+\r
+\b \q{Even}: an extra parity bit is sent alongside each byte, and\r
+arranged so that the total number of 1 bits is even.\r
+\r
+\b \q{Mark}: an extra parity bit is sent alongside each byte, and\r
+always set to 1.\r
+\r
+\b \q{Space}: an extra parity bit is sent alongside each byte, and\r
+always set to 0.\r
+\r
+\S{config-serial-flow} Selecting the serial flow control scheme\r
+\r
+\cfg{winhelp-topic}{serial.flow}\r
+\r
+The \q{Flow control} box allows you to choose what type of flow\r
+control checking is used on the serial line. The settings are:\r
+\r
+\b \q{None}: no flow control is done. Data may be lost if either\r
+side attempts to send faster than the serial line permits.\r
+\r
+\b \q{XON/XOFF}: flow control is done by sending XON and XOFF\r
+characters within the data stream.\r
+\r
+\b \q{RTS/CTS}: flow control is done using the RTS and CTS wires on\r
+the serial line.\r
+\r
+\b \q{DSR/DTR}: flow control is done using the DSR and DTR wires on\r
+the serial line.\r
+\r
+\H{config-file} \ii{Storing configuration in a file}\r
+\r
+PuTTY does not currently support storing its configuration in a file\r
+instead of the \i{Registry}. However, you can work around this with a\r
+couple of \i{batch file}s.\r
+\r
+You will need a file called (say) \c{PUTTY.BAT} which imports the\r
+contents of a file into the Registry, then runs PuTTY, exports the\r
+contents of the Registry back into the file, and deletes the\r
+Registry entries. This can all be done using the Regedit command\r
+line options, so it's all automatic. Here is what you need in\r
+\c{PUTTY.BAT}:\r
+\r
+\c @ECHO OFF\r
+\c regedit /s putty.reg\r
+\c regedit /s puttyrnd.reg\r
+\c start /w putty.exe\r
+\c regedit /ea new.reg HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\r
+\c copy new.reg putty.reg\r
+\c del new.reg\r
+\c regedit /s puttydel.reg\r
+\r
+This batch file needs two auxiliary files: \c{PUTTYRND.REG} which\r
+sets up an initial safe location for the \c{PUTTY.RND} random seed\r
+file, and \c{PUTTYDEL.REG} which destroys everything in the Registry\r
+once it's been successfully saved back to the file.\r
+\r
+Here is \c{PUTTYDEL.REG}:\r
+\r
+\c REGEDIT4\r
+\c  \r
+\c [-HKEY_CURRENT_USER\Software\SimonTatham\PuTTY]\r
+\r
+Here is an example \c{PUTTYRND.REG} file:\r
+\r
+\c REGEDIT4\r
+\c  \r
+\c [HKEY_CURRENT_USER\Software\SimonTatham\PuTTY]\r
+\c "RandSeedFile"="a:\\putty.rnd"\r
+\r
+You should replace \c{a:\\putty.rnd} with the location where you\r
+want to store your random number data. If the aim is to carry around\r
+PuTTY and its settings on one floppy, you probably want to store it\r
+on the floppy.\r
diff --git a/putty/DOC/ERRORS.BUT b/putty/DOC/ERRORS.BUT
new file mode 100644 (file)
index 0000000..4de6713
--- /dev/null
@@ -0,0 +1,356 @@
+\define{versioniderrors} \versionid $Id: errors.but 8897 2010-03-13 14:47:14Z jacob $\r
+\r
+\C{errors} Common \i{error messages}\r
+\r
+This chapter lists a number of common error messages which PuTTY and\r
+its associated tools can produce, and explains what they mean in\r
+more detail.\r
+\r
+We do not attempt to list \e{all} error messages here: there are\r
+many which should never occur, and some which should be\r
+self-explanatory. If you get an error message which is not listed in\r
+this chapter and which you don't understand, report it to us as a\r
+bug (see \k{feedback}) and we will add documentation for it.\r
+\r
+\H{errors-hostkey-absent} \q{The server's host key is not cached in\r
+the registry}\r
+\r
+\cfg{winhelp-topic}{errors.hostkey.absent}\r
+\r
+This error message occurs when PuTTY connects to a new SSH server.\r
+Every server identifies itself by means of a host key; once PuTTY\r
+knows the host key for a server, it will be able to detect if a\r
+malicious attacker redirects your connection to another machine.\r
+\r
+If you see this message, it means that PuTTY has not seen this host\r
+key before, and has no way of knowing whether it is correct or not.\r
+You should attempt to verify the host key by other means, such as\r
+asking the machine's administrator.\r
+\r
+If you see this message and you know that your installation of PuTTY\r
+\e{has} connected to the same server before, it may have been\r
+recently upgraded to SSH protocol version 2. SSH protocols 1 and 2\r
+use separate host keys, so when you first use \i{SSH-2} with a server\r
+you have only used SSH-1 with before, you will see this message\r
+again. You should verify the correctness of the key as before.\r
+\r
+See \k{gs-hostkey} for more information on host keys.\r
+\r
+\H{errors-hostkey-wrong} \q{WARNING - POTENTIAL SECURITY BREACH!}\r
+\r
+\cfg{winhelp-topic}{errors.hostkey.changed}\r
+\r
+This message, followed by \q{The server's host key does not match\r
+the one PuTTY has cached in the registry}, means that PuTTY has\r
+connected to the SSH server before, knows what its host key\r
+\e{should} be, but has found a different one.\r
+\r
+This may mean that a malicious attacker has replaced your server\r
+with a different one, or has redirected your network connection to\r
+their own machine. On the other hand, it may simply mean that the\r
+administrator of your server has accidentally changed the key while\r
+upgrading the SSH software; this \e{shouldn't} happen but it is\r
+unfortunately possible.\r
+\r
+You should contact your server's administrator and see whether they\r
+expect the host key to have changed. If so, verify the new host key\r
+in the same way as you would if it was new.\r
+\r
+See \k{gs-hostkey} for more information on host keys.\r
+\r
+\H{errors-portfwd-space} \q{Out of space for port forwardings}\r
+\r
+PuTTY has a fixed-size buffer which it uses to store the details of\r
+all \i{port forwardings} you have set up in an SSH session. If you\r
+specify too many port forwardings on the PuTTY or Plink command line\r
+and this buffer becomes full, you will see this error message.\r
+\r
+We need to fix this (fixed-size buffers are almost always a mistake)\r
+but we haven't got round to it. If you actually have trouble with\r
+this, let us know and we'll move it up our priority list.\r
+\r
+If you're running into this limit, you may want to consider using\r
+dynamic port forwarding instead; see \k{using-port-forwarding}.\r
+\r
+\H{errors-cipher-warning} \q{The first cipher supported by the server is\r
+... below the configured warning threshold}\r
+\r
+This occurs when the SSH server does not offer any ciphers which you\r
+have configured PuTTY to consider strong enough. By default, PuTTY\r
+puts up this warning only for \ii{single-DES} and \i{Arcfour} encryption.\r
+\r
+See \k{config-ssh-encryption} for more information on this message.\r
+\r
+\H{errors-toomanyauth} \q{Server sent disconnect message type 2\r
+(protocol error): "Too many authentication failures for root"}\r
+\r
+This message is produced by an \i{OpenSSH} (or \i{Sun SSH}) server if it\r
+receives more failed authentication attempts than it is willing to\r
+tolerate.\r
+\r
+This can easily happen if you are using Pageant and have a\r
+large number of keys loaded into it, since these servers count each\r
+offer of a public key as an authentication attempt. This can be worked\r
+around by specifying the key that's required for the authentication in\r
+the PuTTY configuration (see \k{config-ssh-privkey}); PuTTY will ignore\r
+any other keys Pageant may have, but will ask Pageant to do the\r
+authentication, so that you don't have to type your passphrase.\r
+\r
+On the server, this can be worked around by disabling public-key\r
+authentication or (for Sun SSH only) by increasing \c{MaxAuthTries} in\r
+\c{sshd_config}.\r
+\r
+\H{errors-memory} \q{\ii{Out of memory}}\r
+\r
+This occurs when PuTTY tries to allocate more memory than the system\r
+can give it. This \e{may} happen for genuine reasons: if the\r
+computer really has run out of memory, or if you have configured an\r
+extremely large number of lines of scrollback in your terminal.\r
+PuTTY is not able to recover from running out of memory; it will\r
+terminate immediately after giving this error.\r
+\r
+However, this error can also occur when memory is not running out at\r
+all, because PuTTY receives data in the wrong format. In SSH-2 and\r
+also in SFTP, the server sends the length of each message before the\r
+message itself; so PuTTY will receive the length, try to allocate\r
+space for the message, and then receive the rest of the message. If\r
+the length PuTTY receives is garbage, it will try to allocate a\r
+ridiculous amount of memory, and will terminate with an \q{Out of\r
+memory} error.\r
+\r
+This can happen in SSH-2, if PuTTY and the server have not enabled\r
+encryption in the same way (see \k{faq-outofmem} in the FAQ). Some\r
+versions of \i{OpenSSH} have a known problem with this: see\r
+\k{faq-openssh-bad-openssl}.\r
+\r
+This can also happen in PSCP or PSFTP, if your \i{login scripts} on the\r
+server generate output: the client program will be expecting an SFTP\r
+message starting with a length, and if it receives some text from\r
+your login scripts instead it will try to interpret them as a\r
+message length. See \k{faq-outofmem2} for details of this.\r
+\r
+\H{errors-internal} \q{\ii{Internal error}}, \q{\ii{Internal fault}},\r
+\q{\ii{Assertion failed}}\r
+\r
+Any error beginning with the word \q{Internal} should \e{never}\r
+occur. If it does, there is a bug in PuTTY by definition; please see\r
+\k{feedback} and report it to us.\r
+\r
+Similarly, any error message starting with \q{Assertion failed} is a\r
+bug in PuTTY. Please report it to us, and include the exact text\r
+from the error message box.\r
+\r
+\H{errors-cant-load-key} \q{Unable to use this private key file},\r
+\q{Couldn't load private key}, \q{Key is of wrong type}\r
+\r
+\cfg{winhelp-topic}{errors.cantloadkey}\r
+\r
+Various forms of this error are printed in the PuTTY window, or\r
+written to the PuTTY Event Log (see \k{using-eventlog}) when trying\r
+public-key authentication, or given by Pageant when trying to load a\r
+private key.\r
+\r
+If you see one of these messages, it often indicates that you've tried\r
+to load a key of an inappropriate type into PuTTY, Plink, PSCP, PSFTP,\r
+or Pageant.\r
+\r
+You may have specified a key that's inappropriate for the connection\r
+you're making. The SSH-1 and SSH-2 protocols require different private\r
+key formats, and a SSH-1 key can't be used for a SSH-2 connection (or\r
+vice versa).\r
+\r
+Alternatively, you may have tried to load an SSH-2 key in a \q{foreign}\r
+format (OpenSSH or \cw{ssh.com}) directly into one of the PuTTY tools,\r
+in which case you need to import it into PuTTY's native format\r
+(\c{*.PPK}) using PuTTYgen - see \k{puttygen-conversions}.\r
+\r
+\H{errors-refused} \q{Server refused our public key} or \q{Key\r
+refused}\r
+\r
+Various forms of this error are printed in the PuTTY window, or\r
+written to the PuTTY Event Log (see \k{using-eventlog}) when trying\r
+public-key authentication.\r
+\r
+If you see one of these messages, it means that PuTTY has sent a\r
+public key to the server and offered to authenticate with it, and\r
+the server has refused to accept authentication. This usually means\r
+that the server is not configured to accept this key to authenticate\r
+this user.\r
+\r
+This is almost certainly not a problem with PuTTY. If you see this\r
+type of message, the first thing you should do is check your\r
+\e{server} configuration carefully. Common errors include having\r
+the wrong permissions or ownership set on the public key or the\r
+user's home directory on the server. Also, read the PuTTY Event Log;\r
+the server may have sent diagnostic messages explaining exactly what\r
+problem it had with your setup.\r
+\r
+\K{pubkey-gettingready} has some hints on server-side public key\r
+setup.\r
+\r
+\H{errors-access-denied} \q{Access denied}, \q{Authentication refused}\r
+\r
+Various forms of this error are printed in the PuTTY window, or\r
+written to the PuTTY Event Log (see \k{using-eventlog}) during\r
+authentication.\r
+\r
+If you see one of these messages, it means that the server has refused \r
+all the forms of authentication PuTTY has tried and it has no further\r
+ideas.\r
+\r
+It may be worth checking the Event Log for diagnostic messages from\r
+the server giving more detail.\r
+\r
+This error can be caused by buggy SSH-1 servers that fail to cope with\r
+the various strategies we use for camouflaging passwords in transit.\r
+Upgrade your server, or use the workarounds described in\r
+\k{config-ssh-bug-ignore1} and possibly \k{config-ssh-bug-plainpw1}.\r
+\r
+\H{errors-no-auth} \q{No supported authentication methods available}\r
+\r
+This error indicates that PuTTY has run out of ways to authenticate\r
+you to an SSH server.  This may be because PuTTY has TIS or\r
+keyboard-interactive authentication disabled, in which case\r
+\k{config-ssh-tis} and \k{config-ssh-ki}.\r
+\r
+\H{errors-crc} \q{Incorrect \i{CRC} received on packet} or \q{Incorrect\r
+\i{MAC} received on packet}\r
+\r
+This error occurs when PuTTY decrypts an SSH packet and its checksum\r
+is not correct. This probably means something has gone wrong in the\r
+encryption or decryption process. It's difficult to tell from this\r
+error message whether the problem is in the client, in the server,\r
+or in between.\r
+\r
+In particular, if the network is corrupting data at the TCP level, it\r
+may only be obvious with cryptographic protocols such as SSH, which\r
+explicitly check the integrity of the transferred data and complain\r
+loudly if the checks fail. Corruption of protocols without integrity\r
+protection (such as HTTP) will manifest in more subtle failures (such\r
+as misdisplayed text or images in a web browser) which may not be\r
+noticed.\r
+\r
+A known server problem which can cause this error is described in\r
+\k{faq-openssh-bad-openssl} in the FAQ.\r
+\r
+\H{errors-garbled} \q{Incoming packet was garbled on decryption}\r
+\r
+This error occurs when PuTTY decrypts an SSH packet and the\r
+decrypted data makes no sense. This probably means something has\r
+gone wrong in the encryption or decryption process. It's difficult\r
+to tell from this error message whether the problem is in the client,\r
+in the server, or in between.\r
+\r
+If you get this error, one thing you could try would be to fiddle with\r
+the setting of \q{Miscomputes SSH-2 encryption keys} (see\r
+\k{config-ssh-bug-derivekey2}) or \q{Ignores SSH-2 maximum packet\r
+size} (see \k{config-ssh-bug-maxpkt2}) on the Bugs panel .\r
+\r
+Another known server problem which can cause this error is described\r
+in \k{faq-openssh-bad-openssl} in the FAQ.\r
+\r
+\H{errors-x11-proxy} \q{PuTTY X11 proxy: \e{various errors}}\r
+\r
+This family of errors are reported when PuTTY is doing X forwarding.\r
+They are sent back to the X application running on the SSH server,\r
+which will usually report the error to the user.\r
+\r
+When PuTTY enables X forwarding (see \k{using-x-forwarding}) it\r
+creates a virtual X display running on the SSH server. This display\r
+requires authentication to connect to it (this is how PuTTY prevents\r
+other users on your server machine from connecting through the PuTTY\r
+proxy to your real X display). PuTTY also sends the server the\r
+details it needs to enable clients to connect, and the server should\r
+put this mechanism in place automatically, so your X applications\r
+should just work.\r
+\r
+A common reason why people see one of these messages is because they\r
+used SSH to log in as one user (let's say \q{fred}), and then used\r
+the Unix \c{su} command to become another user (typically \q{root}).\r
+The original user, \q{fred}, has access to the X authentication data\r
+provided by the SSH server, and can run X applications which are\r
+forwarded over the SSH connection. However, the second user\r
+(\q{root}) does not automatically have the authentication data\r
+passed on to it, so attempting to run an X application as that user\r
+often fails with this error.\r
+\r
+If this happens, \e{it is not a problem with PuTTY}. You need to\r
+arrange for your X authentication data to be passed from the user\r
+you logged in as to the user you used \c{su} to become. How you do\r
+this depends on your particular system; in fact many modern versions\r
+of \c{su} do it automatically.\r
+\r
+\H{errors-connaborted} \q{Network error: Software caused connection\r
+abort}\r
+\r
+This is a generic error produced by the Windows network code when it\r
+kills an established connection for some reason. For example, it might\r
+happen if you pull the network cable out of the back of an\r
+Ethernet-connected computer, or if Windows has any other similar\r
+reason to believe the entire network has become unreachable.\r
+\r
+Windows also generates this error if it has given up on the machine\r
+at the other end of the connection ever responding to it. If the\r
+network between your client and server goes down and your client\r
+then tries to send some data, Windows will make several attempts to\r
+send the data and will then give up and kill the connection. In\r
+particular, this can occur even if you didn't type anything, if you\r
+are using SSH-2 and PuTTY attempts a key re-exchange. (See\r
+\k{config-ssh-kex-rekey} for more about key re-exchange.)\r
+\r
+(It can also occur if you are using keepalives in your connection.\r
+Other people have reported that keepalives \e{fix} this error for\r
+them. See \k{config-keepalive} for a discussion of the pros and cons\r
+of keepalives.)\r
+\r
+We are not aware of any reason why this error might occur that would\r
+represent a bug in PuTTY. The problem is between you, your Windows\r
+system, your network and the remote system.\r
+\r
+\H{errors-connreset} \q{Network error: Connection reset by peer}\r
+\r
+This error occurs when the machines at each end of a network\r
+connection lose track of the state of the connection between them.\r
+For example, you might see it if your SSH server crashes, and\r
+manages to reboot fully before you next attempt to send data to it.\r
+\r
+However, the most common reason to see this message is if you are\r
+connecting through a \i{firewall} or a \i{NAT router} which has timed the\r
+connection out. See \k{faq-idleout} in the FAQ for more details. You\r
+may be able to improve the situation by using keepalives; see\r
+\k{config-keepalive} for details on this.\r
+\r
+Note that Windows can produce this error in some circumstances without\r
+seeing a connection reset from the server, for instance if the\r
+connection to the network is lost.\r
+\r
+\H{errors-connrefused} \q{Network error: Connection refused}\r
+\r
+This error means that the network connection PuTTY tried to make to\r
+your server was rejected by the server. Usually this happens because\r
+the server does not provide the service which PuTTY is trying to\r
+access.\r
+\r
+Check that you are connecting with the correct protocol (SSH, Telnet\r
+or Rlogin), and check that the port number is correct. If that\r
+fails, consult the administrator of your server.\r
+\r
+\H{errors-conntimedout} \q{Network error: Connection timed out}\r
+\r
+This error means that the network connection PuTTY tried to make to\r
+your server received no response at all from the server. Usually\r
+this happens because the server machine is completely isolated from\r
+the network, or because it is turned off.\r
+\r
+Check that you have correctly entered the host name or IP address of\r
+your server machine. If that fails, consult the administrator of\r
+your server.\r
+\r
+\i{Unix} also generates this error when it tries to send data down a\r
+connection and contact with the server has been completely lost\r
+during a connection. (There is a delay of minutes before Unix gives\r
+up on receiving a reply from the server.) This can occur if you type\r
+things into PuTTY while the network is down, but it can also occur\r
+if PuTTY decides of its own accord to send data: due to a repeat key\r
+exchange in SSH-2 (see \k{config-ssh-kex-rekey}) or due to\r
+keepalives (\k{config-keepalive}).\r
diff --git a/putty/DOC/FAQ.BUT b/putty/DOC/FAQ.BUT
new file mode 100644 (file)
index 0000000..913c254
--- /dev/null
@@ -0,0 +1,1454 @@
+\define{versionidfaq} \versionid $Id: faq.but 8733 2009-11-01 22:06:05Z jacob $\r
+\r
+\A{faq} PuTTY \i{FAQ}\r
+\r
+This FAQ is published on the PuTTY web site, and also provided as an\r
+appendix in the manual.\r
+\r
+\H{faq-intro} Introduction\r
+\r
+\S{faq-what}{Question} What is PuTTY?\r
+\r
+PuTTY is a client program for the SSH, Telnet and Rlogin network\r
+protocols.\r
+\r
+These protocols are all used to run a remote session on a computer,\r
+over a network. PuTTY implements the client end of that session: the\r
+end at which the session is displayed, rather than the end at which\r
+it runs.\r
+\r
+In really simple terms: you run PuTTY on a Windows machine, and tell\r
+it to connect to (for example) a Unix machine. PuTTY opens a window.\r
+Then, anything you type into that window is sent straight to the\r
+Unix machine, and everything the Unix machine sends back is\r
+displayed in the window. So you can work on the Unix machine as if\r
+you were sitting at its console, while actually sitting somewhere\r
+else.\r
+\r
+\H{faq-support} Features supported in PuTTY\r
+\r
+\I{supported features}In general, if you want to know if PuTTY supports\r
+a particular feature, you should look for it on the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}{PuTTY web site}.\r
+In particular:\r
+\r
+\b try the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{changes\r
+page}, and see if you can find the feature on there. If a feature is\r
+listed there, it's been implemented. If it's listed as a change made\r
+\e{since} the latest version, it should be available in the\r
+development snapshots, in which case testing will be very welcome.\r
+\r
+\b try the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist\r
+page}, and see if you can find the feature there. If it's on there,\r
+and not in the \q{Recently fixed} section, it probably \e{hasn't} been\r
+implemented.\r
+\r
+\S{faq-ssh2}{Question} Does PuTTY support SSH-2?\r
+\r
+Yes. SSH-2 support has been available in PuTTY since version 0.50.\r
+\r
+Public key authentication (both RSA and DSA) in SSH-2 is new in\r
+version 0.52.\r
+\r
+\S{faq-ssh2-keyfmt}{Question} Does PuTTY support reading OpenSSH or\r
+\cw{ssh.com} SSH-2 private key files?\r
+\r
+PuTTY doesn't support this natively (see\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/key-formats-natively.html}{the wishlist entry}\r
+for reasons why not), but as of 0.53\r
+PuTTYgen can convert both OpenSSH and \cw{ssh.com} private key\r
+files into PuTTY's format.\r
+\r
+\S{faq-ssh1}{Question} Does PuTTY support SSH-1?\r
+\r
+Yes. SSH-1 support has always been available in PuTTY.\r
+\r
+\S{faq-localecho}{Question} Does PuTTY support \i{local echo}?\r
+\r
+Yes. Version 0.52 has proper support for local echo.\r
+\r
+In version 0.51 and before, local echo could not be separated from\r
+local line editing (where you type a line of text locally, and it is\r
+not sent to the server until you press Return, so you have the\r
+chance to edit it and correct mistakes \e{before} the server sees\r
+it). New in version 0.52, local echo and local line editing are\r
+separate options, and by default PuTTY will try to determine\r
+automatically whether to enable them or not, based on which protocol\r
+you have selected and also based on hints from the server. If you\r
+have a problem with PuTTY's default choice, you can force each\r
+option to be enabled or disabled as you choose. The controls are in\r
+the Terminal panel, in the section marked \q{Line discipline\r
+options}.\r
+\r
+\S{faq-savedsettings}{Question} Does PuTTY support storing settings,\r
+so I don't have to change them every time?\r
+\r
+Yes, all of PuTTY's settings can be saved in named session profiles.\r
+You can also change the default settings that are used for new sessions.\r
+See \k{config-saving} in the documentation for how to do this.\r
+\r
+\S{faq-disksettings}{Question} Does PuTTY support storing its\r
+settings in a disk file?\r
+\r
+Not at present, although \k{config-file} in the documentation gives\r
+a method of achieving the same effect.\r
+\r
+\S{faq-fullscreen}{Question} Does PuTTY support full-screen mode,\r
+like a DOS box?\r
+\r
+Yes; this is a new feature in version 0.52.\r
+\r
+\S{faq-password-remember}{Question} Does PuTTY have the ability to\r
+\i{remember my password} so I don't have to type it every time?\r
+\r
+No, it doesn't.\r
+\r
+Remembering your password is a bad plan for obvious security\r
+reasons: anyone who gains access to your machine while you're away\r
+from your desk can find out the remembered password, and use it,\r
+abuse it or change it.\r
+\r
+In addition, it's not even \e{possible} for PuTTY to automatically\r
+send your password in a Telnet session, because Telnet doesn't give\r
+the client software any indication of which part of the login\r
+process is the password prompt. PuTTY would have to guess, by\r
+looking for words like \q{password} in the session data; and if your\r
+login program is written in something other than English, this won't\r
+work.\r
+\r
+In SSH, remembering your password would be possible in theory, but\r
+there doesn't seem to be much point since SSH supports public key\r
+authentication, which is more flexible and more secure. See\r
+\k{pubkey} in the documentation for a full discussion of public key\r
+authentication.\r
+\r
+\S{faq-hostkeys}{Question} Is there an option to turn off the\r
+\I{verifying the host key}annoying host key prompts?\r
+\r
+No, there isn't. And there won't be. Even if you write it yourself\r
+and send us the patch, we won't accept it.\r
+\r
+Those annoying host key prompts are the \e{whole point} of SSH.\r
+Without them, all the cryptographic technology SSH uses to secure\r
+your session is doing nothing more than making an attacker's job\r
+slightly harder; instead of sitting between you and the server with\r
+a packet sniffer, the attacker must actually subvert a router and\r
+start modifying the packets going back and forth. But that's not all\r
+that much harder than just sniffing; and without host key checking,\r
+it will go completely undetected by client or server.\r
+\r
+Host key checking is your guarantee that the encryption you put on\r
+your data at the client end is the \e{same} encryption taken off the\r
+data at the server end; it's your guarantee that it hasn't been\r
+removed and replaced somewhere on the way. Host key checking makes\r
+the attacker's job \e{astronomically} hard, compared to packet\r
+sniffing, and even compared to subverting a router. Instead of\r
+applying a little intelligence and keeping an eye on Bugtraq, the\r
+attacker must now perform a brute-force attack against at least one\r
+military-strength cipher. That insignificant host key prompt really\r
+does make \e{that} much difference.\r
+\r
+If you're having a specific problem with host key checking - perhaps\r
+you want an automated batch job to make use of PSCP or Plink, and\r
+the interactive host key prompt is hanging the batch process - then\r
+the right way to fix it is to add the correct host key to the\r
+Registry in advance. That way, you retain the \e{important} feature\r
+of host key checking: the right key will be accepted and the wrong\r
+ones will not. Adding an option to turn host key checking off\r
+completely is the wrong solution and we will not do it.\r
+\r
+If you have host keys available in the common \i\c{known_hosts} format,\r
+we have a script called \r
+\W{http://svn.tartarus.org/sgt/putty/contrib/kh2reg.py?view=markup}\c{kh2reg.py}\r
+to convert them to a Windows .REG file, which can be installed ahead of\r
+time by double-clicking or using \c{REGEDIT}.\r
+\r
+\S{faq-server}{Question} Will you write an SSH server for the PuTTY\r
+suite, to go with the client?\r
+\r
+No. The only reason we might want to would be if we could easily\r
+re-use existing code and significantly cut down the effort. We don't\r
+believe this is the case; there just isn't enough common ground\r
+between an SSH client and server to make it worthwhile.\r
+\r
+If someone else wants to use bits of PuTTY in the process of writing\r
+a Windows SSH server, they'd be perfectly welcome to of course, but\r
+I really can't see it being a lot less effort for us to do that than\r
+it would be for us to write a server from the ground up. We don't\r
+have time, and we don't have motivation. The code is available if\r
+anyone else wants to try it.\r
+\r
+\S{faq-pscp-ascii}{Question} Can PSCP or PSFTP transfer files in\r
+\i{ASCII} mode?\r
+\r
+Unfortunately not.\r
+\r
+Until recently, this was a limitation of the file transfer protocols:\r
+the SCP and SFTP protocols had no notion of transferring a file in\r
+anything other than binary mode. (This is still true of SCP.)\r
+\r
+The current draft protocol spec of SFTP proposes a means of\r
+implementing ASCII transfer. At some point PSCP/PSFTP may implement\r
+this proposal.\r
+\r
+\H{faq-ports} Ports to other operating systems\r
+\r
+The eventual goal is for PuTTY to be a multi-platform program, able\r
+to run on at least Windows, Mac OS and Unix.\r
+\r
+Porting will become easier once PuTTY has a generalised porting\r
+layer, drawing a clear line between platform-dependent and\r
+platform-independent code. The general intention was for this\r
+porting layer to evolve naturally as part of the process of doing\r
+the first port; a Unix port has now been released and the plan\r
+seems to be working so far.\r
+\r
+\S{faq-ports-general}{Question} What ports of PuTTY exist?\r
+\r
+Currently, release versions of PuTTY tools only run on full Win32\r
+systems and Unix. \q{\i{Win32}} includes versions of Windows from\r
+Windows 95 onwards (as opposed to the 16-bit Windows 3.1; see\r
+\k{faq-win31}), up to and including Windows 7; and we know of no\r
+reason why PuTTY should not continue to work on future versions\r
+of Windows.\r
+\r
+The Windows executables we provide are for the 32-bit \q{\i{x86}}\r
+processor architecture, but they should work fine on 64-bit\r
+processors that are backward-compatible with that architecture.\r
+(We used to also provide executables for Windows for the Alpha\r
+processor, but stopped after 0.58 due to lack of interest.)\r
+\r
+In the development code, partial ports to the Mac OSes exist (see\r
+\k{faq-mac-port}).\r
+\r
+Currently PuTTY does \e{not} run on Windows CE (see \k{faq-wince}).\r
+\r
+We do not have release-quality ports for any other systems at the\r
+present time. If anyone told you we had an EPOC port, or an iPaq port,\r
+or any other port of PuTTY, they were mistaken. We don't.\r
+\r
+There are some third-party ports to various platforms, mentioned\r
+on the \r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website}.\r
+\r
+\S{faq-unix}{Question} \I{Unix version}Is there a port to Unix?\r
+\r
+As of 0.54, there are Unix ports of most of the traditional PuTTY\r
+tools, and also one entirely new application.\r
+\r
+If you look at the source release, you should find a \c{unix}\r
+subdirectory. There are a couple of ways of building it,\r
+including the usual \c{configure}/\c{make}; see the file \c{README}\r
+in the source distribution. This should build you Unix\r
+ports of Plink, PuTTY itself, PuTTYgen, PSCP, PSFTP, and also\r
+\i\c{pterm} - an \cw{xterm}-type program which supports the same\r
+terminal emulation as PuTTY. We do not yet have a Unix port of\r
+Pageant.\r
+\r
+If you don't have \i{Gtk}, you should still be able to build the\r
+command-line tools.\r
+\r
+Note that Unix PuTTY has mostly only been tested on Linux so far;\r
+portability problems such as BSD-style ptys or different header file\r
+requirements are expected.\r
+\r
+\S{faq-unix-why}{Question} What's the point of the Unix port? Unix\r
+has OpenSSH.\r
+\r
+All sorts of little things. \c{pterm} is directly useful to anyone\r
+who prefers PuTTY's terminal emulation to \c{xterm}'s, which at\r
+least some people do. Unix Plink has apparently found a niche among\r
+people who find the complexity of OpenSSL makes OpenSSH hard to\r
+install (and who don't mind Plink not having as many features). Some\r
+users want to generate a large number of SSH keys on Unix and then\r
+copy them all into PuTTY, and the Unix PuTTYgen should allow them to\r
+automate that conversion process.\r
+\r
+There were development advantages as well; porting PuTTY to Unix was\r
+a valuable path-finding effort for other future ports, and also\r
+allowed us to use the excellent Linux tool\r
+\W{http://valgrind.kde.org/}{Valgrind} to help with debugging, which\r
+has already improved PuTTY's stability on \e{all} platforms.\r
+\r
+However, if you're a Unix user and you can see no reason to switch\r
+from OpenSSH to PuTTY/Plink, then you're probably right. We don't\r
+expect our Unix port to be the right thing for everybody.\r
+\r
+\S{faq-wince}{Question} Will there be a port to Windows CE or PocketPC?\r
+\r
+We have done some work on such a port, but it only reached an early\r
+stage, and certainly not a useful one. It's no longer being actively\r
+worked on.\r
+\r
+However, there's a third-party port at\r
+\W{http://www.pocketputty.net/}\c{http://www.pocketputty.net/}.\r
+\r
+\S{faq-win31}{Question} Is there a port to \i{Windows 3.1}?\r
+\r
+PuTTY is a 32-bit application from the ground up, so it won't run on\r
+Windows 3.1 as a native 16-bit program; and it would be \e{very}\r
+hard to port it to do so, because of Windows 3.1's vile memory\r
+allocation mechanisms.\r
+\r
+However, it is possible in theory to compile the existing PuTTY\r
+source in such a way that it will run under \i{Win32s} (an extension to\r
+Windows 3.1 to let you run 32-bit programs). In order to do this\r
+you'll need the right kind of C compiler - modern versions of Visual\r
+C at least have stopped being backwards compatible to Win32s. Also,\r
+the last time we tried this it didn't work very well.\r
+\r
+If you're interested in running PuTTY under Windows 3.1, help and\r
+testing in this area would be very welcome!\r
+\r
+\S{faq-mac-port}{Question} Will there be a port to the \I{Mac OS}Mac?\r
+\r
+There are several answers to this question:\r
+\r
+\b The Unix/Gtk port is already fully working under Mac OS X as an X11\r
+application.\r
+\r
+\b A native (Cocoa) Mac OS X port has been started. It's just about\r
+usable, but is of nowhere near release quality yet, and is likely to\r
+behave in unexpected ways. Currently it's unlikely to be completed\r
+unless someone steps in to help.\r
+\r
+\b A separate port to the classic Mac OS (pre-OSX) is also in\r
+progress; it too is not ready yet.\r
+\r
+\S{faq-epoc}{Question} Will there be a port to EPOC?\r
+\r
+I hope so, but given that ports aren't really progressing very fast\r
+even on systems the developers \e{do} already know how to program\r
+for, it might be a long time before any of us get round to learning\r
+a new system and doing the port for that.\r
+\r
+However, some of the work has been done by other people; see the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website}\r
+for various third-party ports.\r
+\r
+\S{faq-iphone}{Question} Will there be a port to the iPhone?\r
+\r
+We have no plans to write such a port ourselves; none of us has an\r
+iPhone, and developing and publishing applications for it looks\r
+awkward and expensive. Such a port would probably depend upon the\r
+stalled Mac OS X port (see \k{faq-mac-port}).\r
+\r
+However, there is a third-party SSH client for the iPhone and\r
+iPod\_Touch called \W{http://www.instantcocoa.com/products/pTerm/}{pTerm},\r
+which is apparently based on PuTTY. (This is nothing to do with our\r
+similarly-named \c{pterm}, which is a standalone terminal emulator for\r
+Unix systems; see \k{faq-unix}.)\r
+\r
+\H{faq-embedding} Embedding PuTTY in other programs\r
+\r
+\S{faq-dll}{Question} Is the SSH or Telnet code available as a DLL?\r
+\r
+No, it isn't. It would take a reasonable amount of rewriting for\r
+this to be possible, and since the PuTTY project itself doesn't\r
+believe in DLLs (they make installation more error-prone) none of us\r
+has taken the time to do it.\r
+\r
+Most of the code cleanup work would be a good thing to happen in\r
+general, so if anyone feels like helping, we wouldn't say no.\r
+\r
+See also\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/dll-frontend.html}{the wishlist entry}.\r
+\r
+\S{faq-vb}{Question} Is the SSH or Telnet code available as a Visual\r
+Basic component?\r
+\r
+No, it isn't. None of the PuTTY team uses Visual Basic, and none of\r
+us has any particular need to make SSH connections from a Visual\r
+Basic application. In addition, all the preliminary work to turn it\r
+into a DLL would be necessary first; and furthermore, we don't even\r
+know how to write VB components.\r
+\r
+If someone offers to do some of this work for us, we might consider\r
+it, but unless that happens I can't see VB integration being\r
+anywhere other than the very bottom of our priority list.\r
+\r
+\S{faq-ipc}{Question} How can I use PuTTY to make an SSH connection\r
+from within another program?\r
+\r
+Probably your best bet is to use Plink, the command-line connection\r
+tool. If you can start Plink as a second Windows process, and\r
+arrange for your primary process to be able to send data to the\r
+Plink process, and receive data from it, through pipes, then you\r
+should be able to make SSH connections from your program.\r
+\r
+This is what CVS for Windows does, for example.\r
+\r
+\H{faq-details} Details of PuTTY's operation\r
+\r
+\S{faq-term}{Question} What \i{terminal type} does PuTTY use?\r
+\r
+For most purposes, PuTTY can be considered to be an \cw{xterm}\r
+terminal.\r
+\r
+PuTTY also supports some terminal \i{control sequences} not supported by\r
+the real \cw{xterm}: notably the Linux console sequences that\r
+reconfigure the colour palette, and the title bar control sequences\r
+used by \i\cw{DECterm} (which are different from the \cw{xterm} ones;\r
+PuTTY supports both).\r
+\r
+By default, PuTTY announces its terminal type to the server as\r
+\c{xterm}. If you have a problem with this, you can reconfigure it\r
+to say something else; \c{vt220} might help if you have trouble.\r
+\r
+\S{faq-settings}{Question} Where does PuTTY store its data?\r
+\r
+On Windows, PuTTY stores most of its data (saved sessions, SSH host\r
+keys) in the \i{Registry}. The precise location is\r
+\r
+\c HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\r
+\r
+and within that area, saved sessions are stored under \c{Sessions}\r
+while host keys are stored under \c{SshHostKeys}.\r
+\r
+PuTTY also requires a random number seed file, to improve the\r
+unpredictability of randomly chosen data needed as part of the SSH\r
+cryptography. This is stored by default in a file called \i\c{PUTTY.RND};\r
+this is stored by default in the \q{Application Data} directory,\r
+or failing that, one of a number of fallback locations. If you\r
+want to change the location of the random number seed file, you can\r
+put your chosen pathname in the Registry, at\r
+\r
+\c HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\RandSeedFile\r
+\r
+You can ask PuTTY to delete all this data; see \k{faq-cleanup}.\r
+\r
+On Unix, PuTTY stores all of this data in a directory \cw{~/.putty}.\r
+\r
+\H{faq-howto} HOWTO questions\r
+\r
+\S{faq-login}{Question} What login name / password should I use?\r
+\r
+This is not a question you should be asking \e{us}.\r
+\r
+PuTTY is a communications tool, for making connections to other\r
+computers. We maintain the tool; we \e{don't} administer any computers\r
+that you're likely to be able to use, in the same way that the people\r
+who make web browsers aren't responsible for most of the content you can\r
+view in them. \#{FIXME: less technical analogy?} We cannot help with\r
+questions of this sort.\r
+\r
+If you know the name of the computer you want to connect to, but don't\r
+know what login name or password to use, you should talk to whoever\r
+administers that computer. If you don't know who that is, see the next\r
+question for some possible ways to find out.\r
+\r
+\# FIXME: some people ask us to provide them with a login name\r
+apparently as random members of the public rather than in the\r
+belief that we run a server belonging to an organisation they already\r
+have some relationship with. Not sure what to say to such people.\r
+\r
+\S{faq-commands}{Question} \I{commands on the server}What commands\r
+can I type into my PuTTY terminal window?\r
+\r
+Again, this is not a question you should be asking \e{us}. You need\r
+to read the manuals, or ask the administrator, of \e{the computer\r
+you have connected to}.\r
+\r
+PuTTY does not process the commands you type into it. It's only a\r
+communications tool. It makes a connection to another computer; it\r
+passes the commands you type to that other computer; and it passes\r
+the other computer's responses back to you. Therefore, the precise\r
+range of commands you can use will not depend on PuTTY, but on what\r
+kind of computer you have connected to and what software is running\r
+on it. The PuTTY team cannot help you with that.\r
+\r
+(Think of PuTTY as being a bit like a telephone. If you phone\r
+somebody up and you don't know what language to speak to make them\r
+understand you, it isn't \e{the telephone company}'s job to find\r
+that out for you. We just provide the means for you to get in touch;\r
+making yourself understood is somebody else's problem.)\r
+\r
+If you are unsure of where to start looking for the administrator of\r
+your server, a good place to start might be to remember how you\r
+found out the host name in the PuTTY configuration. If you were\r
+given that host name by e-mail, for example, you could try asking\r
+the person who sent you that e-mail. If your company's IT department\r
+provided you with ready-made PuTTY saved sessions, then that IT\r
+department can probably also tell you something about what commands\r
+you can type during those sessions. But the PuTTY maintainer team\r
+does not administer any server you are likely to be connecting to,\r
+and cannot help you with questions of this type.\r
+\r
+\S{faq-startmax}{Question} How can I make PuTTY start up \i{maximise}d?\r
+\r
+Create a Windows shortcut to start PuTTY from, and set it as \q{Run\r
+Maximized}.\r
+\r
+\S{faq-startsess}{Question} How can I create a \i{Windows shortcut} to\r
+start a particular saved session directly?\r
+\r
+To run a PuTTY session saved under the name \q{\cw{mysession}},\r
+create a Windows shortcut that invokes PuTTY with a command line\r
+like\r
+\r
+\c \path\name\to\putty.exe -load "mysession"\r
+\r
+(Note: prior to 0.53, the syntax was \c{@session}. This is now\r
+deprecated and may be removed at some point.)\r
+\r
+\S{faq-startssh}{Question} How can I start an SSH session straight\r
+from the command line?\r
+\r
+Use the command line \c{putty -ssh host.name}. Alternatively, create\r
+a saved session that specifies the SSH protocol, and start the saved\r
+session as shown in \k{faq-startsess}.\r
+\r
+\S{faq-cutpaste}{Question} How do I \i{copy and paste} between PuTTY and\r
+other Windows applications?\r
+\r
+Copy and paste works similarly to the X Window System. You use the\r
+left mouse button to select text in the PuTTY window. The act of\r
+selection \e{automatically} copies the text to the clipboard: there\r
+is no need to press Ctrl-Ins or Ctrl-C or anything else. In fact,\r
+pressing Ctrl-C will send a Ctrl-C character to the other end of\r
+your connection (just like it does the rest of the time), which may\r
+have unpleasant effects. The \e{only} thing you need to do, to copy\r
+text to the clipboard, is to select it.\r
+\r
+To paste the clipboard contents into a PuTTY window, by default you\r
+click the right mouse button. If you have a three-button mouse and\r
+are used to X applications, you can configure pasting to be done by\r
+the middle button instead, but this is not the default because most\r
+Windows users don't have a middle button at all.\r
+\r
+You can also paste by pressing Shift-Ins.\r
+\r
+\S{faq-options}{Question} How do I use all PuTTY's features (public\r
+keys, proxying, cipher selection, etc.) in PSCP, PSFTP and Plink?\r
+\r
+Most major features (e.g., public keys, port forwarding) are available\r
+through command line options. See the documentation.\r
+\r
+Not all features are accessible from the command line yet, although\r
+we'd like to fix this. In the meantime, you can use most of\r
+PuTTY's features if you create a PuTTY saved session, and then use\r
+the name of the saved session on the command line in place of a\r
+hostname. This works for PSCP, PSFTP and Plink (but don't expect\r
+port forwarding in the file transfer applications!).\r
+\r
+\S{faq-pscp}{Question} How do I use PSCP.EXE? When I double-click it\r
+gives me a command prompt window which then closes instantly.\r
+\r
+PSCP is a command-line application, not a GUI application. If you\r
+run it without arguments, it will simply print a help message and\r
+terminate.\r
+\r
+To use PSCP properly, run it from a Command Prompt window. See\r
+\k{pscp} in the documentation for more details.\r
+\r
+\S{faq-pscp-spaces}{Question} \I{spaces in filenames}How do I use\r
+PSCP to copy a file whose name has spaces in?\r
+\r
+If PSCP is using the traditional SCP protocol, this is confusing. If\r
+you're specifying a file at the local end, you just use one set of\r
+quotes as you would normally do:\r
+\r
+\c pscp "local filename with spaces" user@host:\r
+\c pscp user@host:myfile "local filename with spaces"\r
+\r
+But if the filename you're specifying is on the \e{remote} side, you\r
+have to use backslashes and two sets of quotes:\r
+\r
+\c pscp user@host:"\"remote filename with spaces\"" local_filename\r
+\c pscp local_filename user@host:"\"remote filename with spaces\""\r
+\r
+Worse still, in a remote-to-local copy you have to specify the local\r
+file name explicitly, otherwise PSCP will complain that they don't\r
+match (unless you specified the \c{-unsafe} option). The following\r
+command will give an error message:\r
+\r
+\c c:\>pscp user@host:"\"oo er\"" .\r
+\c warning: remote host tried to write to a file called 'oo er'\r
+\c          when we requested a file called '"oo er"'.\r
+\r
+Instead, you need to specify the local file name in full:\r
+\r
+\c c:\>pscp user@host:"\"oo er\"" "oo er"\r
+\r
+If PSCP is using the newer SFTP protocol, none of this is a problem,\r
+and all filenames with spaces in are specified using a single pair\r
+of quotes in the obvious way:\r
+\r
+\c pscp "local file" user@host:\r
+\c pscp user@host:"remote file" .\r
+\r
+\H{faq-trouble} Troubleshooting\r
+\r
+\S{faq-incorrect-mac}{Question} Why do I see \q{Incorrect MAC\r
+received on packet}?\r
+\r
+One possible cause of this that used to be common is a bug in old\r
+SSH-2 servers distributed by \cw{ssh.com}. (This is not the only\r
+possible cause; see \k{errors-crc} in the documentation.)\r
+Version 2.3.0 and below of their SSH-2 server\r
+constructs Message Authentication Codes in the wrong way, and\r
+expects the client to construct them in the same wrong way. PuTTY\r
+constructs the MACs correctly by default, and hence these old\r
+servers will fail to work with it.\r
+\r
+If you are using PuTTY version 0.52 or better, this should work\r
+automatically: PuTTY should detect the buggy servers from their\r
+version number announcement, and automatically start to construct\r
+its MACs in the same incorrect manner as they do, so it will be able\r
+to work with them.\r
+\r
+If you are using PuTTY version 0.51 or below, you can enable the\r
+workaround by going to the SSH panel and ticking the box labelled\r
+\q{Imitate SSH2 MAC bug}. It's possible that you might have to do\r
+this with 0.52 as well, if a buggy server exists that PuTTY doesn't\r
+know about.\r
+\r
+In this context MAC stands for \ii{Message Authentication Code}. It's a\r
+cryptographic term, and it has nothing at all to do with Ethernet\r
+MAC (Media Access Control) addresses.\r
+\r
+\S{faq-pscp-protocol}{Question} Why do I see \q{Fatal: Protocol\r
+error: Expected control record} in PSCP?\r
+\r
+This happens because PSCP was expecting to see data from the server\r
+that was part of the PSCP protocol exchange, and instead it saw data\r
+that it couldn't make any sense of at all.\r
+\r
+This almost always happens because the \i{startup scripts} in your\r
+account on the server machine are generating output. This is\r
+impossible for PSCP, or any other SCP client, to work around. You\r
+should never use startup files (\c{.bashrc}, \c{.cshrc} and so on)\r
+which generate output in non-interactive sessions.\r
+\r
+This is not actually a PuTTY problem. If PSCP fails in this way,\r
+then all other SCP clients are likely to fail in exactly the same\r
+way. The problem is at the server end.\r
+\r
+\S{faq-colours}{Question} I clicked on a colour in the \ii{Colours}\r
+panel, and the colour didn't change in my terminal.\r
+\r
+That isn't how you're supposed to use the Colours panel.\r
+\r
+During the course of a session, PuTTY potentially uses \e{all} the\r
+colours listed in the Colours panel. It's not a question of using\r
+only one of them and you choosing which one; PuTTY will use them\r
+\e{all}. The purpose of the Colours panel is to let you adjust the\r
+appearance of all the colours. So to change the colour of the\r
+cursor, for example, you would select \q{Cursor Colour}, press the\r
+\q{Modify} button, and select a new colour from the dialog box that\r
+appeared. Similarly, if you want your session to appear in green,\r
+you should select \q{Default Foreground} and press \q{Modify}.\r
+Clicking on \q{ANSI Green} won't turn your session green; it will\r
+only allow you to adjust the \e{shade} of green used when PuTTY is\r
+instructed by the server to display green text.\r
+\r
+\S{faq-winsock2}{Question} Plink on \i{Windows 95} says it can't find\r
+\i\cw{WS2_32.DLL}.\r
+\r
+Plink requires the extended Windows network library, WinSock version\r
+2. This is installed as standard on Windows 98 and above, and on\r
+Windows NT, and even on later versions of Windows 95; but early\r
+Win95 installations don't have it.\r
+\r
+In order to use Plink on these systems, you will need to download\r
+the\r
+\W{http://www.microsoft.com/windows95/downloads/contents/wuadmintools/s_wunetworkingtools/w95sockets2/}{WinSock 2 upgrade}:\r
+\r
+\c http://www.microsoft.com/windows95/downloads/contents/\r
+\c   wuadmintools/s_wunetworkingtools/w95sockets2/\r
+\r
+\S{faq-outofmem}{Question} After trying to establish an SSH-2\r
+connection, PuTTY says \q{\ii{Out of memory}} and dies.\r
+\r
+If this happens just while the connection is starting up, this often\r
+indicates that for some reason the client and server have failed to\r
+establish a session encryption key. Somehow, they have performed\r
+calculations that should have given each of them the same key, but\r
+have ended up with different keys; so data encrypted by one and\r
+decrypted by the other looks like random garbage.\r
+\r
+This causes an \q{out of memory} error because the first encrypted\r
+data PuTTY expects to see is the length of an SSH message. Normally\r
+this will be something well under 100 bytes. If the decryption has\r
+failed, PuTTY will see a completely random length in the region of\r
+two \e{gigabytes}, and will try to allocate enough memory to store\r
+this non-existent message. This will immediately lead to it thinking\r
+it doesn't have enough memory, and panicking.\r
+\r
+If this happens to you, it is quite likely to still be a PuTTY bug\r
+and you should report it (although it might be a bug in your SSH\r
+server instead); but it doesn't necessarily mean you've actually run\r
+out of memory.\r
+\r
+\S{faq-outofmem2}{Question} When attempting a file transfer, either\r
+PSCP or PSFTP says \q{\ii{Out of memory}} and dies.\r
+\r
+This is almost always caused by your \i{login scripts} on the server\r
+generating output. PSCP or PSFTP will receive that output when they\r
+were expecting to see the start of a file transfer protocol, and\r
+they will attempt to interpret the output as file-transfer protocol.\r
+This will usually lead to an \q{out of memory} error for much the\r
+same reasons as given in \k{faq-outofmem}.\r
+\r
+This is a setup problem in your account on your server, \e{not} a\r
+PSCP/PSFTP bug. Your login scripts should \e{never} generate output\r
+during non-interactive sessions; secure file transfer is not the\r
+only form of remote access that will break if they do.\r
+\r
+On Unix, a simple fix is to ensure that all the parts of your login\r
+script that might generate output are in \c{.profile} (if you use a\r
+Bourne shell derivative) or \c{.login} (if you use a C shell).\r
+Putting them in more general files such as \c{.bashrc} or \c{.cshrc}\r
+is liable to lead to problems.\r
+\r
+\S{faq-psftp-slow}{Question} PSFTP transfers files much slower than PSCP.\r
+\r
+The throughput of PSFTP 0.54 should be much better than 0.53b and\r
+prior; we've added code to the SFTP backend to queue several blocks\r
+of data rather than waiting for an acknowledgement for each. (The\r
+SCP backend did not suffer from this performance issue because SCP\r
+is a much simpler protocol.)\r
+\r
+\S{faq-bce}{Question} When I run full-colour applications, I see\r
+areas of black space where colour ought to be, or vice versa.\r
+\r
+You almost certainly need to change the \q{Use \i{background colour} to\r
+erase screen} setting in the Terminal panel. If there is too much\r
+black space (the commoner situation), you should enable it, while if\r
+there is too much colour, you should disable it. (See \k{config-erase}.)\r
+\r
+In old versions of PuTTY, this was disabled by default, and would not\r
+take effect until you reset the terminal (see \k{faq-resetterm}).\r
+Since 0.54, it is enabled by default, and changes take effect\r
+immediately.\r
+\r
+\S{faq-resetterm}{Question} When I change some terminal settings,\r
+nothing happens.\r
+\r
+Some of the terminal options (notably \ii{Auto Wrap} and\r
+background-colour screen erase) actually represent the \e{default}\r
+setting, rather than the currently active setting. The server can\r
+send sequences that modify these options in mid-session, but when\r
+the terminal is reset (by server action, or by you choosing \q{Reset\r
+Terminal} from the System menu) the defaults are restored.\r
+\r
+In versions 0.53b and prior, if you change one of these options in\r
+the middle of a session, you will find that the change does not\r
+immediately take effect. It will only take effect once you reset\r
+the terminal.\r
+\r
+In version 0.54, the behaviour has changed - changes to these\r
+settings take effect immediately.\r
+\r
+\S{faq-idleout}{Question} My PuTTY sessions unexpectedly close after\r
+they are \I{idle connections}idle for a while.\r
+\r
+Some types of \i{firewall}, and almost any router doing Network Address\r
+Translation (\i{NAT}, also known as IP masquerading), will forget about\r
+a connection through them if the connection does nothing for too\r
+long. This will cause the connection to be rudely cut off when\r
+contact is resumed.\r
+\r
+You can try to combat this by telling PuTTY to send \e{keepalives}:\r
+packets of data which have no effect on the actual session, but\r
+which reassure the router or firewall that the network connection is\r
+still active and worth remembering about.\r
+\r
+Keepalives don't solve everything, unfortunately; although they\r
+cause greater robustness against this sort of router, they can also\r
+cause a \e{loss} of robustness against network dropouts. See\r
+\k{config-keepalive} in the documentation for more discussion of\r
+this.\r
+\r
+\S{faq-timeout}{Question} PuTTY's network connections time out too\r
+quickly when \I{breaks in connectivity}network connectivity is\r
+temporarily lost.\r
+\r
+This is a Windows problem, not a PuTTY problem. The timeout value\r
+can't be set on per application or per session basis. To increase\r
+the TCP timeout globally, you need to tinker with the Registry.\r
+\r
+On Windows 95, 98 or ME, the registry key you need to create or\r
+change is\r
+\r
+\c HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\\r
+\c   MSTCP\MaxDataRetries\r
+\r
+(it must be of type DWORD in Win95, or String in Win98/ME).\r
+(See MS Knowledge Base article\r
+\W{http://support.microsoft.com/default.aspx?scid=kb;en-us;158474}{158474}\r
+for more information.)\r
+\r
+On Windows NT, 2000, or XP, the registry key to create or change is\r
+\r
+\c HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\\r
+\c   Parameters\TcpMaxDataRetransmissions\r
+\r
+and it must be of type DWORD.\r
+(See MS Knowledge Base articles\r
+\W{http://support.microsoft.com/default.aspx?scid=kb;en-us;120642}{120642}\r
+and\r
+\W{http://support.microsoft.com/default.aspx?scid=kb;en-us;314053}{314053}\r
+for more information.)\r
+\r
+Set the key's value to something like 10. This will cause Windows to\r
+try harder to keep connections alive instead of abandoning them.\r
+\r
+\S{faq-puttyputty}{Question} When I \cw{cat} a binary file, I get\r
+\q{PuTTYPuTTYPuTTY} on my command line.\r
+\r
+Don't do that, then.\r
+\r
+This is designed behaviour; when PuTTY receives the character\r
+Control-E from the remote server, it interprets it as a request to\r
+identify itself, and so it sends back the string \q{\cw{PuTTY}} as\r
+if that string had been entered at the keyboard. Control-E should\r
+only be sent by programs that are prepared to deal with the\r
+response. Writing a binary file to your terminal is likely to output\r
+many Control-E characters, and cause this behaviour. Don't do it.\r
+It's a bad plan.\r
+\r
+To mitigate the effects, you could configure the answerback string\r
+to be empty (see \k{config-answerback}); but writing binary files to\r
+your terminal is likely to cause various other unpleasant behaviour,\r
+so this is only a small remedy.\r
+\r
+\S{faq-wintitle}{Question} When I \cw{cat} a binary file, my \i{window\r
+title} changes to a nonsense string.\r
+\r
+Don't do that, then.\r
+\r
+It is designed behaviour that PuTTY should have the ability to\r
+adjust the window title on instructions from the server. Normally\r
+the control sequence that does this should only be sent\r
+deliberately, by programs that know what they are doing and intend\r
+to put meaningful text in the window title. Writing a binary file to\r
+your terminal runs the risk of sending the same control sequence by\r
+accident, and cause unexpected changes in the window title. Don't do\r
+it.\r
+\r
+\S{faq-password-fails}{Question} My \i{keyboard} stops working once\r
+PuTTY displays the \i{password prompt}.\r
+\r
+No, it doesn't. PuTTY just doesn't display the password you type, so\r
+that someone looking at your screen can't see what it is.\r
+\r
+Unlike the Windows login prompts, PuTTY doesn't display the password\r
+as a row of asterisks either. This is so that someone looking at\r
+your screen can't even tell how \e{long} your password is, which\r
+might be valuable information.\r
+\r
+\S{faq-keyboard}{Question} One or more \I{keyboard}\i{function keys}\r
+don't do what I expected in a server-side application.\r
+\r
+If you've already tried all the relevant options in the PuTTY\r
+Keyboard panel, you may need to mail the PuTTY maintainers and ask.\r
+\r
+It is \e{not} usually helpful just to tell us which application,\r
+which server operating system, and which key isn't working; in order\r
+to replicate the problem we would need to have a copy of every\r
+operating system, and every application, that anyone has ever\r
+complained about.\r
+\r
+PuTTY responds to function key presses by sending a sequence of\r
+control characters to the server. If a function key isn't doing what\r
+you expect, it's likely that the character sequence your application\r
+is expecting to receive is not the same as the one PuTTY is sending.\r
+Therefore what we really need to know is \e{what} sequence the\r
+application is expecting.\r
+\r
+The simplest way to investigate this is to find some other terminal\r
+environment, in which that function key \e{does} work; and then\r
+investigate what sequence the function key is sending in that\r
+situation. One reasonably easy way to do this on a \i{Unix} system is to\r
+type the command \i\c{cat}, and then press the function key. This is\r
+likely to produce output of the form \c{^[[11~}. You can also do\r
+this in PuTTY, to find out what sequence the function key is\r
+producing in that. Then you can mail the PuTTY maintainers and tell\r
+us \q{I wanted the F1 key to send \c{^[[11~}, but instead it's\r
+sending \c{^[OP}, can this be done?}, or something similar.\r
+\r
+You should still read the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/feedback.html}{Feedback\r
+page} on the PuTTY website (also provided as \k{feedback} in the\r
+manual), and follow the guidelines contained in that.\r
+\r
+\S{faq-openssh-bad-openssl}{Question} Since my SSH server was upgraded\r
+to \i{OpenSSH} 3.1p1/3.4p1, I can no longer connect with PuTTY.\r
+\r
+There is a known problem when OpenSSH has been built against an\r
+incorrect version of OpenSSL; the quick workaround is to configure\r
+PuTTY to use SSH protocol 2 and the Blowfish cipher.\r
+\r
+For more details and OpenSSH patches, see\r
+\W{http://bugzilla.mindrot.org/show_bug.cgi?id=138}{bug 138} in the\r
+OpenSSH BTS.\r
+\r
+This is not a PuTTY-specific problem; if you try to connect with\r
+another client you'll likely have similar problems. (Although PuTTY's\r
+default cipher differs from many other clients.)\r
+\r
+\e{OpenSSH 3.1p1:} configurations known to be broken (and symptoms):\r
+\r
+\b SSH-2 with AES cipher (PuTTY says \q{Assertion failed! Expression:\r
+(len & 15) == 0} in \cw{sshaes.c}, or \q{Out of memory}, or crashes)\r
+\r
+\b SSH-2 with 3DES (PuTTY says \q{Incorrect MAC received on packet})\r
+\r
+\b SSH-1 with Blowfish (PuTTY says \q{Incorrect CRC received on\r
+packet})\r
+\r
+\b SSH-1 with 3DES\r
+\r
+\e{OpenSSH 3.4p1:} as of 3.4p1, only the problem with SSH-1 and\r
+Blowfish remains. Rebuild your server, apply the patch linked to from\r
+bug 138 above, or use another cipher (e.g., 3DES) instead.\r
+\r
+\e{Other versions:} we occasionally get reports of the same symptom\r
+and workarounds with older versions of OpenSSH, although it's not\r
+clear the underlying cause is the same.\r
+\r
+\S{faq-ssh2key-ssh1conn}{Question} Why do I see \q{Couldn't load\r
+private key from ...}? Why can PuTTYgen load my key but not PuTTY?\r
+\r
+It's likely that you've generated an SSH protocol 2 key with PuTTYgen,\r
+but you're trying to use it in an SSH-1 connection. SSH-1 and SSH-2 keys\r
+have different formats, and (at least in 0.52) PuTTY's reporting of a\r
+key in the wrong format isn't optimal.\r
+\r
+To connect using SSH-2 to a server that supports both versions, you\r
+need to change the configuration from the default (see \k{faq-ssh2}).\r
+\r
+\S{faq-rh8-utf8}{Question} When I'm connected to a \i{Red Hat Linux} 8.0\r
+system, some characters don't display properly.\r
+\r
+A common complaint is that hyphens in man pages show up as a-acute.\r
+\r
+With release 8.0, Red Hat appear to have made \i{UTF-8} the default\r
+character set. There appears to be no way for terminal emulators such\r
+as PuTTY to know this (as far as we know, the appropriate escape\r
+sequence to switch into UTF-8 mode isn't sent).\r
+\r
+A fix is to configure sessions to RH8 systems to use UTF-8\r
+translation - see \k{config-charset} in the documentation. (Note that\r
+if you use \q{Change Settings}, changes may not take place immediately\r
+- see \k{faq-resetterm}.)\r
+\r
+If you really want to change the character set used by the server, the\r
+right place is \c{/etc/sysconfig/i18n}, but this shouldn't be\r
+necessary.\r
+\r
+\S{faq-screen}{Question} Since I upgraded to PuTTY 0.54, the\r
+scrollback has stopped working when I run \c{screen}.\r
+\r
+PuTTY's terminal emulator has always had the policy that when the\r
+\q{\i{alternate screen}} is in use, nothing is added to the scrollback.\r
+This is because the usual sorts of programs which use the alternate\r
+screen are things like text editors, which tend to scroll back and\r
+forth in the same document a lot; so (a) they would fill up the\r
+scrollback with a large amount of unhelpfully disordered text, and\r
+(b) they contain their \e{own} method for the user to scroll back to\r
+the bit they were interested in. We have generally found this policy\r
+to do the Right Thing in almost all situations.\r
+\r
+Unfortunately, \c{screen} is one exception: it uses the alternate\r
+screen, but it's still usually helpful to have PuTTY's scrollback\r
+continue working. The simplest solution is to go to the Features\r
+control panel and tick \q{Disable switching to alternate terminal\r
+screen}. (See \k{config-features-altscreen} for more details.)\r
+Alternatively, you can tell \c{screen} itself not to use the\r
+alternate screen: the\r
+\W{http://www4.informatik.uni-erlangen.de/~jnweiger/screen-faq.html}{\c{screen}\r
+FAQ} suggests adding the line \cq{termcapinfo xterm ti@:te@} to your\r
+\cw{.screenrc} file.\r
+\r
+The reason why this only started to be a problem in 0.54 is because\r
+\c{screen} typically uses an unusual control sequence to switch to\r
+the alternate screen, and previous versions of PuTTY did not support\r
+this sequence.\r
+\r
+\S{faq-alternate-localhost}{Question} Since I upgraded \i{Windows XP}\r
+to Service Pack 2, I can't use addresses like \cw{127.0.0.2}.\r
+\r
+Some people who ask PuTTY to listen on \i{localhost} addresses other\r
+than \cw{127.0.0.1} to forward services such as \i{SMB} and \i{Windows\r
+Terminal Services} have found that doing so no longer works since\r
+they upgraded to WinXP SP2.\r
+\r
+This is apparently an issue with SP2 that is acknowledged by Microsoft\r
+in MS Knowledge Base article\r
+\W{http://support.microsoft.com/default.aspx?scid=kb;en-us;884020}{884020}.\r
+The article links to a fix you can download.\r
+\r
+(\e{However}, we've been told that SP2 \e{also} fixes the bug that\r
+means you need to use non-\cw{127.0.0.1} addresses to forward\r
+Terminal Services in the first place.)\r
+\r
+\S{faq-missing-slash}{Question} PSFTP commands seem to be missing a\r
+directory separator (slash). \r
+\r
+Some people have reported the following incorrect behaviour with\r
+PSFTP:\r
+\r
+\c psftp> pwd\r
+\e        iii\r
+\c Remote directory is /dir1/dir2\r
+\c psftp> get filename.ext\r
+\e        iiiiiiiiiiiiiiii\r
+\c /dir1/dir2filename.ext: no such file or directory\r
+\r
+This is not a bug in PSFTP. There is a known bug in some versions of\r
+portable \i{OpenSSH}\r
+(\W{http://bugzilla.mindrot.org/show_bug.cgi?id=697}{bug 697}) that\r
+causes these symptoms; it appears to have been introduced around\r
+3.7.x. It manifests only on certain platforms (AIX is what has been\r
+reported to us).\r
+\r
+There is a patch for OpenSSH attached to that bug; it's also fixed in\r
+recent versions of portable OpenSSH (from around 3.8).\r
+\r
+\S{faq-connaborted}{Question} Do you want to hear about \q{Software\r
+caused connection abort}?\r
+\r
+In the documentation for PuTTY 0.53 and 0.53b, we mentioned that we'd\r
+like to hear about any occurrences of this error.  Since the release\r
+of PuTTY 0.54, however, we've been convinced that this error doesn't\r
+indicate that PuTTY's doing anything wrong, and we don't need to hear\r
+about further occurrences.  See \k{errors-connaborted} for our current\r
+documentation of this error.\r
+\r
+\S{faq-rekey}{Question} My SSH-2 session \I{locking up, SSH-2\r
+sessions}locks up for a few seconds every so often.\r
+\r
+Recent versions of PuTTY automatically initiate \i{repeat key\r
+exchange} once per hour, to improve session security. If your client\r
+or server machine is slow, you may experience this as a delay of\r
+anything up to thirty seconds or so.\r
+\r
+These \I{delays, in SSH-2 sessions}delays are inconvenient, but they\r
+are there for your protection. If they really cause you a problem,\r
+you can choose to turn off periodic rekeying using the \q{Kex}\r
+configuration panel (see \k{config-ssh-kex}), but be aware that you\r
+will be sacrificing security for this. (Falling back to SSH-1 would\r
+also remove the delays, but would lose a \e{lot} more security\r
+still. We do not recommend it.)\r
+\r
+\S{faq-xpwontrun}{Question} PuTTY fails to start up.  Windows claims that\r
+\q{the application configuration is incorrect}.\r
+\r
+This is caused by a bug in certain versions of \i{Windows XP} which\r
+is triggered by PuTTY 0.58. This was fixed in 0.59. The\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/xp-wont-run}{\q{xp-wont-run}}\r
+entry in PuTTY's wishlist has more details.\r
+\r
+\H{faq-secure} Security questions\r
+\r
+\S{faq-publicpc}{Question} Is it safe for me to download PuTTY and\r
+use it on a public PC?\r
+\r
+It depends on whether you trust that PC. If you don't trust the\r
+public PC, don't use PuTTY on it, and don't use any other software\r
+you plan to type passwords into either. It might be watching your\r
+keystrokes, or it might tamper with the PuTTY binary you download.\r
+There is \e{no} program safe enough that you can run it on an\r
+actively malicious PC and get away with typing passwords into it.\r
+\r
+If you do trust the PC, then it's probably OK to use PuTTY on it\r
+(but if you don't trust the network, then the PuTTY download might\r
+be tampered with, so it would be better to carry PuTTY with you on a\r
+floppy).\r
+\r
+\S{faq-cleanup}{Question} What does PuTTY leave on a system? How can\r
+I \i{clean up} after it?\r
+\r
+PuTTY will leave some Registry entries, and a random seed file, on\r
+the PC (see \k{faq-settings}). If you are using PuTTY on a public\r
+PC, or somebody else's PC, you might want to clean these up when you\r
+leave. You can do that automatically, by running the command\r
+\c{putty -cleanup}. (Note that this only removes settings for\r
+the currently logged-in user on \i{multi-user systems}.)\r
+\r
+If PuTTY was installed from the installer package, it will also\r
+appear in \q{Add/Remove Programs}. Older versions of the uninstaller\r
+do not remove the above-mentioned registry entries and file.\r
+\r
+\S{faq-dsa}{Question} How come PuTTY now supports \i{DSA}, when the\r
+website used to say how insecure it was?\r
+\r
+DSA has a major weakness \e{if badly implemented}: it relies on a\r
+random number generator to far too great an extent. If the random\r
+number generator produces a number an attacker can predict, the DSA\r
+private key is exposed - meaning that the attacker can log in as you\r
+on all systems that accept that key.\r
+\r
+The PuTTY policy changed because the developers were informed of\r
+ways to implement DSA which do not suffer nearly as badly from this\r
+weakness, and indeed which don't need to rely on random numbers at\r
+all. For this reason we now believe PuTTY's DSA implementation is\r
+probably OK. However, if you have the choice, we still recommend you\r
+use RSA instead.\r
+\r
+\S{faq-virtuallock}{Question} Couldn't Pageant use\r
+\cw{VirtualLock()} to stop private keys being written to disk?\r
+\r
+Unfortunately not. The \cw{VirtualLock()} function in the Windows\r
+API doesn't do a proper job: it may prevent small pieces of a\r
+process's memory from being paged to disk while the process is\r
+running, but it doesn't stop the process's memory as a whole from\r
+being swapped completely out to disk when the process is long-term\r
+inactive. And Pageant spends most of its time inactive.\r
+\r
+\H{faq-admin} Administrative questions\r
+\r
+\S{faq-domain}{Question} Would you like me to register you a nicer\r
+domain name?\r
+\r
+No, thank you. Even if you can find one (most of them seem to have\r
+been registered already, by people who didn't ask whether we\r
+actually wanted it before they applied), we're happy with the PuTTY\r
+web site being exactly where it is. It's not hard to find (just type\r
+\q{putty} into \W{http://www.google.com/}{google.com} and we're the\r
+first link returned), and we don't believe the administrative hassle\r
+of moving the site would be worth the benefit.\r
+\r
+In addition, if we \e{did} want a custom domain name, we would want\r
+to run it ourselves, so we knew for certain that it would continue\r
+to point where we wanted it, and wouldn't suddenly change or do\r
+strange things. Having it registered for us by a third party who we\r
+don't even know is not the best way to achieve this.\r
+\r
+\S{faq-webhosting}{Question} Would you like free web hosting for the\r
+PuTTY web site?\r
+\r
+We already have some, thanks.\r
+\r
+\S{faq-link}{Question} Would you link to my web site from the PuTTY\r
+web site?\r
+\r
+Only if the content of your web page is of definite direct interest\r
+to PuTTY users. If your content is unrelated, or only tangentially\r
+related, to PuTTY, then the link would simply be advertising for\r
+you.\r
+\r
+One very nice effect of the Google ranking mechanism is that by and\r
+large, the most popular web sites get the highest rankings. This\r
+means that when an ordinary person does a search, the top item in\r
+the search is very likely to be a high-quality site or the site they\r
+actually wanted, rather than the site which paid the most money for\r
+its ranking.\r
+\r
+The PuTTY web site is held in high esteem by Google, for precisely\r
+this reason: lots of people have linked to it simply because they\r
+like PuTTY, without us ever having to ask anyone to link to us. We\r
+feel that it would be an abuse of this esteem to use it to boost the\r
+ranking of random advertisers' web sites. If you want your web site\r
+to have a high Google ranking, we'd prefer that you achieve this the\r
+way we did - by being good enough at what you do that people will\r
+link to you simply because they like you.\r
+\r
+In particular, we aren't interested in trading links for money (see\r
+above), and we \e{certainly} aren't interested in trading links for\r
+other links (since we have no advertising on our web site, our\r
+Google ranking is not even directly worth anything to us). If we\r
+don't want to link to you for free, then we probably won't want to\r
+link to you at all.\r
+\r
+If you have software based on PuTTY, or specifically designed to\r
+interoperate with PuTTY, or in some other way of genuine interest to\r
+PuTTY users, then we will probably be happy to add a link to you on\r
+our Links page. And if you're running a particularly valuable mirror\r
+of the PuTTY web site, we might be interested in linking to you from\r
+our Mirrors page.\r
+\r
+\S{faq-sourceforge}{Question} Why don't you move PuTTY to\r
+SourceForge?\r
+\r
+Partly, because we don't want to move the web site location (see\r
+\k{faq-domain}).\r
+\r
+Also, security reasons. PuTTY is a security product, and as such it\r
+is particularly important to guard the code and the web site against\r
+unauthorised modifications which might introduce subtle security\r
+flaws. Therefore, we prefer that the Subversion repository, web site and\r
+FTP site remain where they are, under the direct control of system\r
+administrators we know and trust personally, rather than being run\r
+by a large organisation full of people we've never met and which is\r
+known to have had breakins in the past.\r
+\r
+No offence to SourceForge; I think they do a wonderful job. But\r
+they're not ideal for everyone, and in particular they're not ideal\r
+for us.\r
+\r
+\S{faq-mailinglist1}{Question} Why can't I subscribe to the\r
+putty-bugs mailing list?\r
+\r
+Because you're not a member of the PuTTY core development team. The\r
+putty-bugs mailing list is not a general newsgroup-like discussion\r
+forum; it's a contact address for the core developers, and an\r
+\e{internal} mailing list for us to discuss things among ourselves.\r
+If we opened it up for everybody to subscribe to, it would turn into\r
+something more like a newsgroup and we would be completely\r
+overwhelmed by the volume of traffic. It's hard enough to keep up\r
+with the list as it is.\r
+\r
+\S{faq-mailinglist2}{Question} If putty-bugs isn't a\r
+general-subscription mailing list, what is?\r
+\r
+There isn't one, that we know of.\r
+\r
+If someone else wants to set up a mailing list or other forum for\r
+PuTTY users to help each other with common problems, that would be\r
+fine with us, though the PuTTY team would almost certainly not have the\r
+time to read it.  It's probably better to use one of the established\r
+newsgroups for this purpose (see \k{feedback-other-fora}).\r
+\r
+\S{faq-donations}{Question} How can I donate to PuTTY development?\r
+\r
+Please, \e{please} don't feel you have to. PuTTY is completely free\r
+software, and not shareware. We think it's very important that\r
+\e{everybody} who wants to use PuTTY should be able to, whether they\r
+have any money or not; so the last thing we would want is for a\r
+PuTTY user to feel guilty because they haven't paid us any money. If\r
+you want to keep your money, please do keep it. We wouldn't dream of\r
+asking for any.\r
+\r
+Having said all that, if you still really \e{want} to give us money,\r
+we won't argue :-) The easiest way for us to accept donations is if\r
+you send money to \cw{<anakin@pobox.com>} using PayPal\r
+(\W{http://www.paypal.com/}\cw{www.paypal.com}). If you don't like\r
+PayPal, talk to us; we can probably arrange some alternative means.\r
+\r
+Small donations (tens of dollars or tens of euros) will probably be\r
+spent on beer or curry, which helps motivate our volunteer team to\r
+continue doing this for the world. Larger donations will be spent on\r
+something that actually helps development, if we can find anything\r
+(perhaps new hardware, or a copy of Windows XP), but if we can't\r
+find anything then we'll just distribute the money among the\r
+developers. If you want to be sure your donation is going towards\r
+something worthwhile, ask us first. If you don't like these terms,\r
+feel perfectly free not to donate. We don't mind.\r
+\r
+\S{faq-permission}{Question} Can I have permission to put PuTTY on a\r
+cover disk / distribute it with other software / etc?\r
+\r
+Yes. For most things, you need not bother asking us explicitly for\r
+permission; our licence already grants you permission.\r
+\r
+See \k{feedback-permission} for more details.\r
+\r
+\S{faq-indemnity}{Question} Can you sign an agreement indemnifying\r
+us against security problems in PuTTY?\r
+\r
+No!\r
+\r
+A vendor of physical security products (e.g. locks) might plausibly\r
+be willing to accept financial liability for a product that failed\r
+to perform as advertised and resulted in damage (e.g. valuables\r
+being stolen). The reason they can afford to do this is because they\r
+sell a \e{lot} of units, and only a small proportion of them will\r
+fail; so they can meet their financial liability out of the income\r
+from all the rest of their sales, and still have enough left over to\r
+make a profit. Financial liability is intrinsically linked to\r
+selling your product for money.\r
+\r
+There are two reasons why PuTTY is not analogous to a physical lock\r
+in this context. One is that software products don't exhibit random\r
+variation: \e{if} PuTTY has a security hole (which does happen,\r
+although we do our utmost to prevent it and to respond quickly when\r
+it does), every copy of PuTTY will have the same hole, so it's\r
+likely to affect all the users at the same time. So even if our\r
+users were all paying us to use PuTTY, we wouldn't be able to\r
+\e{simultaneously} pay every affected user compensation in excess of\r
+the amount they had paid us in the first place. It just wouldn't\r
+work.\r
+\r
+The second, much more important, reason is that PuTTY users\r
+\e{don't} pay us. The PuTTY team does not have an income; it's a\r
+volunteer effort composed of people spending their spare time to try\r
+to write useful software. We aren't even a company or any kind of\r
+legally recognised organisation. We're just a bunch of people who\r
+happen to do some stuff in our spare time.\r
+\r
+Therefore, to ask us to assume financial liability is to ask us to\r
+assume a risk of having to pay it out of our own \e{personal}\r
+pockets: out of the same budget from which we buy food and clothes\r
+and pay our rent. That's more than we're willing to give. We're\r
+already giving a lot of our spare \e{time} to developing software\r
+for free; if we had to pay our own \e{money} to do it as well, we'd\r
+start to wonder why we were bothering.\r
+\r
+Free software fundamentally does not work on the basis of financial\r
+guarantees. Your guarantee of the software functioning correctly is\r
+simply that you have the source code and can check it before you use\r
+it. If you want to be sure there aren't any security holes, do a\r
+security audit of the PuTTY code, or hire a security engineer if you\r
+don't have the necessary skills yourself: instead of trying to\r
+ensure you can get compensation in the event of a disaster, try to\r
+ensure there isn't a disaster in the first place.\r
+\r
+If you \e{really} want financial security, see if you can find a\r
+security engineer who will take financial responsibility for the\r
+correctness of their review. (This might be less likely to suffer\r
+from the everything-failing-at-once problem mentioned above, because\r
+such an engineer would probably be reviewing a lot of \e{different}\r
+products which would tend to fail independently.) Failing that, see\r
+if you can persuade an insurance company to insure you against\r
+security incidents, and if the insurer demands it as a condition\r
+then get our code reviewed by a security engineer they're happy\r
+with.\r
+\r
+\S{faq-permission-form}{Question} Can you sign this form granting us\r
+permission to use/distribute PuTTY?\r
+\r
+If your form contains any clause along the lines of \q{the\r
+undersigned represents and warrants}, we're not going to sign it.\r
+This is particularly true if it asks us to warrant that PuTTY is\r
+secure; see \k{faq-indemnity} for more discussion of this. But it\r
+doesn't really matter what we're supposed to be warranting: even if\r
+it's something we already believe is true, such as that we don't\r
+infringe any third-party copyright, we will not sign a document\r
+accepting any legal or financial liability. This is simply because\r
+the PuTTY development project has no income out of which to satisfy\r
+that liability, or pay legal costs, should it become necessary. We\r
+cannot afford to be sued. We are assuring you that \e{we have done\r
+our best}; if that isn't good enough for you, tough.\r
+\r
+The existing PuTTY licence document already gives you permission to\r
+use or distribute PuTTY in pretty much any way which does not\r
+involve pretending you wrote it or suing us if it goes wrong. We\r
+think that really ought to be enough for anybody.\r
+\r
+See also \k{faq-permission-general} for another reason why we don't\r
+want to do this sort of thing.\r
+\r
+\S{faq-permission-future}{Question} Can you write us a formal notice\r
+of permission to use PuTTY?\r
+\r
+We could, in principle, but it isn't clear what use it would be. If\r
+you think there's a serious chance of one of the PuTTY copyright\r
+holders suing you (which we don't!), you would presumably want a\r
+signed notice from \e{all} of them; and we couldn't provide that\r
+even if we wanted to, because many of the copyright holders are\r
+people who contributed some code in the past and with whom we\r
+subsequently lost contact. Therefore the best we would be able to do\r
+\e{even in theory} would be to have the core development team sign\r
+the document, which wouldn't guarantee you that some other copyright\r
+holder might not sue.\r
+\r
+See also \k{faq-permission-general} for another reason why we don't\r
+want to do this sort of thing.\r
+\r
+\S{faq-permission-general}{Question} Can you sign \e{anything} for\r
+us?\r
+\r
+Not unless there's an incredibly good reason.\r
+\r
+We are generally unwilling to set a precedent that involves us\r
+having to enter into individual agreements with PuTTY users. We\r
+estimate that we have literally \e{millions} of users, and we\r
+absolutely would not have time to go round signing specific\r
+agreements with every one of them. So if you want us to sign\r
+something specific for you, you might usefully stop to consider\r
+whether there's anything special that distinguishes you from 999,999\r
+other users, and therefore any reason we should be willing to sign\r
+something for you without it setting such a precedent.\r
+\r
+If your company policy requires you to have an individual agreement\r
+with the supplier of any software you use, then your company policy\r
+is simply not well suited to using popular free software, and we\r
+urge you to consider this as a flaw in your policy.\r
+\r
+\S{faq-permission-assurance}{Question} If you won't sign anything,\r
+can you give us some sort of assurance that you won't make PuTTY\r
+closed-source in future?\r
+\r
+Yes and no.\r
+\r
+If what you want is an assurance that some \e{current version} of\r
+PuTTY which you've already downloaded will remain free, then you\r
+already have that assurance: it's called the PuTTY Licence. It\r
+grants you permission to use, distribute and copy the software to\r
+which it applies; once we've granted that permission (which we\r
+have), we can't just revoke it.\r
+\r
+On the other hand, if you want an assurance that \e{future} versions\r
+of PuTTY won't be closed-source, that's more difficult. We could in\r
+principle sign a document stating that we would never release a\r
+closed-source PuTTY, but that wouldn't assure you that we \e{would}\r
+keep releasing \e{open}-source PuTTYs: we would still have the\r
+option of ceasing to develop PuTTY at all, which would surely be\r
+even worse for you than making it closed-source! (And we almost\r
+certainly wouldn't \e{want} to sign a document guaranteeing that we\r
+would actually continue to do development work on PuTTY; we\r
+certainly wouldn't sign it for free. Documents like that are called\r
+contracts of employment, and are generally not signed except in\r
+return for a sizeable salary.)\r
+\r
+If we \e{were} to stop developing PuTTY, or to decide to make all\r
+future releases closed-source, then you would still be free to copy\r
+the last open release in accordance with the current licence, and in\r
+particular you could start your own fork of the project from that\r
+release. If this happened, I confidently predict that \e{somebody}\r
+would do that, and that some kind of a free PuTTY would continue to\r
+be developed. There's already precedent for that sort of thing\r
+happening in free software. We can't guarantee that somebody\r
+\e{other than you} would do it, of course; you might have to do it\r
+yourself. But we can assure you that there would be nothing\r
+\e{preventing} anyone from continuing free development if we\r
+stopped.\r
+\r
+(Finally, we can also confidently predict that if we made PuTTY\r
+closed-source and someone made an open-source fork, most people\r
+would switch to the latter. Therefore, it would be pretty stupid of\r
+us to try it.)\r
+\r
+\S{faq-export-cert}{Question} Can you provide us with export control\r
+information / FIPS certification for PuTTY?\r
+\r
+Some people have asked us for an Export Control Classification Number\r
+(ECCN) for PuTTY.  We don't know whether we have one, and as a team of\r
+free software developers based in the UK we don't have the time,\r
+money, or effort to deal with US bureaucracy to investigate any\r
+further.  We believe that PuTTY falls under 5D002 on the US Commerce\r
+Control List, but that shouldn't be taken as definitive.  If you need\r
+to know more you should seek professional legal advice.  The same\r
+applies to any other country's legal requirements and restrictions.\r
+\r
+Similarly, some people have asked us for FIPS certification of the\r
+PuTTY tools.  Unless someone else is prepared to do the necessary work\r
+and pay any costs, we can't provide this.\r
+\r
+\H{faq-misc} Miscellaneous questions\r
+\r
+\S{faq-openssh}{Question} Is PuTTY a port of \i{OpenSSH}, or based on\r
+OpenSSH or OpenSSL?\r
+\r
+No, it isn't. PuTTY is almost completely composed of code written\r
+from scratch for PuTTY. The only code we share with OpenSSH is the\r
+detector for SSH-1 CRC compensation attacks, written by CORE SDI\r
+S.A; we share no code at all with OpenSSL.\r
+\r
+\S{faq-sillyputty}{Question} Where can I buy silly putty?\r
+\r
+You're looking at the wrong web site; the only PuTTY we know about\r
+here is the name of a computer program.\r
+\r
+If you want the kind of putty you can buy as an executive toy, the\r
+PuTTY team can personally recommend Thinking Putty, which you can\r
+buy from Crazy Aaron's Putty World, at\r
+\W{http://www.puttyworld.com}\cw{www.puttyworld.com}.\r
+\r
+\S{faq-meaning}{Question} What does \q{PuTTY} mean?\r
+\r
+It's the name of a popular SSH and Telnet client.  Any other meaning\r
+is in the eye of the beholder.  It's been rumoured that \q{PuTTY}\r
+is the antonym of \q{\cw{getty}}, or that it's the stuff that makes your\r
+Windows useful, or that it's a kind of plutonium Teletype.  We\r
+couldn't possibly comment on such allegations.\r
+\r
+\S{faq-pronounce}{Question} How do I pronounce \q{PuTTY}?\r
+\r
+Exactly like the English word \q{putty}, which we pronounce\r
+/\u02C8{'}p\u028C{V}ti/.\r
diff --git a/putty/DOC/FEEDBACK.BUT b/putty/DOC/FEEDBACK.BUT
new file mode 100644 (file)
index 0000000..0f9f494
--- /dev/null
@@ -0,0 +1,423 @@
+\define{versionidfeedback} \versionid $Id: feedback.but 7824 2007-12-20 11:03:45Z simon $\r
+\r
+\A{feedback} \ii{Feedback} and \i{bug reporting}\r
+\r
+This is a guide to providing feedback to the PuTTY development team.\r
+It is provided as both a web page on the PuTTY site, and an appendix\r
+in the PuTTY manual.\r
+\r
+\K{feedback-general} gives some general guidelines for sending any\r
+kind of e-mail to the development team. Following sections give more\r
+specific guidelines for particular types of e-mail, such as bug\r
+reports and feature requests.\r
+\r
+\H{feedback-general} General guidelines\r
+\r
+The PuTTY development team gets a \e{lot} of mail. If you can\r
+possibly solve your own problem by reading the manual, reading the\r
+FAQ, reading the web site, asking a fellow user, perhaps posting to a\r
+newsgroup (see \k{feedback-other-fora}), or some other means, then it\r
+would make our lives much easier.\r
+\r
+We get so much e-mail that we literally do not have time to answer\r
+it all. We regret this, but there's nothing we can do about it. So\r
+if you can \e{possibly} avoid sending mail to the PuTTY team, we\r
+recommend you do so. In particular, support requests\r
+(\k{feedback-support}) are probably better sent to newsgroups, or\r
+passed to a local expert if possible.\r
+\r
+The PuTTY contact email address is a private \i{mailing list} containing\r
+four or five core developers. Don't be put off by it being a mailing\r
+list: if you need to send confidential data as part of a bug report,\r
+you can trust the people on the list to respect that confidence.\r
+Also, the archives aren't publicly available, so you shouldn't be\r
+letting yourself in for any spam by sending us mail.\r
+\r
+Please use a meaningful subject line on your message.  We get a lot of\r
+mail, and it's hard to find the message we're looking for if they all\r
+have subject lines like \q{PuTTY bug}.\r
+\r
+\S{feedback-largefiles} Sending large attachments\r
+\r
+Since the PuTTY contact address is a mailing list, e-mails larger\r
+than 40Kb will be held for inspection by the list administrator, and\r
+will not be allowed through unless they really appear to be worth\r
+their large size.\r
+\r
+If you are considering sending any kind of large data file to the\r
+PuTTY team, it's almost always a bad idea, or at the very least it\r
+would be better to ask us first whether we actually need the file.\r
+Alternatively, you could put the file on a web site and just send us\r
+the URL; that way, we don't have to download it unless we decide we\r
+actually need it, and only one of us needs to download it instead of\r
+it being automatically copied to all the developers.\r
+\r
+Some people like to send mail in MS Word format. Please \e{don't}\r
+send us bug reports, or any other mail, as a Word document. Word\r
+documents are roughly fifty times larger than writing the same\r
+report in plain text. In addition, most of the PuTTY team read their\r
+e-mail on Unix machines, so copying the file to a Windows box to run\r
+Word is very inconvenient. Not only that, but several of us don't\r
+even \e{have} a copy of Word!\r
+\r
+Some people like to send us screen shots when demonstrating a\r
+problem. Please don't do this without checking with us first - we\r
+almost never actually need the information in the screen shot.\r
+Sending a screen shot of an error box is almost certainly\r
+unnecessary when you could just tell us in plain text what the error\r
+was. (On some versions of Windows, pressing Ctrl-C when the error\r
+box is displayed will copy the text of the message to the clipboard.)\r
+Sending a full-screen shot is \e{occasionally} useful, but it's\r
+probably still wise to check whether we need it before sending it.\r
+\r
+If you \e{must} mail a screen shot, don't send it as a \cw{.BMP}\r
+file. \cw{BMP}s have no compression and they are \e{much} larger\r
+than other image formats such as PNG, TIFF and GIF. Convert the file\r
+to a properly compressed image format before sending it.\r
+\r
+Please don't mail us executables, at all. Our mail server blocks all\r
+incoming e-mail containing executables, as a defence against the\r
+vast numbers of e-mail viruses we receive every day. If you mail us\r
+an executable, it will just bounce.\r
+\r
+If you have made a tiny modification to the PuTTY code, please send\r
+us a \e{patch} to the source code if possible, rather than sending\r
+us a huge \cw{.ZIP} file containing the complete sources plus your\r
+modification. If you've only changed 10 lines, we'd prefer to\r
+receive a mail that's 30 lines long than one containing multiple\r
+megabytes of data we already have.\r
+\r
+\S{feedback-other-fora} Other places to ask for help\r
+\r
+There are two Usenet newsgroups that are particularly relevant to the\r
+PuTTY tools:\r
+\r
+\b \W{news:comp.security.ssh}\c{comp.security.ssh}, for questions\r
+specific to using the SSH protocol;\r
+\r
+\b \W{news:comp.terminals}\c{comp.terminals}, for issues relating to\r
+terminal emulation (for instance, keyboard problems).\r
+\r
+Please use the newsgroup most appropriate to your query, and remember\r
+that these are general newsgroups, not specifically about PuTTY.\r
+\r
+If you don't have direct access to Usenet, you can access these\r
+newsgroups through Google Groups\r
+(\W{http://groups.google.com/}\cw{groups.google.com}).\r
+\r
+\H{feedback-bugs} Reporting bugs\r
+\r
+If you think you have found a bug in PuTTY, your first steps should\r
+be:\r
+\r
+\b Check the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist\r
+page} on the PuTTY website, and see if we already know about the\r
+problem. If we do, it is almost certainly not necessary to mail us\r
+about it, unless you think you have extra information that might be\r
+helpful to us in fixing it. (Of course, if we actually \e{need}\r
+specific extra information about a particular bug, the Wishlist page\r
+will say so.)\r
+\r
+\b Check the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change\r
+Log} on the PuTTY website, and see if we have already fixed the bug\r
+in the \i{development snapshots}.\r
+\r
+\b Check the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/faq.html}{FAQ}\r
+on the PuTTY website (also provided as \k{faq} in the manual), and\r
+see if it answers your question. The FAQ lists the most common\r
+things which people think are bugs, but which aren't bugs.\r
+\r
+\b Download the latest development snapshot and see if the problem\r
+still happens with that. This really is worth doing. As a general\r
+rule we aren't very interested in bugs that appear in the release\r
+version but not in the development version, because that usually\r
+means they are bugs we have \e{already fixed}. On the other hand, if\r
+you can find a bug in the development version that doesn't appear in\r
+the release, that's likely to be a new bug we've introduced since\r
+the release and we're definitely interested in it.\r
+\r
+If none of those options solved your problem, and you still need to\r
+report a bug to us, it is useful if you include some general\r
+information:\r
+\r
+\b Tell us what \i{version of PuTTY} you are running. To find this out,\r
+use the \q{About PuTTY} option from the System menu. Please \e{do\r
+not} just tell us \q{I'm running the latest version}; e-mail can be\r
+delayed and it may not be obvious which version was the latest at\r
+the time you sent the message. \r
+\r
+\b PuTTY is a multi-platform application; tell us what version of what\r
+OS you are running PuTTY on. (If you're running on Unix, or Windows\r
+for Alpha, tell us, or we'll assume you're running on Windows for\r
+Intel as this is overwhelmingly the case.)\r
+\r
+\b Tell us what protocol you are connecting with: SSH, Telnet,\r
+Rlogin or Raw mode.\r
+\r
+\b Tell us what kind of server you are connecting to; what OS, and\r
+if possible what SSH server (if you're using SSH). You can get some\r
+of this information from the PuTTY Event Log (see \k{using-eventlog}\r
+in the manual).\r
+\r
+\b Send us the contents of the PuTTY Event Log, unless you\r
+have a specific reason not to (for example, if it contains\r
+confidential information that you think we should be able to solve\r
+your problem without needing to know).\r
+\r
+\b Try to give us as much information as you can to help us\r
+see the problem for ourselves. If possible, give us a step-by-step\r
+sequence of \e{precise} instructions for reproducing the fault.\r
+\r
+\b Don't just tell us that PuTTY \q{does the wrong thing}; tell us\r
+exactly and precisely what it did, and also tell us exactly and\r
+precisely what you think it should have done instead. Some people\r
+tell us PuTTY does the wrong thing, and it turns out that it was\r
+doing the right thing and their expectations were wrong. Help to\r
+avoid this problem by telling us exactly what you think it should\r
+have done, and exactly what it did do.\r
+\r
+\b If you think you can, you're welcome to try to fix the problem\r
+yourself. A \i{patch} to the code which fixes a bug is an excellent\r
+addition to a bug report. However, a patch is never a \e{substitute}\r
+for a good bug report; if your patch is wrong or inappropriate, and\r
+you haven't supplied us with full information about the actual bug,\r
+then we won't be able to find a better solution.\r
+\r
+\b\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\cw{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\r
+is an article on how to report bugs effectively in general. If your\r
+bug report is \e{particularly} unclear, we may ask you to go away,\r
+read this article, and then report the bug again.\r
+\r
+It is reasonable to report bugs in PuTTY's documentation, if you\r
+think the documentation is unclear or unhelpful. But we do need to\r
+be given exact details of \e{what} you think the documentation has\r
+failed to tell you, or \e{how} you think it could be made clearer.\r
+If your problem is simply that you don't \e{understand} the\r
+documentation, we suggest posting to a newsgroup (see\r
+\k{feedback-other-fora}) and seeing if someone\r
+will explain what you need to know. \e{Then}, if you think the\r
+documentation could usefully have told you that, send us a bug\r
+report and explain how you think we should change it.\r
+\r
+\H{feedback-features} Requesting extra features \r
+\r
+If you want to request a new feature in PuTTY, the very first things\r
+you should do are:\r
+\r
+\b Check the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist\r
+page} on the PuTTY website, and see if your feature is already on\r
+the list. If it is, it probably won't achieve very much to repeat\r
+the request. (But see \k{feedback-feature-priority} if you want to\r
+persuade us to give your particular feature higher priority.)\r
+\r
+\b Check the Wishlist and\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change\r
+Log} on the PuTTY website, and see if we have already added your\r
+feature in the development snapshots. If it isn't clear, download\r
+the latest development snapshot and see if the feature is present.\r
+If it is, then it will also be in the next release and there is no\r
+need to mail us at all.\r
+\r
+If you can't find your feature in either the development snapshots\r
+\e{or} the Wishlist, then you probably do need to submit a feature\r
+request. Since the PuTTY authors are very busy, it helps if you try\r
+to do some of the work for us:\r
+\r
+\b Do as much of the design as you can. Think about \q{corner\r
+cases}; think about how your feature interacts with other existing\r
+features. Think about the user interface; if you can't come up with\r
+a simple and intuitive interface to your feature, you shouldn't be\r
+surprised if we can't either. Always imagine whether it's possible\r
+for there to be more than one, or less than one, of something you'd\r
+assumed there would be one of. (For example, if you were to want\r
+PuTTY to put an icon in the System tray rather than the Taskbar, you\r
+should think about what happens if there's more than one PuTTY\r
+active; how would the user tell which was which?)\r
+\r
+\b If you can program, it may be worth offering to write the feature\r
+yourself and send us a patch. However, it is likely to be helpful\r
+if you confer with us first; there may be design issues you haven't\r
+thought of, or we may be about to make big changes to the code which\r
+your patch would clash with, or something. If you check with the\r
+maintainers first, there is a better chance of your code actually\r
+being usable. Also, read the design principles listed in \k{udp}: if\r
+you do not conform to them, we will probably not be able to accept\r
+your patch.\r
+\r
+\H{feedback-feature-priority} Requesting features that have already\r
+been requested\r
+\r
+If a feature is already listed on the Wishlist, then it usually\r
+means we would like to add it to PuTTY at some point. However, this\r
+may not be in the near future. If there's a feature on the Wishlist\r
+which you would like to see in the \e{near} future, there are\r
+several things you can do to try to increase its priority level:\r
+\r
+\b Mail us and vote for it. (Be sure to mention that you've seen it\r
+on the Wishlist, or we might think you haven't even \e{read} the\r
+Wishlist). This probably won't have very \e{much} effect; if a huge\r
+number of people vote for something then it may make a difference,\r
+but one or two extra votes for a particular feature are unlikely to\r
+change our priority list immediately. Offering a new and compelling\r
+justification might help. Also, don't expect a reply.\r
+\r
+\b Offer us money if we do the work sooner rather than later. This\r
+sometimes works, but not always. The PuTTY team all have full-time\r
+jobs and we're doing all of this work in our free time; we may\r
+sometimes be willing to give up some more of our free time in\r
+exchange for some money, but if you try to bribe us for a \e{big}\r
+feature it's entirely possible that we simply won't have the time to\r
+spare - whether you pay us or not. (Also, we don't accept bribes to\r
+add \e{bad} features to the Wishlist, because our desire to provide\r
+high-quality software to the users comes first.)\r
+\r
+\b Offer to help us write the code. This is probably the \e{only}\r
+way to get a feature implemented quickly, if it's a big one that we\r
+don't have time to do ourselves.\r
+\r
+\H{feedback-support} \ii{Support requests}\r
+\r
+If you're trying to make PuTTY do something for you and it isn't\r
+working, but you're not sure whether it's a bug or not, then\r
+\e{please} consider looking for help somewhere else. This is one of\r
+the most common types of mail the PuTTY team receives, and we simply\r
+don't have time to answer all the questions. Questions of this type\r
+include:\r
+\r
+\b If you want to do something with PuTTY but have no idea where to\r
+start, and reading the manual hasn't helped, try posting to a\r
+newsgroup (see \k{feedback-other-fora}) and see if someone can explain\r
+it to you.\r
+\r
+\b If you have tried to do something with PuTTY but it hasn't\r
+worked, and you aren't sure whether it's a bug in PuTTY or a bug in\r
+your SSH server or simply that you're not doing it right, then try\r
+posting to a newsgroup (see \k{feedback-other-fora}) and see\r
+if someone can solve your problem. Or try doing the same thing with\r
+a different SSH client and see if it works with that. Please do not\r
+report it as a PuTTY bug unless you are really sure it \e{is} a bug\r
+in PuTTY.\r
+\r
+\b If someone else installed PuTTY for you, or you're using PuTTY on\r
+someone else's computer, try asking them for help first.  They're more\r
+likely to understand how they installed it and what they expected you\r
+to use it for than we are.\r
+\r
+\b If you have successfully made a connection to your server and now\r
+need to know what to type at the server's command prompt, or other\r
+details of how to use the server-end software, talk to your server's\r
+system administrator. This is not the PuTTY team's problem. PuTTY is\r
+only a communications tool, like a telephone; if you can't speak the\r
+same language as the person at the other end of the phone, it isn't\r
+the telephone company's job to teach it to you.\r
+\r
+If you absolutely cannot get a support question answered any other\r
+way, you can try mailing it to us, but we can't guarantee to have\r
+time to answer it.\r
+\r
+\H{feedback-webadmin} Web server administration\r
+\r
+If the PuTTY \i{web site} is down (Connection Timed Out), please don't\r
+bother mailing us to tell us about it. Most of us read our e-mail on\r
+the same machines that host the web site, so if those machines are\r
+down then we will notice \e{before} we read our e-mail. So there's\r
+no point telling us our servers are down.\r
+\r
+Of course, if the web site has some other error (Connection Refused,\r
+404 Not Found, 403 Forbidden, or something else) then we might\r
+\e{not} have noticed and it might still be worth telling us about it.\r
+\r
+If you want to report a problem with our web site, check that you're\r
+looking at our \e{real} web site and not a mirror. The real web site\r
+is at\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\c{http://www.chiark.greenend.org.uk/~sgtatham/putty/};\r
+if that's not where you're reading this, then don't report the\r
+problem to us until you've checked that it's really a problem with\r
+the main site. If it's only a problem with the mirror, you should\r
+try to contact the administrator of that mirror site first, and only\r
+contact us if that doesn't solve the problem (in case we need to\r
+remove the mirror from our list).\r
+\r
+\H{feedback-permission} Asking permission for things\r
+\r
+PuTTY is distributed under the MIT Licence (see \k{licence} for\r
+details). This means you can do almost \e{anything} you like with\r
+our software, our source code, and our documentation. The only\r
+things you aren't allowed to do are to remove our copyright notices\r
+or the licence text itself, or to hold us legally responsible if\r
+something goes wrong.\r
+\r
+So if you want permission to include PuTTY on a magazine cover disk,\r
+or as part of a collection of useful software on a CD or a web site,\r
+then \e{permission is already granted}. You don't have to mail us\r
+and ask. Just go ahead and do it. We don't mind.\r
+\r
+(If you want to distribute PuTTY alongside your own application for\r
+use with that application, or if you want to distribute PuTTY within\r
+your own organisation, then we recommend, but do not insist, that\r
+you offer your own first-line technical support, to answer questions\r
+about the interaction of PuTTY with your environment. If your users\r
+mail us directly, we won't be able to tell them anything useful about\r
+your specific setup.)\r
+\r
+If you want to use parts of the PuTTY source code in another\r
+program, then it might be worth mailing us to talk about technical\r
+details, but if all you want is to ask permission then you don't\r
+need to bother. You already have permission.\r
+\r
+If you just want to link to our web site, just go ahead. (It's not\r
+clear that we \e{could} stop you doing this, even if we wanted to!)\r
+\r
+\H{feedback-mirrors} Mirroring the PuTTY web site\r
+\r
+\# the next two paragraphs also on the Mirrors page itself, with\r
+\# minor context changes\r
+\r
+If you want to set up a mirror of the PuTTY website, go ahead and\r
+set one up. Please don't bother asking us for permission before\r
+setting up a mirror. You already have permission.\r
+\r
+If the mirror is in a country where we don't already have plenty of\r
+mirrors, we may be willing to add it to the list on our\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{mirrors\r
+page}. Read the guidelines on that page, make sure your mirror\r
+works, and email us the information listed at the bottom of the\r
+page.\r
+\r
+Note that we do not \e{promise} to list your mirror: we get a lot of\r
+mirror notifications and yours may not happen to find its way to the\r
+top of the list.\r
+\r
+Also note that we link to all our mirror sites using the\r
+\c{rel="nofollow"} attribute. Running a PuTTY mirror is not intended\r
+to be a cheap way to gain search rankings.\r
+\r
+If you have technical questions about the process of mirroring, then\r
+you might want to mail us before setting up the mirror (see also the\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html#guidelines}{guidelines on the Mirrors page});\r
+but if you just want to ask for permission, you don't need to. You\r
+already have permission.\r
+\r
+\H{feedback-compliments} Praise and compliments\r
+\r
+One of the most rewarding things about maintaining free software is\r
+getting e-mails that just say \q{thanks}. We are always happy to\r
+receive e-mails of this type.\r
+\r
+Regrettably we don't have time to answer them all in person. If you\r
+mail us a compliment and don't receive a reply, \e{please} don't\r
+think we've ignored you. We did receive it and we were happy about\r
+it; we just didn't have time to tell you so personally.\r
+\r
+To everyone who's ever sent us praise and compliments, in the past\r
+and the future: \e{you're welcome}!\r
+\r
+\H{feedback-address} E-mail address\r
+\r
+The actual address to mail is\r
+\cw{<\W{mailto:putty@projects.tartarus.org}{putty@projects.tartarus.org}>}.\r
diff --git a/putty/DOC/GS.BUT b/putty/DOC/GS.BUT
new file mode 100644 (file)
index 0000000..b6d8a82
--- /dev/null
@@ -0,0 +1,155 @@
+\define{versionidgs} \versionid $Id: gs.but 6815 2006-08-28 10:35:12Z simon $\r
+\r
+\C{gs} Getting started with PuTTY\r
+\r
+This chapter gives a quick guide to the simplest types of\r
+interactive login session using PuTTY.\r
+\r
+\H{gs-insecure} \ii{Starting a session}\r
+\r
+When you start PuTTY, you will see a \i{dialog box}. This dialog box\r
+allows you to control everything PuTTY can do. See \k{config} for\r
+details of all the things you can control.\r
+\r
+You don't usually need to change most of the configuration options.\r
+To start the simplest kind of session, all you need to do is to\r
+enter a few basic parameters.\r
+\r
+In the \q{Host Name} box, enter the Internet \i{host name} of the server\r
+you want to connect to. You should have been told this by the\r
+provider of your login account.\r
+\r
+Now select a login \i{protocol} to use, from the \q{Connection type}\r
+buttons. For a login session, you should select \i{Telnet},\r
+\i{Rlogin} or \i{SSH}. See \k{which-one} for a description of the\r
+differences between the three protocols, and advice on which one to\r
+use. The fourth protocol, \I{raw protocol}\e{Raw}, is not used for\r
+interactive login sessions; you would usually use this for debugging\r
+other Internet services (see \k{using-rawprot}). The fifth option,\r
+\e{Serial}, is used for connecting to a local serial line, and works\r
+somewhat differently: see \k{using-serial} for more information on\r
+this.\r
+\r
+When you change the selected protocol, the number in the \q{Port}\r
+box will change. This is normal: it happens because the various\r
+login services are usually provided on different network ports by\r
+the server machine. Most servers will use the standard port numbers,\r
+so you will not need to change the port setting. If your server\r
+provides login services on a non-standard port, your system\r
+administrator should have told you which one. (For example, many\r
+\i{MUDs} run Telnet service on a port other than 23.)\r
+\r
+Once you have filled in the \q{Host Name}, \q{Protocol}, and\r
+possibly \q{Port} settings, you are ready to connect. Press the\r
+\q{Open} button at the bottom of the dialog box, and PuTTY will\r
+begin trying to connect you to the server.\r
+\r
+\H{gs-hostkey} \ii{Verifying the host key} (SSH only)\r
+\r
+If you are not using the \i{SSH} protocol, you can skip this\r
+section.\r
+\r
+If you are using SSH to connect to a server for the first time, you\r
+will probably see a message looking something like this:\r
+\r
+\c The server's host key is not cached in the registry. You\r
+\c have no guarantee that the server is the computer you\r
+\c think it is.\r
+\c The server's rsa2 key fingerprint is:\r
+\c ssh-rsa 1024 7b:e5:6f:a7:f4:f9:81:62:5c:e3:1f:bf:8b:57:6c:5a\r
+\c If you trust this host, hit Yes to add the key to\r
+\c PuTTY's cache and carry on connecting.\r
+\c If you want to carry on connecting just once, without\r
+\c adding the key to the cache, hit No.\r
+\c If you do not trust this host, hit Cancel to abandon the\r
+\c connection.\r
+\r
+This is a feature of the SSH protocol. It is designed to protect you\r
+against a network attack known as \i\e{spoofing}: secretly\r
+redirecting your connection to a different computer, so that you\r
+send your password to the wrong machine. Using this technique, an\r
+attacker would be able to learn the password that guards your login\r
+account, and could then log in as if they were you and use the\r
+account for their own purposes.\r
+\r
+To prevent this attack, each server has a unique identifying code,\r
+called a \e{host key}. These keys are created in a way that prevents\r
+one server from forging another server's key. So if you connect to a\r
+server and it sends you a different host key from the one you were\r
+expecting, PuTTY can warn you that the server may have been switched\r
+and that a spoofing attack might be in progress.\r
+\r
+PuTTY records the host key for each server you connect to, in the\r
+Windows \i{Registry}. Every time you connect to a server, it checks\r
+that the host key presented by the server is the same host key as it\r
+was the last time you connected. If it is not, you will see a\r
+warning, and you will have the chance to abandon your connection\r
+before you type any private information (such as a password) into\r
+it.\r
+\r
+However, when you connect to a server you have not connected to\r
+before, PuTTY has no way of telling whether the host key is the\r
+right one or not. So it gives the warning shown above, and asks you\r
+whether you want to \I{trusting host keys}trust this host key or\r
+not.\r
+\r
+Whether or not to trust the host key is your choice. If you are\r
+connecting within a company network, you might feel that all the\r
+network users are on the same side and spoofing attacks are\r
+unlikely, so you might choose to trust the key without checking it.\r
+If you are connecting across a hostile network (such as the\r
+Internet), you should check with your system administrator, perhaps\r
+by telephone or in person. (Some modern servers have more than one\r
+host key. If the system administrator sends you more than one\r
+\I{host key fingerprint}fingerprint, you should make sure the one\r
+PuTTY shows you is on the list, but it doesn't matter which one it is.)\r
+\r
+\# FIXME: this is all very fine but of course in practice the world\r
+doesn't work that way. Ask the team if they have any good ideas for\r
+changes to this section!\r
+\r
+\H{gs-login} \ii{Logging in}\r
+\r
+After you have connected, and perhaps verified the server's host\r
+key, you will be asked to log in, probably using a \i{username} and\r
+a \i{password}. Your system administrator should have provided you\r
+with these. Enter the username and the password, and the server\r
+should grant you access and begin your session. If you have\r
+\I{mistyping a password}mistyped your password, most servers will\r
+give you several chances to get it right.\r
+\r
+If you are using SSH, be careful not to type your username wrongly,\r
+because you will not have a chance to correct it after you press\r
+Return; many SSH servers do not permit you to make two login attempts\r
+using \i{different usernames}. If you type your username wrongly, you\r
+must close PuTTY and start again.\r
+\r
+If your password is refused but you are sure you have typed it\r
+correctly, check that Caps Lock is not enabled. Many login servers,\r
+particularly Unix computers, treat upper case and lower case as\r
+different when checking your password; so if Caps Lock is on, your\r
+password will probably be refused.\r
+\r
+\H{gs-session} After logging in\r
+\r
+After you log in to the server, what happens next is up to the\r
+server! Most servers will print some sort of login message and then\r
+present a \i{prompt}, at which you can type\r
+\I{commands on the server}commands which the\r
+server will carry out. Some servers will offer you on-line help;\r
+others might not. If you are in doubt about what to do next, consult\r
+your system administrator.\r
+\r
+\H{gs-logout} \ii{Logging out}\r
+\r
+When you have finished your session, you should log out by typing\r
+the server's own logout command. This might vary between servers; if\r
+in doubt, try \c{logout} or \c{exit}, or consult a manual or your\r
+system administrator. When the server processes your logout command,\r
+the PuTTY window should close itself automatically.\r
+\r
+You \e{can} close a PuTTY session using the \i{Close button} in the\r
+window border, but this might confuse the server - a bit like\r
+hanging up a telephone unexpectedly in the middle of a conversation.\r
+We recommend you do not do this unless the server has stopped\r
+responding to you and you cannot close the window any other way.\r
diff --git a/putty/DOC/INDEX.BUT b/putty/DOC/INDEX.BUT
new file mode 100644 (file)
index 0000000..aded2e9
--- /dev/null
@@ -0,0 +1,851 @@
+\define{versionidindex} \versionid $Id: index.but 9009 2010-09-25 16:18:02Z jacob $\r
+\r
+\IM{Unix version} Unix version of PuTTY tools\r
+\IM{Unix version} Linux version of PuTTY tools\r
+\r
+\IM{Unix} Unix\r
+\IM{Unix} Linux\r
+\r
+\IM{Command Prompt}{command prompt window}{MS-DOS Prompt}{console window} Command Prompt\r
+\IM{Command Prompt}{command prompt window}{MS-DOS Prompt}{console window} MS-DOS Prompt\r
+\IM{Command Prompt}{command prompt window}{MS-DOS Prompt}{console window} console window\r
+\r
+\IM{spoof}{spoofed}{spoofing} spoofing\r
+\r
+\IM{verifying the host key} verifying the host key\r
+\IM{verifying the host key} host key, verifying\r
+\r
+\IM{trusting host keys} trusting host keys\r
+\IM{trusting host keys} host keys, trusting\r
+\r
+\IM{host key fingerprint} fingerprint, of SSH host key\r
+\IM{host key fingerprint} host key fingerprint (SSH)\r
+\IM{host key fingerprint} SSH host key fingerprint\r
+\r
+\IM{starting a session} starting a session\r
+\IM{starting a session} session, starting\r
+\r
+\IM{commands on the server}{remote command} commands on the server\r
+\IM{commands on the server}{remote command} remote commands\r
+\IM{commands on the server}{remote command} server, commands on\r
+\r
+\IM{mistyping a password} mistyping a password\r
+\IM{mistyping a password} password, mistyping\r
+\r
+\IM{different usernames}{changes of username} different user names\r
+\IM{different usernames}{changes of username} changing user names\r
+\IM{different usernames}{changes of username} user names, different\r
+\IM{different usernames}{changes of username} login names, different\r
+\IM{different usernames}{changes of username} account names, different\r
+\r
+\IM{differences between SSH, Telnet and Rlogin} differences between\r
+SSH, Telnet and Rlogin\r
+\IM{differences between SSH, Telnet and Rlogin} protocols,\r
+differences between\r
+\IM{differences between SSH, Telnet and Rlogin} SSH, differences\r
+from Telnet and Rlogin\r
+\IM{differences between SSH, Telnet and Rlogin} Telnet, differences\r
+from SSH and Rlogin\r
+\IM{differences between SSH, Telnet and Rlogin} Rlogin, differences\r
+from SSH and Telnet\r
+\IM{differences between SSH, Telnet and Rlogin} selecting a protocol\r
+\IM{differences between SSH, Telnet and Rlogin} choosing a protocol\r
+\r
+\IM{MUD}{MUDs} MUDs\r
+\r
+\IM{talker}{talker systems} talker systems\r
+\r
+\IM{security hazard}{security risk} security hazard\r
+\r
+\IM{SSH-1}{SSH protocol version 1} SSH-1\r
+\IM{SSH-2}{SSH protocol version 2} SSH-2\r
+\r
+\IM{terminal window}{PuTTY window} terminal window\r
+\IM{terminal window}{PuTTY window} PuTTY terminal window\r
+\IM{terminal window}{PuTTY window} window, terminal\r
+\r
+\IM{copy and paste} copy and paste\r
+\IM{copy and paste} cut and paste\r
+\IM{copy and paste} paste, copy and\r
+\r
+\IM{three-button mouse} three-button mouse\r
+\IM{three-button mouse} mouse, three-button\r
+\r
+\IM{left mouse button}{left button} left mouse button\r
+\IM{middle mouse button}{middle button} middle mouse button\r
+\IM{right mouse button}{right button} right mouse button\r
+\r
+\IM{selecting words}{word-by-word selection} selecting whole words\r
+\IM{selecting words}{word-by-word selection} words, selecting\r
+\r
+\IM{selecting lines} selecting whole lines\r
+\IM{selecting lines} lines, selecting\r
+\r
+\IM{rectangular selection} rectangular selection\r
+\IM{rectangular selection} selection, rectangular\r
+\r
+\IM{adjusting a selection} adjusting a selection\r
+\IM{adjusting a selection} extending a selection\r
+\IM{adjusting a selection} selection, adjusting\r
+\r
+\IM{right mouse button, with Ctrl} right mouse button, with Ctrl\r
+\IM{right mouse button, with Ctrl} Ctrl, with right mouse button\r
+\r
+\IM{system menu} system menu\r
+\IM{system menu} menu, system\r
+\IM{system menu} window menu\r
+\r
+\IM{context menu} context menu\r
+\IM{context menu} menu, context\r
+\IM{context menu} right mouse button menu\r
+\r
+\IM{Event Log} Event Log\r
+\IM{Event Log} PuTTY Event Log\r
+\IM{Event Log} Log, Event\r
+\r
+\IM{Telnet special commands} Telnet special commands\r
+\IM{Telnet special commands} special commands, in Telnet\r
+\r
+\IM{SSH special commands} SSH special commands\r
+\IM{SSH special commands} special commands, in SSH\r
+\r
+\IM{Repeat key exchange, SSH special command} Repeat key exchange, SSH special command\r
+\IM{Repeat key exchange, SSH special command} key exchange, forcing repeat\r
+\IM{Repeat key exchange, SSH special command} SSH key exchange, forcing repeat\r
+\r
+\IM{accented characters} accented characters\r
+\IM{accented characters} characters, accented\r
+\r
+\IM{line-drawing characters} line-drawing characters\r
+\IM{line-drawing characters} box-drawing characters\r
+\IM{line-drawing characters} characters, line-drawing\r
+\IM{line-drawing characters} ANSI graphics\r
+\r
+\IM{port forwarding}{port forwardings} port forwarding in SSH\r
+\IM{port forwarding}{port forwardings} SSH port forwarding\r
+\IM{port forwarding}{port forwardings} forwarding ports in SSH\r
+\IM{port forwarding}{port forwardings} tunnelling using SSH\r
+\IM{port forwarding}{port forwardings} SSH tunnelling\r
+\r
+\IM{port forwarding, changing mid-session} port forwarding in SSH, changing mid-session\r
+\IM{port forwarding, changing mid-session} SSH port forwarding, changing mid-session\r
+\IM{port forwarding, changing mid-session} forwarding ports in SSH, changing mid-session\r
+\IM{port forwarding, changing mid-session} tunnelling using SSH, changing mid-session\r
+\IM{port forwarding, changing mid-session} SSH tunnelling, changing mid-session\r
+\r
+\IM{local port forwarding} local-to-remote port forwarding\r
+\IM{remote port forwarding} remote-to-local port forwarding\r
+\r
+\IM{dynamic port forwarding} dynamic port forwarding\r
+\IM{dynamic port forwarding} SOCKS port forwarding\r
+\r
+\IM{debugging Internet protocols} debugging Internet protocols\r
+\IM{debugging Internet protocols} Internet protocols, debugging\r
+\IM{debugging Internet protocols} protocols, debugging\r
+\r
+\IM{Internet protocol version} Internet Protocol version\r
+\IM{Internet protocol version} version, of Internet Protocol\r
+\r
+\IM{raw TCP connections} raw TCP connections\r
+\IM{raw TCP connections} TCP connections, raw\r
+\r
+\IM{command-line arguments} command-line arguments\r
+\IM{command-line arguments} arguments, command-line\r
+\IM{command-line arguments} options, command-line\r
+\IM{command-line arguments} switches, command-line\r
+\r
+\IM{Windows shortcut} Windows shortcut\r
+\IM{Windows shortcut} shortcut, Windows\r
+\r
+\IM{telnet URLs} Telnet URLs\r
+\IM{telnet URLs} URLs, Telnet\r
+\r
+\IM{saved sessions, loading from command line} saved sessions,\r
+loading from command line\r
+\IM{saved sessions, loading from command line} loading saved\r
+sessions from command line\r
+\IM{saved sessions, loading from command line} command line, loading\r
+saved sessions from\r
+\r
+\IM{putty @sessionname} \c{putty @sessionname}\r
+\IM{putty @sessionname} \c{@sessionname} command-line argument\r
+\r
+\IM{protocol selection} protocol selection\r
+\IM{protocol selection} selecting a protocol\r
+\IM{protocol selection} choosing a protocol\r
+\r
+\IM{login name}{username} login name\r
+\IM{login name}{username} user name\r
+\IM{login name}{username} account name\r
+\r
+\IM{reading commands from a file} reading commands from a file\r
+\IM{reading commands from a file} commands, reading from a file\r
+\r
+\IM{agent forwarding} agent forwarding\r
+\IM{agent forwarding} authentication agent forwarding\r
+\IM{agent forwarding} SSH agent forwarding\r
+\IM{agent forwarding} forwarding, SSH agent\r
+\r
+\IM{X11 forwarding}{forwarding of X11} X11 forwarding\r
+\IM{X11 forwarding}{forwarding of X11} SSH X11 forwarding\r
+\IM{X11 forwarding}{forwarding of X11} forwarding, of X11\r
+\r
+\IM{X11 authentication} X11 authentication\r
+\IM{X11 authentication} authentication, X11\r
+\r
+\IM{pseudo-terminal allocation} pseudo-terminal allocation\r
+\IM{pseudo-terminal allocation} pty allocation\r
+\IM{pseudo-terminal allocation} allocation, of pseudo-terminal\r
+\r
+\IM{ERASE special character} \cw{ERASE}, special character\r
+\IM{ERASE special character} \cw{VERASE}, special character\r
+\IM{QUIT special character} \cw{QUIT}, special character\r
+\IM{QUIT special character} \cw{VQUIT}, special character\r
+\r
+\IM{-telnet} \c{-telnet} command-line option\r
+\IM{-raw} \c{-raw} command-line option\r
+\IM{-rlogin} \c{-rlogin} command-line option\r
+\IM{-ssh} \c{-ssh} command-line option\r
+\IM{-serial} \c{-serial} command-line option\r
+\IM{-cleanup} \c{-cleanup} command-line option\r
+\IM{-load} \c{-load} command-line option\r
+\IM{-v} \c{-v} command-line option\r
+\IM{-l} \c{-l} command-line option\r
+\IM{-L-upper} \c{-L} command-line option\r
+\IM{-R-upper} \c{-R} command-line option\r
+\IM{-D-upper} \c{-D} command-line option\r
+\IM{-m} \c{-m} command-line option\r
+\IM{-P-upper} \c{-P} command-line option\r
+\IM{-pw} \c{-pw} command-line option\r
+\IM{-A-upper} \c{-A} command-line option\r
+\IM{-a} \c{-a} command-line option\r
+\IM{-X-upper} \c{-X} command-line option\r
+\IM{-x} \c{-x} command-line option\r
+\IM{-T-upper} \c{-T} command-line option\r
+\IM{-t} \c{-t} command-line option\r
+\IM{-C-upper} \c{-C} command-line option\r
+\IM{-N-upper} \c{-N} command-line option\r
+\IM{-1} \c{-1} command-line option\r
+\IM{-2} \c{-2} command-line option\r
+\IM{-i} \c{-i} command-line option\r
+\IM{-pgpfp} \c{-pgpfp} command-line option\r
+\IM{-sercfg} \c{-sercfg} command-line option\r
+\r
+\IM{removing registry entries} removing registry entries\r
+\IM{removing registry entries} registry entries, removing\r
+\r
+\IM{random seed file} random seed file\r
+\IM{random seed file} \c{putty.rnd} (random seed file)\r
+\r
+\IM{putty.rnd} \c{putty.rnd} (random seed file)\r
+\r
+\IM{suppressing remote shell} remote shell, suppressing\r
+\IM{suppressing remote shell} shell, remote, suppressing\r
+\r
+\IM{SSH protocol version} SSH protocol version\r
+\IM{SSH protocol version} protocol version, SSH\r
+\IM{SSH protocol version} version, of SSH protocol\r
+\r
+\IM{PPK} \cw{PPK} file\r
+\IM{PPK} private key file, PuTTY\r
+\r
+\IM{PGP key fingerprint} PGP key fingerprint\r
+\IM{PGP key fingerprint} fingerprint, of PGP key\r
+\r
+\IM{verifying new versions} verifying new versions of PuTTY\r
+\IM{verifying new versions} new version, verifying\r
+\IM{verifying new versions} upgraded version, verifying\r
+\r
+\IM{connection}{network connection} network connection\r
+\IM{connection}{network connection} connection, network\r
+\r
+\IM{host name}{hostname} host name\r
+\IM{host name}{hostname} DNS name\r
+\IM{host name}{hostname} server name\r
+\r
+\IM{IP address}{Internet address} IP address\r
+\IM{IP address}{Internet address} address, IP\r
+\r
+\IM{localhost} \c{localhost}\r
+\r
+\IM{loopback IP address}{loopback address} loopback IP address\r
+\IM{loopback IP address}{loopback address} IP address, loopback\r
+\r
+\IM{listen address} listen address\r
+\IM{listen address} bind address\r
+\r
+\IM{DNS} DNS\r
+\IM{DNS} Domain Name System\r
+\r
+\IM{name resolution} name resolution\r
+\IM{name resolution} DNS resolution\r
+\IM{name resolution} host name resolution\r
+\IM{name resolution} server name resolution\r
+\r
+\IM{loading and storing saved sessions} sessions, loading and storing\r
+\IM{loading and storing saved sessions} settings, loading and storing\r
+\IM{loading and storing saved sessions} saving settings\r
+\IM{loading and storing saved sessions} storing settings\r
+\IM{loading and storing saved sessions} loading settings\r
+\r
+\IM{Default Settings} Default Settings\r
+\IM{Default Settings} settings, default\r
+\r
+\IM{Registry} Registry (Windows)\r
+\IM{Registry} Windows Registry\r
+\r
+\IM{inactive window} inactive window\r
+\IM{inactive window} window, inactive\r
+\IM{inactive window} terminal window, inactive\r
+\r
+\IM{SSH packet log} SSH packet log\r
+\IM{SSH packet log} packet log, SSH\r
+\r
+\IM{auto wrap mode}{auto wrap} auto wrap mode\r
+\IM{auto wrap mode}{auto wrap} wrapping, automatic\r
+\IM{auto wrap mode}{auto wrap} line wrapping, automatic\r
+\r
+\IM{control sequence}{control codes} control sequences\r
+\IM{control sequence}{control codes} terminal control sequences\r
+\IM{control sequence}{control codes} escape sequences\r
+\r
+\IM{cursor coordinates} cursor coordinates\r
+\IM{cursor coordinates} coordinates, cursor\r
+\r
+\IM{CR} CR (Carriage Return)\r
+\IM{CR} Carriage Return\r
+\r
+\IM{LF} LF (Line Feed)\r
+\IM{LF} Line Feed\r
+\r
+\IM{clear screen} clear screen\r
+\IM{clear screen} erase screen\r
+\IM{clear screen} screen, clearing\r
+\r
+\IM{blinking text} blinking text\r
+\IM{blinking text} flashing text\r
+\r
+\IM{answerback} answerback string\r
+\r
+\IM{local echo} local echo\r
+\IM{local echo} echo, local\r
+\r
+\IM{remote echo} remote echo\r
+\IM{remote echo} echo, remote\r
+\r
+\IM{local line editing} local line editing\r
+\IM{local line editing} line editing, local\r
+\r
+\IM{remote-controlled printing} ANSI printing\r
+\IM{remote-controlled printing} remote-controlled printing\r
+\IM{remote-controlled printing} printing, remote-controlled\r
+\r
+\IM{Home and End keys} Home key\r
+\IM{Home and End keys} End key\r
+\r
+\IM{keypad} keypad, numeric\r
+\IM{keypad} numeric keypad\r
+\r
+\IM{Application Cursor Keys} Application Cursor Keys\r
+\IM{Application Cursor Keys} cursor keys, \q{Application} mode\r
+\r
+\IM{Application Keypad} Application Keypad\r
+\IM{Application Keypad} keypad, \q{Application} mode\r
+\IM{Application Keypad} numeric keypad, \q{Application} mode\r
+\r
+\IM{Num Lock}{NumLock} Num Lock\r
+\r
+\IM{NetHack keypad mode} NetHack keypad mode\r
+\IM{NetHack keypad mode} keypad, NetHack mode\r
+\r
+\IM{compose key} Compose key\r
+\IM{compose key} DEC Compose key\r
+\r
+\IM{terminal bell} terminal bell\r
+\IM{terminal bell} bell, terminal\r
+\IM{terminal bell} beep, terminal\r
+\IM{terminal bell} feep\r
+\r
+\IM{Windows Default Beep} Windows Default Beep sound\r
+\IM{Windows Default Beep} Default Beep sound, Windows\r
+\r
+\IM{terminal bell, disabling} terminal bell, disabling\r
+\IM{terminal bell, disabling} bell, disabling\r
+\r
+\IM{visual bell} visual bell\r
+\IM{visual bell} bell, visual\r
+\r
+\IM{PC speaker} PC speaker\r
+\IM{PC speaker} beep, with PC speaker\r
+\r
+\IM{sound file} sound file\r
+\IM{sound file} \cw{WAV} file\r
+\r
+\IM{bell overload} bell overload mode\r
+\IM{bell overload} terminal bell overload mode\r
+\r
+\IM{mouse reporting} mouse reporting\r
+\IM{mouse reporting} \c{xterm} mouse reporting\r
+\r
+\IM{links} \c{links} (web browser)\r
+\r
+\IM{mc} \c{mc}\r
+\IM{mc} Midnight Commander\r
+\r
+\IM{terminal resizing}{window resizing} terminal resizing\r
+\IM{terminal resizing}{window resizing} window resizing\r
+\IM{terminal resizing}{window resizing} resizing, terminal\r
+\r
+\IM{destructive backspace} destructive backspace\r
+\IM{destructive backspace} non-destructive backspace\r
+\IM{destructive backspace} backspace, destructive\r
+\r
+\IM{Arabic text shaping} Arabic text shaping\r
+\IM{Arabic text shaping} shaping, of Arabic text\r
+\r
+\IM{Unicode} Unicode\r
+\IM{Unicode} ISO-10646 (Unicode)\r
+\r
+\IM{ASCII} ASCII\r
+\IM{ASCII} US-ASCII\r
+\r
+\IM{bidirectional text} bidirectional text\r
+\IM{bidirectional text} right-to-left text\r
+\r
+\IM{display becomes corrupted} display corruption\r
+\IM{display becomes corrupted} corruption, of display\r
+\r
+\IM{rows} rows, in terminal window\r
+\IM{columns} columns, in terminal window\r
+\r
+\IM{window size} window size\r
+\IM{window size} size, of window\r
+\r
+\IM{font size} font size\r
+\IM{font size} size, of font\r
+\r
+\IM{full screen}{full-screen} full-screen mode\r
+\r
+\IM{cursor blinks} blinking cursor\r
+\IM{cursor blinks} flashing cursor\r
+\IM{cursor blinks} cursor, blinking\r
+\r
+\IM{font} font\r
+\IM{font} typeface\r
+\r
+\IM{minimise} minimise window\r
+\IM{minimise} window, minimising\r
+\r
+\IM{maximise} maximise window\r
+\IM{maximise} window, maximising\r
+\r
+\IM{closing window}{close window} closing window\r
+\IM{closing window}{close window} window, closing\r
+\r
+\IM{Dragon NaturallySpeaking} Dragon NaturallySpeaking\r
+\IM{Dragon NaturallySpeaking} NaturallySpeaking\r
+\r
+\IM{AltGr} \q{AltGr} key\r
+\IM{Alt} \q{Alt} key\r
+\r
+\IM{CJK} CJK\r
+\IM{CJK} Chinese\r
+\IM{CJK} Japanese\r
+\IM{CJK} Korean\r
+\r
+\IM{East Asian Ambiguous characters} East Asian Ambiguous characters\r
+\IM{East Asian Ambiguous characters} CJK ambiguous characters\r
+\r
+\IM{character width} character width\r
+\IM{character width} single-width character\r
+\IM{character width} double-width character\r
+\r
+\IM{Rich Text Format} Rich Text Format\r
+\IM{Rich Text Format} RTF\r
+\r
+\IM{bold}{bold text} bold text\r
+\r
+\IM{colour}{colours} colour\r
+\r
+\IM{8-bit colour} 8-bit colour\r
+\IM{8-bit colour} colour, 8-bit\r
+\r
+\IM{system colours} system colours\r
+\IM{system colours} colours, system\r
+\r
+\IM{ANSI colours} ANSI colours\r
+\IM{ANSI colours} colours, ANSI\r
+\r
+\IM{cursor colour} cursor colour\r
+\IM{cursor colour} colour, of cursor\r
+\r
+\IM{default background} background colour, default\r
+\IM{default background} colour, background, default\r
+\r
+\IM{default foreground} foreground colour, default\r
+\IM{default foreground} colour, foreground, default\r
+\r
+\IM{TERM} \cw{TERM} environment variable\r
+\r
+\IM{logical palettes} logical palettes\r
+\IM{logical palettes} palettes, logical\r
+\r
+\IM{breaks in connectivity} connectivity, breaks in\r
+\IM{breaks in connectivity} intermittent connectivity\r
+\r
+\IM{idle connections} idle connections\r
+\IM{idle connections} timeout, of connections\r
+\IM{idle connections} connections, idle\r
+\r
+\IM{interactive connections}{interactive session} interactive connections\r
+\IM{interactive connections}{interactive session} connections, interactive\r
+\r
+\IM{keepalives} keepalives, application\r
+\r
+\IM{Nagle's algorithm} Nagle's algorithm\r
+\IM{Nagle's algorithm} \cw{TCP_NODELAY}\r
+\r
+\IM{TCP keepalives} TCP keepalives\r
+\IM{TCP keepalives} keepalives, TCP\r
+\IM{TCP keepalives} \cw{SO_KEEPALIVE}\r
+\r
+\IM{half-open connections} half-open connections\r
+\IM{half-open connections} connections, half-open\r
+\r
+\IM{auto-login username} user name, for auto-login\r
+\IM{auto-login username} login name, for auto-login\r
+\IM{auto-login username} account name, for auto-login\r
+\r
+\IM{terminal emulation}{terminal-type} terminal emulation\r
+\IM{terminal emulation}{terminal-type} emulation, terminal\r
+\r
+\IM{terminal speed} terminal speed\r
+\IM{terminal speed} speed, terminal\r
+\IM{terminal speed} baud rate, of terminal\r
+\r
+\IM{environment variables} environment variables\r
+\IM{environment variables} variables, environment\r
+\r
+\IM{proxy} proxy server\r
+\IM{proxy} server, proxy\r
+\r
+\IM{HTTP proxy} HTTP proxy\r
+\IM{HTTP proxy} proxy, HTTP\r
+\IM{HTTP proxy} server, HTTP\r
+\IM{HTTP proxy} \cw{CONNECT} proxy (HTTP)\r
+\r
+\IM{SOCKS server} SOCKS proxy\r
+\IM{SOCKS server} server, SOCKS\r
+\IM{SOCKS server} proxy, SOCKS\r
+\r
+\IM{Telnet proxy} Telnet proxy\r
+\IM{Telnet proxy} TCP proxy\r
+\IM{Telnet proxy} ad-hoc proxy\r
+\IM{Telnet proxy} proxy, Telnet\r
+\r
+\IM{Local proxy} local proxy\r
+\IM{Local proxy} proxy command\r
+\IM{Local proxy} command, proxy\r
+\r
+\IM{proxy DNS} proxy DNS\r
+\IM{proxy DNS} DNS, with proxy\r
+\IM{proxy DNS} name resolution, with proxy\r
+\IM{proxy DNS} host name resolution, with proxy\r
+\IM{proxy DNS} server name resolution, with proxy\r
+\r
+\IM{proxy username} proxy user name\r
+\IM{proxy username} user name, for proxy\r
+\IM{proxy username} login name, for proxy\r
+\IM{proxy username} account name, for proxy\r
+\r
+\IM{proxy password} proxy password\r
+\IM{proxy password} password, for proxy\r
+\r
+\IM{proxy authentication} proxy authentication\r
+\IM{proxy authentication} authentication, to proxy\r
+\r
+\IM{HTTP basic} HTTP \q{basic} authentication\r
+\IM{HTTP basic} \q{basic} authentication (HTTP)\r
+\r
+\IM{plaintext password} plain text password\r
+\IM{plaintext password} password, plain text\r
+\r
+\IM{Telnet negotiation} Telnet option negotiation\r
+\IM{Telnet negotiation} option negotiation, Telnet\r
+\IM{Telnet negotiation} negotiation, of Telnet options\r
+\r
+\IM{firewall}{firewalls} firewalls\r
+\r
+\IM{NAT router}{NAT} NAT routers\r
+\IM{NAT router}{NAT} routers, NAT\r
+\IM{NAT router}{NAT} Network Address Translation\r
+\IM{NAT router}{NAT} IP masquerading\r
+\r
+\IM{Telnet New Line} Telnet New Line\r
+\IM{Telnet New Line} new line, in Telnet\r
+\r
+\IM{.rhosts} \c{.rhosts} file\r
+\IM{.rhosts} \q{rhosts} file\r
+\r
+\IM{passwordless login} passwordless login\r
+\IM{passwordless login} login, passwordless\r
+\r
+\IM{Windows user name} local user name, in Windows\r
+\IM{Windows user name} user name, local, in Windows\r
+\IM{Windows user name} login name, local, in Windows\r
+\IM{Windows user name} account name, local, in Windows\r
+\r
+\IM{local username in Rlogin} local user name, in Rlogin\r
+\IM{local username in Rlogin} user name, local, in Rlogin\r
+\IM{local username in Rlogin} login name, local, in Rlogin\r
+\IM{local username in Rlogin} account name, local, in Rlogin\r
+\r
+\IM{privileged port} privileged port\r
+\IM{privileged port} low-numbered port\r
+\IM{privileged port} port, privileged\r
+\r
+\IM{remote shell} shell, remote\r
+\IM{remote shell} remote shell\r
+\r
+\IM{encryption}{encrypted}{encrypt} encryption\r
+\r
+\IM{encryption algorithm} encryption algorithm\r
+\IM{encryption algorithm} cipher algorithm\r
+\IM{encryption algorithm} symmetric-key algorithm\r
+\IM{encryption algorithm} algorithm, encryption\r
+\r
+\IM{AES} AES\r
+\IM{AES} Advanced Encryption Standard\r
+\IM{AES} Rijndael\r
+\r
+\IM{Arcfour} Arcfour\r
+\IM{Arcfour} RC4\r
+\r
+\IM{triple-DES} triple-DES\r
+\r
+\IM{single-DES} single-DES\r
+\IM{single-DES} DES\r
+\r
+\IM{key exchange} key exchange\r
+\IM{key exchange} kex\r
+\r
+\IM{shared secret} shared secret\r
+\IM{shared secret} secret, shared\r
+\r
+\IM{key exchange algorithm} key exchange algorithm\r
+\IM{key exchange algorithm} algorithm, key exchange\r
+\r
+\IM{Diffie-Hellman key exchange} Diffie-Hellman key exchange\r
+\IM{Diffie-Hellman key exchange} key exchange, Diffie-Hellman\r
+\r
+\IM{group exchange} Diffie-Hellman group exchange\r
+\IM{group exchange} group exchange, Diffie-Hellman\r
+\r
+\IM{repeat key exchange} repeat key exchange\r
+\IM{repeat key exchange} key exchange, repeat\r
+\r
+\IM{challenge/response authentication} challenge/response authentication\r
+\IM{challenge/response authentication} authentication, challenge/response\r
+\r
+\IM{security token} security token\r
+\IM{security token} token, security\r
+\r
+\IM{one-time passwords} one-time passwords\r
+\IM{one-time passwords} password, one-time\r
+\r
+\IM{keyboard-interactive authentication} keyboard-interactive authentication\r
+\IM{keyboard-interactive authentication} authentication, keyboard-interactive\r
+\r
+\IM{password expiry} password expiry\r
+\IM{password expiry} expiry, of passwords\r
+\r
+\IM{public key authentication}{public-key authentication} public key authentication\r
+\IM{public key authentication}{public-key authentication} RSA authentication\r
+\IM{public key authentication}{public-key authentication} DSA authentication\r
+\IM{public key authentication}{public-key authentication} authentication, public key\r
+\r
+\IM{MIT-MAGIC-COOKIE-1} \cw{MIT-MAGIC-COOKIE-1}\r
+\IM{MIT-MAGIC-COOKIE-1} magic cookie\r
+\IM{MIT-MAGIC-COOKIE-1} cookie, magic\r
+\r
+\IM{SSH server bugs} SSH server bugs\r
+\IM{SSH server bugs} bugs, in SSH servers\r
+\r
+\IM{ignore message} SSH \q{ignore} messages\r
+\IM{ignore message} \q{ignore} messages, in SSH\r
+\r
+\IM{message authentication code}{MAC} message authentication code (MAC)\r
+\IM{message authentication code}{MAC} MAC (message authentication code)\r
+\r
+\IM{signatures} signature\r
+\IM{signatures} digital signature\r
+\r
+\IM{storing configuration in a file} storing settings in a file\r
+\IM{storing configuration in a file} saving settings in a file\r
+\IM{storing configuration in a file} loading settings from a file\r
+\r
+\IM{transferring files} transferring files\r
+\IM{transferring files} files, transferring\r
+\r
+\IM{receiving files}{download a file} receiving files\r
+\IM{receiving files}{download a file} files, receiving\r
+\IM{receiving files}{download a file} downloading files\r
+\r
+\IM{sending files}{upload a file} sending files\r
+\IM{sending files}{upload a file} files, sending\r
+\IM{sending files}{upload a file} uploading files\r
+\r
+\IM{listing files} listing files\r
+\IM{listing files} files, listing\r
+\r
+\IM{wildcard}{wildcards} wildcards\r
+\IM{wildcard}{wildcards} glob (wildcard)\r
+\r
+\IM{PATH} \c{PATH} environment variable\r
+\r
+\IM{SFTP} SFTP\r
+\IM{SFTP} SSH file transfer protocol\r
+\r
+\IM{-unsafe} \c{-unsafe} PSCP command-line option\r
+\IM{-ls-PSCP} \c{-ls} PSCP command-line option\r
+\IM{-p-PSCP} \c{-p} PSCP command-line option\r
+\IM{-q-PSCP} \c{-q} PSCP command-line option\r
+\IM{-r-PSCP} \c{-r} PSCP command-line option\r
+\IM{-batch-PSCP} \c{-batch} PSCP command-line option\r
+\IM{-sftp} \c{-sftp} PSCP command-line option\r
+\IM{-scp} \c{-scp} PSCP command-line option\r
+\r
+\IM{return value} return value\r
+\IM{return value} exit value\r
+\r
+\IM{-b-PSFTP} \c{-b} PSFTP command-line option\r
+\IM{-bc-PSFTP} \c{-bc} PSFTP command-line option\r
+\IM{-be-PSFTP} \c{-be} PSFTP command-line option\r
+\IM{-batch-PSFTP} \c{-batch} PSFTP command-line option\r
+\r
+\IM{spaces in filenames} spaces in filenames\r
+\IM{spaces in filenames} filenames containing spaces\r
+\r
+\IM{working directory} working directory\r
+\IM{working directory} current working directory\r
+\r
+\IM{resuming file transfers} resuming file transfers\r
+\IM{resuming file transfers} files, resuming transfer of\r
+\r
+\IM{changing permissions on files} changing permissions on files\r
+\IM{changing permissions on files} permissions on files, changing\r
+\IM{changing permissions on files} files, changing permissions on\r
+\IM{changing permissions on files} modes of files, changing\r
+\IM{changing permissions on files} access to files, changing\r
+\r
+\IM{deleting files} deleting files\r
+\IM{deleting files} files, deleting\r
+\IM{deleting files} removing files\r
+\r
+\IM{create a directory} creating directories\r
+\IM{create a directory} directories, creating\r
+\r
+\IM{remove a directory} removing directories\r
+\IM{remove a directory} directories, removing\r
+\IM{remove a directory} deleting directories\r
+\r
+\IM{rename remote files} renaming files\r
+\IM{rename remote files} files, renaming and moving\r
+\IM{rename remote files} moving files\r
+\r
+\IM{local Windows command} local Windows command\r
+\IM{local Windows command} Windows command\r
+\r
+\IM{PLINK_PROTOCOL} \c{PLINK_PROTOCOL} environment variable\r
+\r
+\IM{-batch-plink} \c{-batch} Plink command-line option\r
+\IM{-s-plink} \c{-s} Plink command-line option\r
+\r
+\IM{subsystem} subsystem, SSH\r
+\IM{subsystem} SSH subsystem\r
+\r
+\IM{batch file}{batch files} batch files\r
+\r
+\IM{CVS_RSH} \c{CVS_RSH} environment variable\r
+\r
+\IM{DSA} DSA\r
+\IM{DSA} Digital Signature Standard\r
+\r
+\IM{public-key algorithm} public-key algorithm\r
+\IM{public-key algorithm} asymmetric key algorithm\r
+\IM{public-key algorithm} algorithm, public-key\r
+\r
+\IM{generating keys} generating key pairs\r
+\IM{generating keys} creating key pairs\r
+\IM{generating keys} key pairs, generating\r
+\IM{generating keys} public keys, generating\r
+\IM{generating keys} private keys, generating\r
+\r
+\IM{authorized_keys file}{authorized_keys} \cw{authorized_keys} file\r
+\r
+\IM{key fingerprint} fingerprint, of SSH authentication key\r
+\IM{key fingerprint} public key fingerprint (SSH)\r
+\IM{key fingerprint} SSH public key fingerprint\r
+\r
+\IM{SSH-2 public key format} SSH-2 public key file format\r
+\IM{SSH-2 public key format} public key file, SSH-2\r
+\r
+\IM{OpenSSH private key format} OpenSSH private key file format\r
+\IM{OpenSSH private key format} private key file, OpenSSH\r
+\r
+\IM{ssh.com private key format} \cw{ssh.com} private key file format\r
+\IM{ssh.com private key format} private key file, \cw{ssh.com}\r
+\r
+\IM{importing keys} importing private keys\r
+\IM{importing keys} loading private keys\r
+\r
+\IM{export private keys} exporting private keys\r
+\IM{export private keys} saving private keys\r
+\r
+\IM{.ssh} \c{.ssh} directory\r
+\r
+\IM{.ssh2} \c{.ssh2} directory\r
+\r
+\IM{authentication agent} authentication agent\r
+\IM{authentication agent} agent, authentication\r
+\r
+\IM{-c-pageant} \c{-c} Pageant command-line option\r
+\r
+\IM{FAQ} FAQ\r
+\IM{FAQ} Frequently Asked Questions\r
+\r
+\IM{supported features} supported features\r
+\IM{supported features} features, supported\r
+\r
+\IM{remember my password} storing passwords\r
+\IM{remember my password} password, storing\r
+\r
+\IM{login scripts}{startup scripts} login scripts\r
+\IM{login scripts}{startup scripts} startup scripts\r
+\r
+\IM{WS2_32.DLL} \cw{WS2_32.DLL}\r
+\IM{WS2_32.DLL} WinSock version 2\r
+\r
+\IM{Red Hat Linux} Red Hat Linux\r
+\IM{Red Hat Linux} Linux, Red Hat\r
+\r
+\IM{SMB} SMB\r
+\IM{SMB} Windows file sharing\r
+\r
+\IM{clean up} clean up after PuTTY\r
+\IM{clean up} uninstalling\r
+\r
+\IM{version of PuTTY} version, of PuTTY\r
+\r
+\IM{PGP signatures} PGP signatures, of PuTTY binaries\r
+\IM{PGP signatures} signatures, of PuTTY binaries\r
+\r
+\IM{logical host name} logical host name\r
+\IM{logical host name} host name, logical\r
+\IM{logical host name} host key, caching policy\r
+\r
+\IM{web browsers} web browser\r
+\r
+\IM{GSSAPI credential delegation} GSSAPI credential delegation\r
+\IM{GSSAPI credential delegation} credential delegation, GSSAPI\r
+\IM{GSSAPI credential delegation} delegation, of GSSAPI credentials\r
diff --git a/putty/DOC/INTRO.BUT b/putty/DOC/INTRO.BUT
new file mode 100644 (file)
index 0000000..8796662
--- /dev/null
@@ -0,0 +1,88 @@
+\define{versionidintro} \versionid $Id: intro.but 5593 2005-04-05 18:01:32Z jacob $\r
+\r
+\C{intro} Introduction to PuTTY\r
+\r
+PuTTY is a free SSH, Telnet and Rlogin client for 32-bit Windows\r
+systems.\r
+\r
+\H{you-what} What are SSH, Telnet and Rlogin?\r
+\r
+If you already know what SSH, Telnet and Rlogin are, you can safely\r
+skip on to the next section.\r
+\r
+SSH, Telnet and Rlogin are three ways of doing the same thing:\r
+logging in to a multi-user computer from another computer, over a\r
+network.\r
+\r
+Multi-user operating systems, such as Unix and VMS, usually present\r
+a \i{command-line interface} to the user, much like the \q{\i{Command\r
+Prompt}} or \q{\i{MS-DOS Prompt}} in Windows. The system prints a\r
+prompt, and you type commands which the system will obey.\r
+\r
+Using this type of interface, there is no need for you to be sitting\r
+at the same machine you are typing commands to. The commands, and\r
+responses, can be sent over a network, so you can sit at one\r
+computer and give commands to another one, or even to more than one.\r
+\r
+SSH, Telnet and Rlogin are \i\e{network protocols} that allow you to\r
+do this. On the computer you sit at, you run a \i\e{client}, which\r
+makes a network connection to the other computer (the \i\e{server}).\r
+The network connection carries your keystrokes and commands from the\r
+client to the server, and carries the server's responses back to\r
+you.\r
+\r
+These protocols can also be used for other types of keyboard-based\r
+interactive session. In particular, there are a lot of bulletin\r
+boards, \i{talker systems} and \i{MUDs} (Multi-User Dungeons) which support\r
+access using Telnet. There are even a few that support SSH.\r
+\r
+You might want to use SSH, Telnet or Rlogin if:\r
+\r
+\b you have an account on a Unix or VMS system which you want to be\r
+able to access from somewhere else\r
+\r
+\b your Internet Service Provider provides you with a login account\r
+on a \i{web server}. (This might also be known as a \i\e{shell account}.\r
+A \e{shell} is the program that runs on the server and interprets\r
+your commands for you.)\r
+\r
+\b you want to use a \i{bulletin board system}, talker or MUD which can\r
+be accessed using Telnet.\r
+\r
+You probably do \e{not} want to use SSH, Telnet or Rlogin if:\r
+\r
+\b you only use Windows. Windows computers have their own\r
+ways of networking between themselves, and unless you are doing\r
+something fairly unusual, you will not need to use any of these\r
+remote login protocols.\r
+\r
+\H{which-one} How do SSH, Telnet and Rlogin differ?\r
+\r
+This list summarises some of the \i{differences between SSH, Telnet\r
+and Rlogin}.\r
+\r
+\b SSH (which stands for \q{\i{secure shell}}) is a recently designed,\r
+high-security protocol. It uses strong cryptography to protect your\r
+connection against eavesdropping, hijacking and other attacks. Telnet\r
+and Rlogin are both older protocols offering minimal security.\r
+\r
+\b SSH and Rlogin both allow you to \I{passwordless login}log in to the\r
+server without having to type a password. (Rlogin's method of doing this is\r
+insecure, and can allow an attacker to access your account on the\r
+server. SSH's method is much more secure, and typically breaking the\r
+security requires the attacker to have gained access to your actual\r
+client machine.)\r
+\r
+\b SSH allows you to connect to the server and automatically send a\r
+command, so that the server will run that command and then\r
+disconnect. So you can use it in automated processing.\r
+\r
+The Internet is a hostile environment and security is everybody's\r
+responsibility. If you are connecting across the open Internet, then\r
+we recommend you use SSH. If the server you want to connect to\r
+doesn't support SSH, it might be worth trying to persuade the\r
+administrator to install it.\r
+\r
+If your client and server are both behind the same (good) firewall,\r
+it is more likely to be safe to use Telnet or Rlogin, but we still\r
+recommend you use SSH.\r
diff --git a/putty/DOC/LICENCE.BUT b/putty/DOC/LICENCE.BUT
new file mode 100644 (file)
index 0000000..62d8d74
--- /dev/null
@@ -0,0 +1,29 @@
+\define{versionidlicence} \versionid $Id: licence.but 9072 2011-01-05 12:01:00Z jacob $\r
+\r
+\A{licence} PuTTY \ii{Licence}\r
+\r
+PuTTY is \i{copyright} 1997-2011 Simon Tatham.\r
+\r
+Portions copyright Robert de Bath, Joris van Rantwijk, Delian\r
+Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,\r
+Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus\r
+Kuhn, Colin Watson, and CORE SDI S.A.\r
+\r
+Permission is hereby granted, free of charge, to any person\r
+obtaining a copy of this software and associated documentation files\r
+(the \q{Software}), to deal in the Software without restriction,\r
+including without limitation the rights to use, copy, modify, merge,\r
+publish, distribute, sublicense, and/or sell copies of the Software,\r
+and to permit persons to whom the Software is furnished to do so,\r
+subject to the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be\r
+included in all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED \q{AS IS}, WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+NONINFRINGEMENT.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE\r
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
diff --git a/putty/DOC/MAKEFILE b/putty/DOC/MAKEFILE
new file mode 100644 (file)
index 0000000..cbdb2a8
--- /dev/null
@@ -0,0 +1,75 @@
+all: man index.html\r
+\r
+# Decide on the versionid policy.\r
+#\r
+# If the user has passed in $(VERSION) on the command line (`make\r
+# VERSION="Release 0.56"'), we use that as an explicit version\r
+# string. Otherwise, we use `svnversion' to examine the checked-out\r
+# documentation source, and if that returns a single revision\r
+# number then we invent a version string reflecting just that\r
+# number. Failing _that_, we resort to versionids.but which shows a\r
+# $Id for each individual file.\r
+#\r
+# So here, we define VERSION using svnversion if it isn't already\r
+# defined ...\r
+ifndef VERSION\r
+SVNVERSION=$(shell test -d .svn && svnversion .)\r
+BADCHARS=$(findstring :,$(SVNVERSION))$(findstring S,$(SVNVERSION))\r
+ifeq ($(BADCHARS),)\r
+ifneq ($(SVNVERSION),)\r
+ifneq ($(SVNVERSION),exported)\r
+VERSION=Built from revision $(patsubst M,,$(SVNVERSION))\r
+endif\r
+endif\r
+endif\r
+endif\r
+# ... and now, we condition our build behaviour on whether or not\r
+# VERSION _is_ defined.\r
+ifdef VERSION\r
+VERSIONIDS=vstr\r
+vstr.but: FORCE\r
+       echo \\versionid $(VERSION) > vstr.but\r
+FORCE:;\r
+else\r
+VERSIONIDS=vids\r
+endif\r
+\r
+CHAPTERS := $(SITE) blurb intro gs using config pscp psftp plink pubkey\r
+CHAPTERS += pageant errors faq feedback licence udp pgpkeys sshnames\r
+CHAPTERS += index $(VERSIONIDS)\r
+\r
+INPUTS = $(patsubst %,%.but,$(CHAPTERS))\r
+\r
+# This is temporary. Hack it locally or something.\r
+HALIBUT = halibut\r
+\r
+index.html: $(INPUTS)\r
+       $(HALIBUT) --text --html --winhelp $(INPUTS)\r
+\r
+# During formal builds it's useful to be able to build this one alone.\r
+putty.hlp: $(INPUTS)\r
+       $(HALIBUT) --winhelp $(INPUTS)\r
+\r
+putty.info: $(INPUTS)\r
+       $(HALIBUT) --info $(INPUTS)\r
+\r
+chm: putty.hhp\r
+putty.hhp: $(INPUTS) chm.but\r
+       $(HALIBUT) --html $(INPUTS) chm.but\r
+\r
+MKMAN = $(HALIBUT) --man=$@ mancfg.but $<\r
+MANPAGES = putty.1 puttygen.1 plink.1 pscp.1 psftp.1 puttytel.1 pterm.1\r
+man: $(MANPAGES)\r
+\r
+putty.1: man-putt.but mancfg.but; $(MKMAN)\r
+puttygen.1: man-pg.but mancfg.but; $(MKMAN)\r
+plink.1: man-pl.but mancfg.but; $(MKMAN)\r
+pscp.1: man-pscp.but mancfg.but; $(MKMAN)\r
+psftp.1: man-psft.but mancfg.but; $(MKMAN)\r
+puttytel.1: man-ptel.but mancfg.but; $(MKMAN)\r
+pterm.1: man-pter.but mancfg.but; $(MKMAN)\r
+\r
+mostlyclean:\r
+       rm -f *.html *.txt *.hlp *.cnt *.1 *.info vstr.but *.hh[pck]\r
+clean: mostlyclean\r
+       rm -f *.chm\r
diff --git a/putty/DOC/MAN-PG.BUT b/putty/DOC/MAN-PG.BUT
new file mode 100644 (file)
index 0000000..d71fe57
--- /dev/null
@@ -0,0 +1,211 @@
+\cfg{man-identity}{puttygen}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite}\r
+\r
+\H{puttygen-manpage} Man page for PuTTYgen\r
+\r
+\S{puttygen-manpage-name} NAME\r
+\r
+\cw{puttygen} - public-key generator for the PuTTY tools\r
+\r
+\S{puttygen-manpage-synopsis} SYNOPSIS\r
+\r
+\c puttygen ( keyfile | -t keytype [ -b bits ] )\r
+\e bbbbbbbb   iiiiiii   bb iiiiiii   bb iiii\r
+\c          [ -C new-comment ] [ -P ] [ -q ]\r
+\e            bb iiiiiiiiiii     bb     bb\r
+\c          [ -O output-type | -l | -L | -p ]\r
+\e            bb iiiiiiiiiii   bb   bb   bb\r
+\c          [ -o output-file ]\r
+\e            bb iiiiiiiiiii\r
+\r
+\S{puttygen-manpage-description} DESCRIPTION\r
+\r
+\c{puttygen} is a tool to generate and manipulate SSH public and\r
+private key pairs. It is part of the PuTTY suite, although it can\r
+also interoperate with the private key formats used by some other\r
+SSH clients.\r
+\r
+When you run \c{puttygen}, it does three things. Firstly, it either\r
+loads an existing key file (if you specified \e{keyfile}), or\r
+generates a new key (if you specified \e{keytype}). Then, it\r
+optionally makes modifications to the key (changing the comment\r
+and/or the passphrase); finally, it outputs the key, or some\r
+information about the key, to a file.\r
+\r
+All three of these phases are controlled by the options described in\r
+the following section.\r
+\r
+\S{puttygen-manpage-options} OPTIONS\r
+\r
+In the first phase, \c{puttygen} either loads or generates a key.\r
+Note that generating a key requires random data (from\r
+\c{/dev/random}), which can cause \c{puttygen} to pause, possibly for\r
+some time if your system does not have much randomness available.\r
+\r
+The options to control this phase are:\r
+\r
+\dt \e{keyfile}\r
+\r
+\dd Specify a private key file to be loaded. This private key file can\r
+be in the (de facto standard) SSH-1 key format, or in PuTTY's SSH-2\r
+key format, or in either of the SSH-2 private key formats used by\r
+OpenSSH and ssh.com's implementation.\r
+\r
+\dt \cw{\-t} \e{keytype}\r
+\r
+\dd Specify a type of key to generate. The acceptable values here are\r
+\c{rsa} and \c{dsa} (to generate SSH-2 keys), and \c{rsa1} (to\r
+generate SSH-1 keys).\r
+\r
+\dt \cw{\-b} \e{bits}\r
+\r
+\dd Specify the size of the key to generate, in bits. Default is 1024.\r
+\r
+\dt \cw{\-q}\r
+\r
+\dd Suppress the progress display when generating a new key.\r
+\r
+In the second phase, \c{puttygen} optionally alters properties of\r
+the key it has loaded or generated. The options to control this are:\r
+\r
+\dt \cw{\-C} \e{new\-comment}\r
+\r
+\dd Specify a comment string to describe the key. This comment string\r
+will be used by PuTTY to identify the key to you (when asking you to\r
+enter the passphrase, for example, so that you know which passphrase\r
+to type).\r
+\r
+\dt \cw{\-P}\r
+\r
+\dd Indicate that you want to change the key's passphrase. This is\r
+automatic when you are generating a new key, but not when you are\r
+modifying an existing key.\r
+\r
+In the third phase, \c{puttygen} saves the key or information\r
+about it. The options to control this are:\r
+\r
+\dt \cw{\-O} \e{output\-type}\r
+\r
+\dd Specify the type of output you want \c{puttygen} to produce.\r
+Acceptable options are:\r
+\r
+\lcont{\r
+\r
+\dt \cw{private}\r
+\r
+\dd Save the private key in a format usable by PuTTY. This will either\r
+be the standard SSH-1 key format, or PuTTY's own SSH-2 key format.\r
+\r
+\dt \cw{public}\r
+\r
+\dd Save the public key only. For SSH-1 keys, the standard public key\r
+format will be used (\q{\cw{1024 37 5698745}...}). For SSH-2 keys, the\r
+public key will be output in the format specified by RFC 4716,\r
+which is a multi-line text file beginning with the line\r
+\q{\cw{---- BEGIN SSH2 PUBLIC KEY ----}}.\r
+\r
+\dt \cw{public-openssh}\r
+\r
+\dd Save the public key only, in a format usable by OpenSSH. For SSH-1\r
+keys, this output format behaves identically to \c{public}. For\r
+SSH-2 keys, the public key will be output in the OpenSSH format,\r
+which is a single line (\q{\cw{ssh-rsa AAAAB3NzaC1yc2}...}).\r
+\r
+\dt \cw{fingerprint}\r
+\r
+\dd Print the fingerprint of the public key. All fingerprinting\r
+algorithms are believed compatible with OpenSSH.\r
+\r
+\dt \cw{private-openssh}\r
+\r
+\dd Save an SSH-2 private key in OpenSSH's format. This option is not\r
+permitted for SSH-1 keys.\r
+\r
+\dt \cw{private-sshcom}\r
+\r
+\dd Save an SSH-2 private key in ssh.com's format. This option is not\r
+permitted for SSH-1 keys.\r
+\r
+If no output type is specified, the default is \c{private}.\r
+\r
+}\r
+\r
+\dt \cw{\-o} \e{output\-file}\r
+\r
+\dd Specify the file where \c{puttygen} should write its output. If\r
+this option is not specified, \c{puttygen} will assume you want to\r
+overwrite the original file if the input and output file types are\r
+the same (changing a comment or passphrase), and will assume you\r
+want to output to stdout if you are asking for a public key or\r
+fingerprint. Otherwise, the \c{\-o} option is required.\r
+\r
+\dt \cw{\-l}\r
+\r
+\dd Synonym for \q{\cw{-O fingerprint}}.\r
+\r
+\dt \cw{\-L}\r
+\r
+\dd Synonym for \q{\cw{-O public-openssh}}.\r
+\r
+\dt \cw{\-p}\r
+\r
+\dd Synonym for \q{\cw{-O public}}.\r
+\r
+The following options do not run PuTTYgen as normal, but print\r
+informational messages and then quit:\r
+\r
+\dt \cw{\-h}, \cw{\-\-help}\r
+\r
+\dd Display a message summarizing the available options.\r
+\r
+\dt \cw{\-V}, \cw{\-\-version}\r
+\r
+\dd Display the version of PuTTYgen.\r
+\r
+\dt \cw{\-\-pgpfp}\r
+\r
+\dd Display the fingerprints of the PuTTY PGP Master Keys, to aid\r
+in verifying new files released by the PuTTY team.\r
+\r
+\S{puttygen-manpage-examples} EXAMPLES\r
+\r
+To generate an SSH-2 RSA key pair and save it in PuTTY's own format\r
+(you will be prompted for the passphrase):\r
+\r
+\c puttygen -t rsa -C "my home key" -o mykey.ppk\r
+\r
+To generate a larger (2048-bit) key:\r
+\r
+\c puttygen -t rsa -b 2048 -C "my home key" -o mykey.ppk\r
+\r
+To change the passphrase on a key (you will be prompted for the old\r
+and new passphrases):\r
+\r
+\c puttygen -P mykey.ppk\r
+\r
+To change the comment on a key:\r
+\r
+\c puttygen -C "new comment" mykey.ppk\r
+\r
+To convert a key into OpenSSH's private key format:\r
+\r
+\c puttygen mykey.ppk -O private-openssh -o my-openssh-key\r
+\r
+To convert a key \e{from} another format (\c{puttygen} will\r
+automatically detect the input key type):\r
+\r
+\c puttygen my-ssh.com-key -o mykey.ppk\r
+\r
+To display the fingerprint of a key (some key types require a\r
+passphrase to extract even this much information):\r
+\r
+\c puttygen -l mykey.ppk\r
+\r
+To add the OpenSSH-format public half of a key to your authorised\r
+keys file:\r
+\r
+\c puttygen -L mykey.ppk >> $HOME/.ssh/authorized_keys\r
+\r
+\S{puttygen-manpage-bugs} BUGS\r
+\r
+There's currently no way to supply passphrases in batch mode, or\r
+even just to specify that you don't want a passphrase at all.\r
diff --git a/putty/DOC/MAN-PL.BUT b/putty/DOC/MAN-PL.BUT
new file mode 100644 (file)
index 0000000..ecc483f
--- /dev/null
@@ -0,0 +1,185 @@
+\cfg{man-identity}{plink}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite}\r
+\r
+\H{plink-manpage} Man page for Plink\r
+\r
+\S{plink-manpage-name} NAME\r
+\r
+\cw{plink} \- PuTTY link, command line network connection tool\r
+\r
+\S{plink-manpage-synopsis} SYNOPSIS\r
+\r
+\c plink [options] [user@]host [command]\r
+\e bbbbb  iiiiiii   iiiib iiii  iiiiiii\r
+\r
+\S{plink-manpage-description} DESCRIPTION\r
+\r
+\cw{plink} is a network connection tool supporting several protocols.\r
+\r
+\S{plink-manpage-options} OPTIONS\r
+\r
+The command-line options supported by \cw{plink} are:\r
+\r
+\dt \cw{-V}\r
+\r
+\dd Show version information and exit.\r
+\r
+\dt \cw{-pgpfp}\r
+\r
+\dd Display the fingerprints of the PuTTY PGP Master Keys and exit,\r
+to aid in verifying new files released by the PuTTY team.\r
+\r
+\dt \cw{-v}\r
+\r
+\dd Show verbose messages.\r
+\r
+\dt \cw{-load} \e{session}\r
+\r
+\dd Load settings from saved session.\r
+\r
+\dt \cw{-ssh}\r
+\r
+\dd Force use of SSH protocol (default).\r
+\r
+\dt \cw{-telnet}\r
+\r
+\dd Force use of Telnet protocol.\r
+\r
+\dt \cw{-rlogin}\r
+\r
+\dd Force use of rlogin protocol.\r
+\r
+\dt \cw{-raw}\r
+\r
+\dd Force raw mode.\r
+\r
+\dt \cw{-serial}\r
+\r
+\dd Force serial mode.\r
+\r
+\dt \cw{-P} \e{port}\r
+\r
+\dd Connect to port \e{port}.\r
+\r
+\dt \cw{-l} \e{user}\r
+\r
+\dd Set remote username to \e{user}.\r
+\r
+\dt \cw{-m} \e{path}\r
+\r
+\dd Read remote command(s) from local file \e{path}.\r
+\r
+\dt \cw{-batch}\r
+\r
+\dd Disable interactive prompts.\r
+\r
+\dt \cw{-pw} \e{password}\r
+\r
+\dd Set remote password to \e{password}. \e{CAUTION:} this will likely\r
+make the password visible to other users of the local machine (via\r
+commands such as \q{\c{w}}).\r
+\r
+\dt \cw{\-L} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport}\r
+\r
+\dd Set up a local port forwarding: listen on \e{srcport} (or\r
+\e{srcaddr}:\e{srcport} if specified), and forward any connections\r
+over the SSH connection to the destination address\r
+\e{desthost}:\e{destport}. Only works in SSH.\r
+\r
+\dt \cw{\-R} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport}\r
+\r
+\dd Set up a remote port forwarding: ask the SSH server to listen on\r
+\e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and to\r
+forward any connections back over the SSH connection where the\r
+client will pass them on to the destination address\r
+\e{desthost}:\e{destport}. Only works in SSH.\r
+\r
+\dt \cw{\-D} [\e{srcaddr}:]\e{srcport}\r
+\r
+\dd Set up dynamic port forwarding. The client listens on\r
+\e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and\r
+implements a SOCKS server. So you can point SOCKS-aware applications\r
+at this port and they will automatically use the SSH connection to\r
+tunnel all their connections. Only works in SSH.\r
+\r
+\dt \cw{-X}\r
+\r
+\dd Enable X11 forwarding.\r
+\r
+\dt \cw{-x}\r
+\r
+\dd Disable X11 forwarding (default).\r
+\r
+\dt \cw{-A}\r
+\r
+\dd Enable agent forwarding.\r
+\r
+\dt \cw{-a}\r
+\r
+\dd Disable agent forwarding (default).\r
+\r
+\dt \cw{-t}\r
+\r
+\dd Enable pty allocation (default if a command is NOT specified).\r
+\r
+\dt \cw{-T}\r
+\r
+\dd Disable pty allocation (default if a command is specified).\r
+\r
+\dt \cw{-1}\r
+\r
+\dd Force use of SSH protocol version 1.\r
+\r
+\dt \cw{-2}\r
+\r
+\dd Force use of SSH protocol version 2.\r
+\r
+\dt \cw{-C}\r
+\r
+\dd Enable SSH compression.\r
+\r
+\dt \cw{-i} \e{path}\r
+\r
+\dd Private key file for authentication.\r
+\r
+\dt \cw{-s}\r
+\r
+\dd Remote command is SSH subsystem (SSH-2 only).\r
+\r
+\dt \cw{-N}\r
+\r
+\dd Don't start a remote command or shell at all (SSH-2 only).\r
+\r
+\dt \cw{\-sercfg} \e{configuration-string}\r
+\r
+\dd Specify the configuration parameters for the serial port, in\r
+\cw{-serial} mode. \e{configuration-string} should be a\r
+comma-separated list of configuration parameters as follows:\r
+\r
+\lcont{\r
+\r
+\b Any single digit from 5 to 9 sets the number of data bits.\r
+\r
+\b \cq{1}, \cq{1.5} or \cq{2} sets the number of stop bits.\r
+\r
+\b Any other numeric string is interpreted as a baud rate.\r
+\r
+\b A single lower-case letter specifies the parity: \cq{n} for none,\r
+\cq{o} for odd, \cq{e} for even, \cq{m} for mark and \cq{s} for space.\r
+\r
+\b A single upper-case letter specifies the flow control: \cq{N} for\r
+none, \cq{X} for XON/XOFF, \cq{R} for RTS/CTS and \cq{D} for\r
+DSR/DTR.\r
+\r
+}\r
+\r
+\S{plink-manpage-more-information} MORE INFORMATION\r
+\r
+For more information on plink, it's probably best to go and look at\r
+the manual on the PuTTY web page:\r
+\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\r
+\r
+\S{plink-manpage-bugs} BUGS\r
+\r
+This man page isn't terribly complete. See the above web link for\r
+better documentation.\r
diff --git a/putty/DOC/MAN-PSCP.BUT b/putty/DOC/MAN-PSCP.BUT
new file mode 100644 (file)
index 0000000..c56db97
--- /dev/null
@@ -0,0 +1,116 @@
+\cfg{man-identity}{pscp}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite}\r
+\r
+\H{pscp-manpage} Man page for PSCP\r
+\r
+\S{pscp-manpage-name} NAME\r
+\r
+\cw{pscp} \- command-line SCP (secure copy) / SFTP client\r
+\r
+\S{pscp-manpage-synopsis} SYNOPSIS\r
+\r
+\c pscp [options] [user@]host:source target\r
+\e bbbb  iiiiiii   iiiib iiiibiiiiii iiiiii\r
+\c pscp [options] source [source...] [user@]host:target\r
+\e bbbb  iiiiiii  iiiiii  iiiiii      iiiib iiiibiiiiii\r
+\c pscp [options] -ls [user@]host:filespec\r
+\e bbbb  iiiiiii  bbb  iiiib iiiibiiiiiiii\r
+\r
+\S{pscp-manpage-description} DESCRIPTION\r
+\r
+\cw{pscp} is a command-line client for the SSH-based SCP (secure\r
+copy) and SFTP (secure file transfer protocol) protocols.\r
+\r
+\S{pscp-manpage-options} OPTIONS\r
+\r
+The command-line options supported by \e{pscp} are:\r
+\r
+\dt \cw{-V}\r
+\r
+\dd Show version information and exit.\r
+\r
+\dt \cw{-pgpfp}\r
+\r
+\dd Display the fingerprints of the PuTTY PGP Master Keys and exit,\r
+to aid in verifying new files released by the PuTTY team.\r
+\r
+\dt \cw{-ls}\r
+\r
+\dd Remote directory listing.\r
+\r
+\dt \cw{-p}\r
+\r
+\dd Preserve file attributes.\r
+\r
+\dt \cw{-q}\r
+\r
+\dd Quiet, don't show statistics.\r
+\r
+\dt \cw{-r}\r
+\r
+\dd Copy directories recursively.\r
+\r
+\dt \cw{-unsafe}\r
+\r
+\dd Allow server-side wildcards (DANGEROUS).\r
+\r
+\dt \cw{-v}\r
+\r
+\dd Show verbose messages.\r
+\r
+\dt \cw{-load} \e{session}\r
+\r
+\dd Load settings from saved session.\r
+\r
+\dt \cw{-P} \e{port}\r
+\r
+\dd Connect to port \e{port}.\r
+\r
+\dt \cw{-l} \e{user}\r
+\r
+\dd Set remote username to \e{user}.\r
+\r
+\dt \cw{-batch}\r
+\r
+\dd Disable interactive prompts.\r
+\r
+\dt \cw{-pw} \e{password}\r
+\r
+\dd Set remote password to \e{password}. \e{CAUTION:} this will likely\r
+make the password visible to other users of the local machine (via\r
+commands such as \q{\c{w}}).\r
+\r
+\dt \cw{-1}\r
+\r
+\dd Force use of SSH protocol version 1.\r
+\r
+\dt \cw{-2}\r
+\r
+\dd Force use of SSH protocol version 2.\r
+\r
+\dt \cw{-C}\r
+\r
+\dd Enable SSH compression.\r
+\r
+\dt \cw{-i} \e{path}\r
+\r
+\dd Private key file for authentication.\r
+\r
+\dt \cw{-scp}\r
+\r
+\dd Force use of SCP protocol.\r
+\r
+\dt \cw{-sftp}\r
+\r
+\dd Force use of SFTP protocol.\r
+\r
+\S{pscp-manpage-more-information} MORE INFORMATION\r
+\r
+For more information on \cw{pscp} it's probably best to go and look at\r
+the manual on the PuTTY web page:\r
+\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\r
+\r
+\S{pscp-manpage-bugs} BUGS\r
+\r
+This man page isn't terribly complete. See the above web link for\r
+better documentation.\r
diff --git a/putty/DOC/MAN-PSFT.BUT b/putty/DOC/MAN-PSFT.BUT
new file mode 100644 (file)
index 0000000..adaf640
--- /dev/null
@@ -0,0 +1,101 @@
+\cfg{man-identity}{psftp}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite}\r
+\r
+\H{psftp-manpage} Man page for PSFTP\r
+\r
+\S{psftp-manpage-name} NAME\r
+\r
+\cw{psftp} \- interactive SFTP (secure file transfer protocol) client\r
+\r
+\S{psftp-manpage-synopsis} SYNOPSIS\r
+\r
+\c psftp [options] [user@]host\r
+\e bbbbb  iiiiiii   iiiib iiii\r
+\r
+\S{psftp-manpage-description} DESCRIPTION\r
+\r
+\cw{psftp} is an interactive text-based client for the SSH-based SFTP\r
+(secure file transfer) protocol.\r
+\r
+\S{psftp-manpage-options} OPTIONS\r
+\r
+The command-line options supported by \cw{psftp} are:\r
+\r
+\dt \cw{-V}\r
+\r
+\dd Show version information and exit.\r
+\r
+\dt \cw{-pgpfp}\r
+\r
+\dd Display the fingerprints of the PuTTY PGP Master Keys and exit,\r
+to aid in verifying new files released by the PuTTY team.\r
+\r
+\dt \cw{-b} \e{batchfile}\r
+\r
+\dd Use specified batchfile.\r
+\r
+\dt \cw{-bc}\r
+\r
+\dd Output batchfile commands.\r
+\r
+\dt \cw{-be}\r
+\r
+\dd Don't stop batchfile processing on errors.\r
+\r
+\dt \cw{-v}\r
+\r
+\dd Show verbose messages.\r
+\r
+\dt \cw{-load} \e{session}\r
+\r
+\dd Load settings from saved session.\r
+\r
+\dt \cw{-P} \e{port}\r
+\r
+\dd Connect to port \e{port}.\r
+\r
+\dt \cw{-l} \e{user}\r
+\r
+\dd Set remote username to \e{user}.\r
+\r
+\dt \cw{-batch}\r
+\r
+\dd Disable interactive prompts.\r
+\r
+\dt \cw{-pw} \e{password}\r
+\r
+\dd Set remote password to \e{password}. \e{CAUTION:} this will likely\r
+make the password visible to other users of the local machine (via\r
+commands such as \q{\c{w}}).\r
+\r
+\dt \cw{-1}\r
+\r
+\dd Force use of SSH protocol version 1.\r
+\r
+\dt \cw{-2}\r
+\r
+\dd Force use of SSH protocol version 2.\r
+\r
+\dt \cw{-C}\r
+\r
+\dd Enable SSH compression.\r
+\r
+\dt \cw{-i} \e{path}\r
+\r
+\dd Private key file for authentication.\r
+\r
+\S{psftp-manpage-commands} COMMANDS\r
+\r
+For a list of commands available inside \cw{psftp}, type \cw{help}\r
+at the \cw{psftp>} prompt.\r
+\r
+\S{psftp-manpage-more-information} MORE INFORMATION\r
+\r
+For more information on \cw{psftp} it's probably best to go and look at\r
+the manual on the PuTTY web page:\r
+\r
+\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\r
+\r
+\S{psftp-manpage-bugs} BUGS\r
+\r
+This man page isn't terribly complete. See the above web link for\r
+better documentation.\r
diff --git a/putty/DOC/MAN-PTEL.BUT b/putty/DOC/MAN-PTEL.BUT
new file mode 100644 (file)
index 0000000..1bdd334
--- /dev/null
@@ -0,0 +1,189 @@
+\cfg{man-identity}{puttytel}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite}\r
+\r
+\H{puttytel-manpage} Man page for PuTTYtel\r
+\r
+\S{puttytel-manpage-name} NAME\r
+\r
+\cw{puttytel} \- GUI Telnet and Rlogin client for X\r
+\r
+\S{puttytel-manpage-synopsis} SYNOPSIS\r
+\r
+\c puttytel [ options ] [ host ]\r
+\e bbbbbbbb   iiiiiii     iiii\r
+\r
+\S{puttytel-manpage-description} DESCRIPTION\r
+\r
+\cw{puttytel} is a graphical Telnet and Rlogin client for X. It\r
+is a direct port of the Windows Telnet and Rlogin client of the same\r
+name, and a cut-down cryptography-free version of PuTTY.\r
+\r
+\S{puttytel-manpage-options} OPTIONS\r
+\r
+The command-line options supported by \cw{puttytel} are:\r
+\r
+\dt \cw{\-\-display} \e{display\-name}\r
+\r
+\dd Specify the X display on which to open \cw{puttytel}. (Note this\r
+option has a double minus sign, even though none of the others do.\r
+This is because this option is supplied automatically by GTK.\r
+Sorry.)\r
+\r
+\dt \cw{\-fn} \e{font-name}\r
+\r
+\dd Specify the font to use for normal text displayed in the terminal.\r
+\r
+\dt \cw{\-fb} \e{font-name}\r
+\r
+\dd Specify the font to use for bold text displayed in the terminal. If\r
+the \cw{BoldAsColour} resource is set to 1 (the default), bold text\r
+will be displayed in different colours instead of a different font,\r
+so this option will be ignored. If \cw{BoldAsColour} is set to 0\r
+and you do not specify a bold font, \cw{puttytel} will overprint the\r
+normal font to make it look bolder.\r
+\r
+\dt \cw{\-fw} \e{font-name}\r
+\r
+\dd Specify the font to use for double-width characters (typically\r
+Chinese, Japanese and Korean text) displayed in the terminal.\r
+\r
+\dt \cw{\-fwb} \e{font-name}\r
+\r
+\dd Specify the font to use for bold double-width characters\r
+(typically Chinese, Japanese and Korean text). Like \cw{-fb}, this\r
+will be ignored unless the \cw{BoldAsColour} resource is set to 0.\r
+\r
+\dt \cw{\-geometry} \e{geometry}\r
+\r
+\dd Specify the size of the terminal, in rows and columns of text. See\r
+\e{X(7)} for more information on the syntax of geometry\r
+specifications.\r
+\r
+\dt \cw{\-sl} \e{lines}\r
+\r
+\dd Specify the number of lines of scrollback to save off the top of the\r
+terminal.\r
+\r
+\dt \cw{\-fg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for normal text.\r
+\r
+\dt \cw{\-bg} \e{colour}\r
+\r
+\dd Specify the background colour to use for normal text.\r
+\r
+\dt \cw{\-bfg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for bold text, if the\r
+\cw{BoldAsColour} resource is set to 1 (the default).\r
+\r
+\dt \cw{\-bbg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for bold reverse-video text, if\r
+the \cw{BoldAsColour} resource is set to 1 (the default). (This\r
+colour is best thought of as the bold version of the background\r
+colour; so it only appears when text is displayed \e{in} the\r
+background colour.)\r
+\r
+\dt \cw{\-cfg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for text covered by the cursor.\r
+\r
+\dt \cw{\-cbg} \e{colour}\r
+\r
+\dd Specify the background colour to use for text covered by the cursor.\r
+In other words, this is the main colour of the cursor.\r
+\r
+\dt \cw{\-title} \e{title}\r
+\r
+\dd Specify the initial title of the terminal window. (This can be\r
+changed under control of the server.)\r
+\r
+\dt \cw{\-sb\-} or \cw{+sb}\r
+\r
+\dd Tells \cw{puttytel} not to display a scroll bar.\r
+\r
+\dt \cw{\-sb}\r
+\r
+\dd Tells \cw{puttytel} to display a scroll bar: this is the opposite of\r
+\cw{\-sb\-}. This is the default option: you will probably only need\r
+to specify it explicitly if you have changed the default using the\r
+\cw{ScrollBar} resource.\r
+\r
+\dt \cw{\-log} \e{filename}\r
+\r
+\dd This option makes \cw{puttytel} log all the terminal output to a file\r
+as well as displaying it in the terminal.\r
+\r
+\dt \cw{\-cs} \e{charset}\r
+\r
+\dd This option specifies the character set in which \cw{puttytel}\r
+should assume the session is operating. This character set will be\r
+used to interpret all the data received from the session, and all\r
+input you type or paste into \cw{puttytel} will be converted into\r
+this character set before being sent to the session.\r
+\r
+\lcont{ Any character set name which is valid in a MIME header (and\r
+supported by \cw{puttytel}) should be valid here (examples are\r
+\q{\cw{ISO-8859-1}}, \q{\cw{windows-1252}} or \q{\cw{UTF-8}}). Also,\r
+any character encoding which is valid in an X logical font\r
+description should be valid (\q{\cw{ibm-cp437}}, for example).\r
+\r
+\cw{puttytel}'s default behaviour is to use the same character\r
+encoding as its primary font. If you supply a Unicode\r
+(\cw{iso10646-1}) font, it will default to the UTF-8 character set.\r
+\r
+Character set names are case-insensitive.\r
+}\r
+\r
+\dt \cw{\-nethack}\r
+\r
+\dd Tells \cw{puttytel} to enable NetHack keypad mode, in which the\r
+numeric keypad generates the NetHack \c{hjklyubn} direction keys.\r
+This enables you to play NetHack with the numeric keypad without\r
+having to use the NetHack \c{number_pad} option (which requires you\r
+to press \q{\cw{n}} before any repeat count). So you can move with\r
+the numeric keypad, and enter repeat counts with the normal number\r
+keys.\r
+\r
+\dt \cw{\-help}, \cw{\-\-help}\r
+\r
+\dd Display a message summarizing the available options.\r
+\r
+\dt \cw{\-pgpfp}\r
+\r
+\dd Display the fingerprints of the PuTTY PGP Master Keys, to aid\r
+in verifying new files released by the PuTTY team.\r
+\r
+\dt \cw{\-load} \e{session}\r
+\r
+\dd Load a saved session by name. This allows you to run a saved session\r
+straight from the command line without having to go through the\r
+configuration box first.\r
+\r
+\dt \cw{\-telnet}, \cw{\-rlogin}, \cw{\-raw}\r
+\r
+\dd Select the protocol \cw{puttytel} will use to make the connection.\r
+\r
+\dt \cw{\-l} \e{username}\r
+\r
+\dd Specify the username to use when logging in to the server.\r
+\r
+\dt \cw{\-P} \e{port}\r
+\r
+\dd Specify the port to connect to the server on.\r
+\r
+\S{puttytel-manpage-saved-sessions} SAVED SESSIONS\r
+\r
+Saved sessions are stored in a \cw{.putty/sessions} subdirectory in\r
+your home directory.\r
+\r
+\S{puttytel-manpage-more-information} MORE INFORMATION\r
+\r
+For more information on PuTTY and PuTTYtel, it's probably best to go\r
+and look at the manual on the web page:\r
+\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\r
+\r
+\S{puttytel-manpage-bugs} BUGS\r
+\r
+This man page isn't terribly complete.\r
diff --git a/putty/DOC/MAN-PTER.BUT b/putty/DOC/MAN-PTER.BUT
new file mode 100644 (file)
index 0000000..9e96f77
--- /dev/null
@@ -0,0 +1,676 @@
+\cfg{man-identity}{pterm}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite}\r
+\r
+\H{pterm-manpage} Man page for pterm\r
+\r
+\S{pterm-manpage-name} NAME\r
+\r
+pterm \- yet another X terminal emulator\r
+\r
+\S{pterm-manpage-synopsis} SYNOPSIS\r
+\r
+\c pterm [ options ]\r
+\e bbbbb   iiiiiii\r
+\r
+\S{pterm-manpage-description} DESCRIPTION\r
+\r
+\cw{pterm} is a terminal emulator for X. It is based on a port of\r
+the terminal emulation engine in the Windows SSH client PuTTY.\r
+\r
+\S{pterm-manpage-options} OPTIONS\r
+\r
+The command-line options supported by \cw{pterm} are:\r
+\r
+\dt \cw{\-e} \e{command} [ \e{arguments} ]\r
+\r
+\dd Specify a command to be executed in the new terminal. Everything on\r
+the command line after this option will be passed straight to the\r
+\cw{execvp} system call; so if you need the command to redirect its\r
+input or output, you will have to use \cw{sh}:\r
+\r
+\lcont{\r
+\r
+\c pterm -e sh -c 'mycommand < inputfile'\r
+\r
+}\r
+\r
+\dt \cw{\-\-display} \e{display\-name}\r
+\r
+\dd Specify the X display on which to open \cw{pterm}. (Note this\r
+option has a double minus sign, even though none of the others do.\r
+This is because this option is supplied automatically by GTK.\r
+Sorry.)\r
+\r
+\dt \cw{\-name} \e{name}\r
+\r
+\dd Specify the name under which \cw{pterm} looks up X resources.\r
+Normally it will look them up as (for example) \cw{pterm.Font}. If\r
+you specify \q{\cw{\-name xyz}}, it will look them up as\r
+\cw{xyz.Font} instead. This allows you to set up several different\r
+sets of defaults and choose between them.\r
+\r
+\dt \cw{\-fn} \e{font-name}\r
+\r
+\dd Specify the font to use for normal text displayed in the terminal.\r
+\r
+\dt \cw{\-fb} \e{font-name}\r
+\r
+\dd Specify the font to use for bold text displayed in the terminal. If\r
+the \cw{BoldAsColour} resource is set to 1 (the default), bold text\r
+will be displayed in different colours instead of a different font,\r
+so this option will be ignored. If \cw{BoldAsColour} is set to 0\r
+and you do not specify a bold font, \cw{pterm} will overprint the\r
+normal font to make it look bolder.\r
+\r
+\dt \cw{\-fw} \e{font-name}\r
+\r
+\dd Specify the font to use for double-width characters (typically\r
+Chinese, Japanese and Korean text) displayed in the terminal.\r
+\r
+\dt \cw{\-fwb} \e{font-name}\r
+\r
+\dd Specify the font to use for bold double-width characters\r
+(typically Chinese, Japanese and Korean text). Like \cw{-fb}, this\r
+will be ignored unless the \cw{BoldAsColour} resource is set to 0.\r
+\r
+\dt \cw{\-geometry} \e{geometry}\r
+\r
+\dd Specify the size of the terminal, in rows and columns of text. See\r
+\e{X(7)} for more information on the syntax of geometry\r
+specifications.\r
+\r
+\dt \cw{\-sl} \e{lines}\r
+\r
+\dd Specify the number of lines of scrollback to save off the top of the\r
+terminal.\r
+\r
+\dt \cw{\-fg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for normal text.\r
+\r
+\dt \cw{\-bg} \e{colour}\r
+\r
+\dd Specify the background colour to use for normal text.\r
+\r
+\dt \cw{\-bfg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for bold text, if the\r
+\cw{BoldAsColour} resource is set to 1 (the default).\r
+\r
+\dt \cw{\-bbg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for bold reverse-video text, if\r
+the \cw{BoldAsColour} resource is set to 1 (the default). (This\r
+colour is best thought of as the bold version of the background\r
+colour; so it only appears when text is displayed \e{in} the\r
+background colour.)\r
+\r
+\dt \cw{\-cfg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for text covered by the cursor.\r
+\r
+\dt \cw{\-cbg} \e{colour}\r
+\r
+\dd Specify the background colour to use for text covered by the cursor.\r
+In other words, this is the main colour of the cursor.\r
+\r
+\dt \cw{\-title} \e{title}\r
+\r
+\dd Specify the initial title of the terminal window. (This can be\r
+changed under control of the server.)\r
+\r
+\dt \cw{\-ut\-} or \cw{+ut}\r
+\r
+\dd Tells \cw{pterm} not to record your login in the \cw{utmp},\r
+\cw{wtmp} and \cw{lastlog} system log files; so you will not show\r
+up on \cw{finger} or \cw{who} listings, for example.\r
+\r
+\dt \cw{\-ut}\r
+\r
+\dd Tells \cw{pterm} to record your login in \cw{utmp}, \cw{wtmp} and\r
+\cw{lastlog}: this is the opposite of \cw{\-ut\-}. This is the\r
+default option: you will probably only need to specify it explicitly\r
+if you have changed the default using the \cw{StampUtmp} resource.\r
+\r
+\dt \cw{\-ls\-} or \cw{+ls}\r
+\r
+\dd Tells \cw{pterm} not to execute your shell as a login shell.\r
+\r
+\dt \cw{\-ls}\r
+\r
+\dd Tells \cw{pterm} to execute your shell as a login shell: this is\r
+the opposite of \cw{\-ls\-}. This is the default option: you will\r
+probably only need to specify it explicitly if you have changed the\r
+default using the \cw{LoginShell} resource.\r
+\r
+\dt \cw{\-sb\-} or \cw{+sb}\r
+\r
+\dd Tells \cw{pterm} not to display a scroll bar.\r
+\r
+\dt \cw{\-sb}\r
+\r
+\dd Tells \cw{pterm} to display a scroll bar: this is the opposite of\r
+\cw{\-sb\-}. This is the default option: you will probably only need\r
+to specify it explicitly if you have changed the default using the\r
+\cw{ScrollBar} resource.\r
+\r
+\dt \cw{\-log} \e{filename}\r
+\r
+\dd This option makes \cw{pterm} log all the terminal output to a file\r
+as well as displaying it in the terminal.\r
+\r
+\dt \cw{\-cs} \e{charset}\r
+\r
+\dd This option specifies the character set in which \cw{pterm} should\r
+assume the session is operating. This character set will be used to\r
+interpret all the data received from the session, and all input you\r
+type or paste into \cw{pterm} will be converted into this character\r
+set before being sent to the session.\r
+\r
+\lcont{ Any character set name which is valid in a MIME header (and\r
+supported by \cw{pterm}) should be valid here (examples are\r
+\q{\cw{ISO-8859-1}}, \q{\cw{windows-1252}} or \q{\cw{UTF-8}}). Also,\r
+any character encoding which is valid in an X logical font\r
+description should be valid (\q{\cw{ibm-cp437}}, for example).\r
+\r
+\cw{pterm}'s default behaviour is to use the same character encoding\r
+as its primary font. If you supply a Unicode (\cw{iso10646-1}) font,\r
+it will default to the UTF-8 character set.\r
+\r
+Character set names are case-insensitive.\r
+}\r
+\r
+\dt \cw{\-nethack}\r
+\r
+\dd Tells \cw{pterm} to enable NetHack keypad mode, in which the\r
+numeric keypad generates the NetHack \c{hjklyubn} direction keys.\r
+This enables you to play NetHack with the numeric keypad without\r
+having to use the NetHack \c{number_pad} option (which requires you\r
+to press \q{\cw{n}} before any repeat count). So you can move with\r
+the numeric keypad, and enter repeat counts with the normal number\r
+keys.\r
+\r
+\dt \cw{\-xrm} \e{resource-string}\r
+\r
+\dd This option specifies an X resource string. Useful for setting\r
+resources which do not have their own command-line options. For\r
+example:\r
+\r
+\lcont{\r
+\r
+\c pterm -xrm 'ScrollbarOnLeft: 1'\r
+\r
+}\r
+\r
+\dt \cw{\-help}, \cw{\-\-help}\r
+\r
+\dd Display a message summarizing the available options.\r
+\r
+\dt \cw{\-pgpfp}\r
+\r
+\dd Display the fingerprints of the PuTTY PGP Master Keys, to aid\r
+in verifying new files released by the PuTTY team.\r
+\r
+\S{pterm-manpage-x-resources} X RESOURCES\r
+\r
+\cw{pterm} can be more completely configured by means of X\r
+resources. All of these resources are of the form \cw{pterm.FOO} for\r
+some \cw{FOO}; you can make \cw{pterm} look them up under another\r
+name, such as \cw{xyz.FOO}, by specifying the command-line option\r
+\q{\cw{\-name xyz}}.\r
+\r
+\dt \cw{pterm.CloseOnExit}\r
+\r
+\dd This option should be set to 0, 1 or 2; the default is 2. It\r
+controls what \cw{pterm} does when the process running inside it\r
+terminates. When set to 2 (the default), \cw{pterm} will close its\r
+window as soon as the process inside it terminates. When set to 0,\r
+\cw{pterm} will print the process's exit status, and the window\r
+will remain present until a key is pressed (allowing you to inspect\r
+the scrollback, and copy and paste text out of it).\r
+\r
+\lcont{\r
+\r
+When this setting is set to 1, \cw{pterm} will close\r
+immediately if the process exits cleanly (with an exit status of\r
+zero), but the window will stay around if the process exits with a\r
+non-zero code or on a signal. This enables you to see what went\r
+wrong if the process suffers an error, but not to have to bother\r
+closing the window in normal circumstances.\r
+\r
+}\r
+\r
+\dt \cw{pterm.WarnOnClose}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1.\r
+When set to 1, \cw{pterm} will ask for confirmation before closing\r
+its window when you press the close button.\r
+\r
+\dt \cw{pterm.TerminalType}\r
+\r
+\dd This controls the value set in the \cw{TERM} environment\r
+variable inside the new terminal. The default is \q{\cw{xterm}}.\r
+\r
+\dt \cw{pterm.BackspaceIsDelete}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1.\r
+When set to 0, the ordinary Backspace key generates the Backspace\r
+character (\cw{^H}); when set to 1, it generates the Delete\r
+character (\cw{^?}). Whichever one you set, the terminal device\r
+inside \cw{pterm} will be set up to expect it.\r
+\r
+\dt \cw{pterm.RXVTHomeEnd}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+it is set to 1, the Home and End keys generate the control sequences\r
+they would generate in the \cw{rxvt} terminal emulator, instead of\r
+the more usual ones generated by other emulators.\r
+\r
+\dt \cw{pterm.LinuxFunctionKeys}\r
+\r
+\dd This option can be set to any number between 0 and 5 inclusive;\r
+the default is 0. The modes vary the control sequences sent by the\r
+function keys; for more complete documentation, it is probably\r
+simplest to try each option in \q{\cw{pterm \-e cat}}, and press the\r
+keys to see what they generate.\r
+\r
+\dt \cw{pterm.NoApplicationKeys}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, it stops the server from ever switching the numeric keypad\r
+into application mode (where the keys send function-key-like\r
+sequences instead of numbers or arrow keys). You probably only need\r
+this if some application is making a nuisance of itself.\r
+\r
+\dt \cw{pterm.NoApplicationCursors}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, it stops the server from ever switching the cursor keys\r
+into application mode (where the keys send slightly different\r
+sequences). You probably only need this if some application is\r
+making a nuisance of itself.\r
+\r
+\dt \cw{pterm.NoMouseReporting}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, it stops the server from ever enabling mouse reporting\r
+mode (where mouse clicks are sent to the application instead of\r
+controlling cut and paste).\r
+\r
+\dt \cw{pterm.NoRemoteResize}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, it stops the server from being able to remotely control\r
+the size of the \cw{pterm} window.\r
+\r
+\dt \cw{pterm.NoAltScreen}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, it stops the server from using the \q{alternate screen}\r
+terminal feature, which lets full-screen applications leave the\r
+screen exactly the way they found it.\r
+\r
+\dt \cw{pterm.NoRemoteWinTitle}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, it stops the server from remotely controlling the title of\r
+the \cw{pterm} window.\r
+\r
+\dt \cw{pterm.NoRemoteQTitle}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. When\r
+set to 1, it stops the server from remotely requesting the title of\r
+the \cw{pterm} window.\r
+\r
+\lcont{\r
+This feature is a \e{POTENTIAL SECURITY HAZARD}. If a malicious\r
+application can write data to your terminal (for example, if you\r
+merely \cw{cat} a file owned by someone else on the server\r
+machine), it can change your window title (unless you have disabled\r
+this using the \cw{NoRemoteWinTitle} resource) and then use this\r
+service to have the new window title sent back to the server as if\r
+typed at the keyboard. This allows an attacker to fake keypresses\r
+and potentially cause your server-side applications to do things you\r
+didn't want. Therefore this feature is disabled by default, and we\r
+recommend you do not turn it on unless you \e{really} know what\r
+you are doing.\r
+}\r
+\r
+\dt \cw{pterm.NoDBackspace}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0.\r
+When set to 1, it disables the normal action of the Delete (\cw{^?})\r
+character when sent from the server to the terminal, which is to\r
+move the cursor left by one space and erase the character now under\r
+it.\r
+\r
+\dt \cw{pterm.ApplicationCursorKeys}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, the default initial state of the cursor keys are\r
+application mode (where the keys send function-key-like sequences\r
+instead of numbers or arrow keys). When set to 0, the default state\r
+is the normal one.\r
+\r
+\dt \cw{pterm.ApplicationKeypad}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, the default initial state of the numeric keypad is\r
+application mode (where the keys send function-key-like sequences\r
+instead of numbers or arrow keys). When set to 0, the default state\r
+is the normal one.\r
+\r
+\dt \cw{pterm.NetHackKeypad}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, the numeric keypad operates in NetHack mode. This is\r
+equivalent to the \cw{\-nethack} command-line option.\r
+\r
+\dt \cw{pterm.Answerback}\r
+\r
+\dd This option controls the string which the terminal sends in\r
+response to receiving the \cw{^E} character (\q{tell me about\r
+yourself}). By default this string is \q{\cw{PuTTY}}.\r
+\r
+\dt \cw{pterm.HideMousePtr}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+it is set to 1, the mouse pointer will disappear if it is over the\r
+\cw{pterm} window and you press a key. It will reappear as soon as\r
+you move it.\r
+\r
+\dt \cw{pterm.WindowBorder}\r
+\r
+\dd This option controls the number of pixels of space between the text\r
+in the \cw{pterm} window and the window frame. The default is 1.\r
+You can increase this value, but decreasing it to 0 is not\r
+recommended because it can cause the window manager's size hints to\r
+work incorrectly.\r
+\r
+\dt \cw{pterm.CurType}\r
+\r
+\dd This option should be set to either 0, 1 or 2; the default is 0.\r
+When set to 0, the text cursor displayed in the window is a\r
+rectangular block. When set to 1, the cursor is an underline; when\r
+set to 2, it is a vertical line.\r
+\r
+\dt \cw{pterm.BlinkCur}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+it is set to 1, the text cursor will blink when the window is active.\r
+\r
+\dt \cw{pterm.Beep}\r
+\r
+\dd This option should be set to either 0 or 2 (yes, 2); the default\r
+is 0. When it is set to 2, \cw{pterm} will respond to a bell\r
+character (\cw{^G}) by flashing the window instead of beeping.\r
+\r
+\dt \cw{pterm.BellOverload}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+it is set to 1, \cw{pterm} will watch out for large numbers of\r
+bells arriving in a short time and will temporarily disable the bell\r
+until they stop. The idea is that if you \cw{cat} a binary file,\r
+the frantic beeping will mostly be silenced by this feature and will\r
+not drive you crazy.\r
+\r
+\lcont{\r
+The bell overload mode is activated by receiving N bells in time T;\r
+after a further time S without any bells, overload mode will turn\r
+itself off again.\r
+\r
+Bell overload mode is always deactivated by any keypress in the\r
+terminal. This means it can respond to large unexpected streams of\r
+data, but does not interfere with ordinary command-line activities\r
+that generate beeps (such as filename completion).\r
+}\r
+\r
+\dt \cw{pterm.BellOverloadN}\r
+\r
+\dd This option counts the number of bell characters which will activate\r
+bell overload if they are received within a length of time T. The\r
+default is 5.\r
+\r
+\dt \cw{pterm.BellOverloadT}\r
+\r
+\dd This option specifies the time period in which receiving N or more\r
+bells will activate bell overload mode. It is measured in\r
+microseconds, so (for example) set it to 1000000 for one second. The\r
+default is 2000000 (two seconds).\r
+\r
+\dt \cw{pterm.BellOverloadS}\r
+\r
+\dd This option specifies the time period of silence required to turn\r
+off bell overload mode. It is measured in microseconds, so (for\r
+example) set it to 1000000 for one second. The default is 5000000\r
+(five seconds of silence).\r
+\r
+\dt \cw{pterm.ScrollbackLines}\r
+\r
+\dd This option specifies how many lines of scrollback to save above the\r
+visible terminal screen. The default is 200. This resource is\r
+equivalent to the \cw{\-sl} command-line option.\r
+\r
+\dt \cw{pterm.DECOriginMode}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. It\r
+specifies the default state of DEC Origin Mode. (If you don't know\r
+what that means, you probably don't need to mess with it.)\r
+\r
+\dt \cw{pterm.AutoWrapMode}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. It\r
+specifies the default state of auto wrap mode. When set to 1, very\r
+long lines will wrap over to the next line on the terminal; when set\r
+to 0, long lines will be squashed against the right-hand edge of the\r
+screen.\r
+\r
+\dt \cw{pterm.LFImpliesCR}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, the terminal will return the cursor to the left side of\r
+the screen when it receives a line feed character.\r
+\r
+\dt \cw{pterm.WinTitle}\r
+\r
+\dd This resource is the same as the \cw{\-T} command-line option:\r
+it controls the initial title of the window. The default is\r
+\q{\cw{pterm}}.\r
+\r
+\dt \cw{pterm.TermWidth}\r
+\r
+\dd This resource is the same as the width part of the \cw{\-geometry}\r
+command-line option: it controls the number of columns of text in\r
+the window. The default is 80.\r
+\r
+\dt \cw{pterm.TermHeight}\r
+\r
+\dd This resource is the same as the width part of the \cw{\-geometry}\r
+command-line option: it controls the number of columns of text in\r
+the window. The defaults is 24.\r
+\r
+\dt \cw{pterm.Font}\r
+\r
+\dd This resource is the same as the \cw{\-fn} command-line option: it\r
+controls the font used to display normal text. The default is\r
+\q{\cw{fixed}}.\r
+\r
+\dt \cw{pterm.BoldFont}\r
+\r
+\dd This resource is the same as the \cw{\-fb} command-line option: it\r
+controls the font used to display bold text when \cw{BoldAsColour}\r
+is turned off. The default is unset (the font will be bolded by\r
+printing it twice at a one-pixel offset).\r
+\r
+\dt \cw{pterm.WideFont}\r
+\r
+\dd This resource is the same as the \cw{\-fw} command-line option: it\r
+controls the font used to display double-width characters. The\r
+default is unset (double-width characters cannot be displayed).\r
+\r
+\dt \cw{pterm.WideBoldFont}\r
+\r
+\dd This resource is the same as the \cw{\-fwb} command-line option: it\r
+controls the font used to display double-width characters in bold,\r
+when \cw{BoldAsColour} is turned off. The default is unset\r
+(double-width characters are displayed in bold by printing them\r
+twice at a one-pixel offset).\r
+\r
+\dt \cw{pterm.ShadowBoldOffset}\r
+\r
+\dd This resource can be set to an integer; the default is \-1. It\r
+specifies the offset at which text is overprinted when using\r
+\q{shadow bold} mode. The default (1) means that the text will be\r
+printed in the normal place, and also one character to the right;\r
+this seems to work well for most X bitmap fonts, which have a blank\r
+line of pixels down the right-hand side. For some fonts, you may\r
+need to set this to \-1, so that the text is overprinted one pixel\r
+to the left; for really large fonts, you may want to set it higher\r
+than 1 (in one direction or the other).\r
+\r
+\dt \cw{pterm.BoldAsColour}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. It\r
+specifies the default state of auto wrap mode. When set to 1, bold\r
+text is shown by displaying it in a brighter colour; when set to 0,\r
+bold text is shown by displaying it in a heavier font.\r
+\r
+\dt \cw{pterm.Colour0}, \cw{pterm.Colour1}, ..., \cw{pterm.Colour21}\r
+\r
+\dd These options control the various colours used to display text\r
+in the \cw{pterm} window. Each one should be specified as a triple\r
+of decimal numbers giving red, green and blue values: so that black\r
+is \q{\cw{0,0,0}}, white is \q{\cw{255,255,255}}, red is\r
+\q{\cw{255,0,0}} and so on.\r
+\r
+\lcont{\r
+\r
+Colours 0 and 1 specify the foreground colour and its bold\r
+equivalent (the \cw{\-fg} and \cw{\-bfg} command-line options).\r
+Colours 2 and 3 specify the background colour and its bold\r
+equivalent (the \cw{\-bg} and \cw{\-bbg} command-line options).\r
+Colours 4 and 5 specify the text and block colours used for the\r
+cursor (the \cw{\-cfg} and \cw{\-cbg} command-line options). Each\r
+even number from 6 to 20 inclusive specifies the colour to be used\r
+for one of the ANSI primary colour specifications (black, red,\r
+green, yellow, blue, magenta, cyan, white, in that order); the odd\r
+numbers from 7 to 21 inclusive specify the bold version of each\r
+colour, in the same order. The defaults are:\r
+\r
+\c pterm.Colour0: 187,187,187\r
+\c pterm.Colour1: 255,255,255\r
+\c pterm.Colour2: 0,0,0\r
+\c pterm.Colour3: 85,85,85\r
+\c pterm.Colour4: 0,0,0\r
+\c pterm.Colour5: 0,255,0\r
+\c pterm.Colour6: 0,0,0\r
+\c pterm.Colour7: 85,85,85\r
+\c pterm.Colour8: 187,0,0\r
+\c pterm.Colour9: 255,85,85\r
+\c pterm.Colour10: 0,187,0\r
+\c pterm.Colour11: 85,255,85\r
+\c pterm.Colour12: 187,187,0\r
+\c pterm.Colour13: 255,255,85\r
+\c pterm.Colour14: 0,0,187\r
+\c pterm.Colour15: 85,85,255\r
+\c pterm.Colour16: 187,0,187\r
+\c pterm.Colour17: 255,85,255\r
+\c pterm.Colour18: 0,187,187\r
+\c pterm.Colour19: 85,255,255\r
+\c pterm.Colour20: 187,187,187\r
+\c pterm.Colour21: 255,255,255\r
+\r
+}\r
+\r
+\dt \cw{pterm.RectSelect}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 0, dragging the mouse over several lines selects to the end\r
+of each line and from the beginning of the next; when set to 1,\r
+dragging the mouse over several lines selects a rectangular region.\r
+In each case, holding down Alt while dragging gives the other\r
+behaviour.\r
+\r
+\dt \cw{pterm.MouseOverride}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. When\r
+set to 1, if the application requests mouse tracking (so that mouse\r
+clicks are sent to it instead of doing selection), holding down\r
+Shift will revert the mouse to normal selection. When set to 0,\r
+mouse tracking completely disables selection.\r
+\r
+\dt \cw{pterm.Printer}\r
+\r
+\dd This option is unset by default. If you set it, then\r
+server-controlled printing is enabled: the server can send control\r
+sequences to request data to be sent to a printer. That data will be\r
+piped into the command you specify here; so you might want to set it\r
+to \q{\cw{lpr}}, for example, or \q{\cw{lpr \-Pmyprinter}}.\r
+\r
+\dt \cw{pterm.ScrollBar}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. When\r
+set to 0, the scrollbar is hidden (although Shift-PageUp and\r
+Shift-PageDown still work). This is the same as the \cw{\-sb}\r
+command-line option.\r
+\r
+\dt \cw{pterm.ScrollbarOnLeft}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, the scrollbar will be displayed on the left of the\r
+terminal instead of on the right.\r
+\r
+\dt \cw{pterm.ScrollOnKey}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, any keypress causes the position of the scrollback to be\r
+reset to the very bottom.\r
+\r
+\dt \cw{pterm.ScrollOnDisp}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. When\r
+set to 1, any activity in the display causes the position of the\r
+scrollback to be reset to the very bottom.\r
+\r
+\dt \cw{pterm.LineCodePage}\r
+\r
+\dd This option specifies the character set to be used for the session.\r
+This is the same as the \cw{\-cs} command-line option.\r
+\r
+\dt \cw{pterm.NoRemoteCharset}\r
+\r
+\dd This option disables the terminal's ability to change its character\r
+set when it receives escape sequences telling it to. You might need\r
+to do this to interoperate with programs which incorrectly change\r
+the character set to something they think is sensible.\r
+\r
+\dt \cw{pterm.BCE}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. When\r
+set to 1, the various control sequences that erase parts of the\r
+terminal display will erase in whatever the current background\r
+colour is; when set to 0, they will erase in black always.\r
+\r
+\dt \cw{pterm.BlinkText}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 0. When\r
+set to 1, text specified as blinking by the server will actually\r
+blink on and off; when set to 0, \cw{pterm} will use the less\r
+distracting approach of making the text's background colour bold.\r
+\r
+\dt \cw{pterm.StampUtmp}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. When\r
+set to 1, \cw{pterm} will log the login in the various system log\r
+files. This resource is equivalent to the \cw{\-ut} command-line\r
+option.\r
+\r
+\dt \cw{pterm.LoginShell}\r
+\r
+\dd This option should be set to either 0 or 1; the default is 1. When\r
+set to 1, \cw{pterm} will execute your shell as a login shell. This\r
+resource is equivalent to the \cw{\-ls} command-line option.\r
+\r
+\S{pterm-manpage-bugs} BUGS\r
+\r
+Most of the X resources have silly names. (Historical reasons from\r
+PuTTY, mostly.)\r
diff --git a/putty/DOC/MAN-PUTT.BUT b/putty/DOC/MAN-PUTT.BUT
new file mode 100644 (file)
index 0000000..004c88e
--- /dev/null
@@ -0,0 +1,263 @@
+\cfg{man-identity}{putty}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite}\r
+\r
+\H{putty-manpage} Man page for PuTTY\r
+\r
+\S{putty-manpage-name} NAME\r
+\r
+\cw{putty} - GUI SSH, Telnet and Rlogin client for X\r
+\r
+\S{putty-manpage-synopsis} SYNOPSIS\r
+\r
+\c putty [ options ] [ host ]\r
+\e bbbbb   iiiiiii     iiii\r
+\r
+\S{putty-manpage-description} DESCRIPTION\r
+\r
+\cw{putty} is a graphical SSH, Telnet and Rlogin client for X. It is\r
+a direct port of the Windows SSH client of the same name.\r
+\r
+\S{putty-manpage-options} OPTIONS\r
+\r
+The command-line options supported by \cw{putty} are:\r
+\r
+\dt \cw{\-\-display} \e{display\-name}\r
+\r
+\dd Specify the X display on which to open \cw{putty}. (Note this\r
+option has a double minus sign, even though none of the others do.\r
+This is because this option is supplied automatically by GTK.\r
+Sorry.)\r
+\r
+\dt \cw{\-fn} \e{font-name}\r
+\r
+\dd Specify the font to use for normal text displayed in the terminal.\r
+\r
+\dt \cw{\-fb} \e{font-name}\r
+\r
+\dd Specify the font to use for bold text displayed in the terminal.\r
+If the \cw{BoldAsColour} resource is set to 1 (the default), bold\r
+text will be displayed in different colours instead of a different\r
+font, so this option will be ignored. If \cw{BoldAsColour} is set to\r
+0 and you do not specify a bold font, \cw{putty} will overprint the\r
+normal font to make it look bolder.\r
+\r
+\dt \cw{\-fw} \e{font-name}\r
+\r
+\dd Specify the font to use for double-width characters (typically\r
+Chinese, Japanese and Korean text) displayed in the terminal.\r
+\r
+\dt \cw{\-fwb} \e{font-name}\r
+\r
+\dd Specify the font to use for bold double-width characters\r
+(typically Chinese, Japanese and Korean text). Like \cw{-fb}, this\r
+will be ignored unless the \cw{BoldAsColour} resource is set to 0.\r
+\r
+\dt \cw{\-geometry} \e{geometry}\r
+\r
+\dd Specify the size of the terminal, in rows and columns of text.\r
+See \e{X(7)} for more information on the syntax of geometry\r
+specifications.\r
+\r
+\dt \cw{\-sl} \e{lines}\r
+\r
+\dd Specify the number of lines of scrollback to save off the top of the\r
+terminal.\r
+\r
+\dt \cw{\-fg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for normal text.\r
+\r
+\dt \cw{\-bg} \e{colour}\r
+\r
+\dd Specify the background colour to use for normal text.\r
+\r
+\dt \cw{\-bfg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for bold text, if the\r
+\cw{BoldAsColour} resource is set to 1 (the default).\r
+\r
+\dt \cw{\-bbg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for bold reverse-video\r
+text, if the \cw{BoldAsColour} resource is set to 1 (the default).\r
+(This colour is best thought of as the bold version of the\r
+background colour; so it only appears when text is displayed \e{in}\r
+the background colour.)\r
+\r
+\dt \cw{\-cfg} \e{colour}\r
+\r
+\dd Specify the foreground colour to use for text covered by the cursor.\r
+\r
+\dt \cw{\-cbg} \e{colour}\r
+\r
+\dd Specify the background colour to use for text covered by the cursor.\r
+In other words, this is the main colour of the cursor.\r
+\r
+\dt \cw{\-title} \e{title}\r
+\r
+\dd Specify the initial title of the terminal window. (This can be\r
+changed under control of the server.)\r
+\r
+\dt \cw{\-sb\-} or \cw{+sb}\r
+\r
+\dd Tells \cw{putty} not to display a scroll bar.\r
+\r
+\dt \cw{\-sb}\r
+\r
+\dd Tells \cw{putty} to display a scroll bar: this is the opposite of\r
+\cw{\-sb\-}. This is the default option: you will probably only need\r
+to specify it explicitly if you have changed the default using the\r
+\cw{ScrollBar} resource.\r
+\r
+\dt \cw{\-log} \e{filename}\r
+\r
+\dd This option makes \cw{putty} log all the terminal output to a file\r
+as well as displaying it in the terminal.\r
+\r
+\r
+\dt \cw{\-cs} \e{charset}\r
+\r
+\dd This option specifies the character set in which \cw{putty}\r
+should assume the session is operating. This character set will be\r
+used to interpret all the data received from the session, and all\r
+input you type or paste into \cw{putty} will be converted into\r
+this character set before being sent to the session.\r
+\r
+\lcont{ Any character set name which is valid in a MIME header (and\r
+supported by \cw{putty}) should be valid here (examples are\r
+\q{\cw{ISO-8859-1}}, \q{\cw{windows-1252}} or \q{\cw{UTF-8}}). Also,\r
+any character encoding which is valid in an X logical font\r
+description should be valid (\q{\cw{ibm-cp437}}, for example).\r
+\r
+\cw{putty}'s default behaviour is to use the same character\r
+encoding as its primary font. If you supply a Unicode\r
+(\cw{iso10646-1}) font, it will default to the UTF-8 character set.\r
+\r
+Character set names are case-insensitive.\r
+}\r
+\r
+\dt \cw{\-nethack}\r
+\r
+\dd Tells \cw{putty} to enable NetHack keypad mode, in which the\r
+numeric keypad generates the NetHack \c{hjklyubn} direction keys.\r
+This enables you to play NetHack with the numeric keypad without\r
+having to use the NetHack \c{number_pad} option (which requires you\r
+to press \q{\cw{n}} before any repeat count). So you can move with\r
+the numeric keypad, and enter repeat counts with the normal number\r
+keys.\r
+\r
+\dt \cw{\-help}, \cw{\-\-help}\r
+\r
+\dd Display a message summarizing the available options.\r
+\r
+\dt \cw{\-pgpfp}\r
+\r
+\dd Display the fingerprints of the PuTTY PGP Master Keys, to aid\r
+in verifying new files released by the PuTTY team.\r
+\r
+\dt \cw{\-load} \e{session}\r
+\r
+\dd Load a saved session by name. This allows you to run a saved session\r
+straight from the command line without having to go through the\r
+configuration box first.\r
+\r
+\dt \cw{\-ssh}, \cw{\-telnet}, \cw{\-rlogin}, \cw{\-raw}, \cw{\-serial}\r
+\r
+\dd Select the protocol \cw{putty} will use to make the connection.\r
+\r
+\dt \cw{\-l} \e{username}\r
+\r
+\dd Specify the username to use when logging in to the server.\r
+\r
+\dt \cw{\-L} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport}\r
+\r
+\dd Set up a local port forwarding: listen on \e{srcport} (or\r
+\e{srcaddr}:\e{srcport} if specified), and forward any connections\r
+over the SSH connection to the destination address\r
+\e{desthost}:\e{destport}. Only works in SSH.\r
+\r
+\dt \cw{\-R} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport}\r
+\r
+\dd Set up a remote port forwarding: ask the SSH server to listen on\r
+\e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and to\r
+forward any connections back over the SSH connection where the\r
+client will pass them on to the destination address\r
+\e{desthost}:\e{destport}. Only works in SSH.\r
+\r
+\dt \cw{\-D} [\e{srcaddr}:]\e{srcport}\r
+\r
+\dd Set up dynamic port forwarding. The client listens on\r
+\e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and\r
+implements a SOCKS server. So you can point SOCKS-aware applications\r
+at this port and they will automatically use the SSH connection to\r
+tunnel all their connections. Only works in SSH.\r
+\r
+\dt \cw{\-P} \e{port}\r
+\r
+\dd Specify the port to connect to the server on.\r
+\r
+\dt \cw{\-A}, \cw{\-a}\r
+\r
+\dd Enable (\cw{\-A}) or disable (\cw{\-a}) SSH agent forwarding.\r
+Currently this only works with OpenSSH and SSH-1.\r
+\r
+\dt \cw{\-X}, \cw{\-x}\r
+\r
+\dd Enable (\cw{\-X}) or disable (\cw{\-x}) X11 forwarding.\r
+\r
+\dt \cw{\-T}, \cw{\-t}\r
+\r
+\dd Enable (\cw{\-t}) or disable (\cw{\-T}) the allocation of a\r
+pseudo-terminal at the server end.\r
+\r
+\dt \cw{\-C}\r
+\r
+\dd Enable zlib-style compression on the connection.\r
+\r
+\dt \cw{\-1}, \cw{\-2}\r
+\r
+\dd Select SSH protocol version 1 or 2.\r
+\r
+\dt \cw{\-i} \e{keyfile}\r
+\r
+\dd Specify a private key file to use for authentication. For SSH-2\r
+keys, this key file must be in PuTTY's format, not OpenSSH's or\r
+anyone else's.\r
+\r
+\dt \cw{\-sercfg} \e{configuration-string}\r
+\r
+\dd Specify the configuration parameters for the serial port, in\r
+\cw{-serial} mode. \e{configuration-string} should be a\r
+comma-separated list of configuration parameters as follows:\r
+\r
+\lcont{\r
+\r
+\b Any single digit from 5 to 9 sets the number of data bits.\r
+\r
+\b \cq{1}, \cq{1.5} or \cq{2} sets the number of stop bits.\r
+\r
+\b Any other numeric string is interpreted as a baud rate.\r
+\r
+\b A single lower-case letter specifies the parity: \cq{n} for none,\r
+\cq{o} for odd, \cq{e} for even, \cq{m} for mark and \cq{s} for space.\r
+\r
+\b A single upper-case letter specifies the flow control: \cq{N} for\r
+none, \cq{X} for XON/XOFF, \cq{R} for RTS/CTS and \cq{D} for\r
+DSR/DTR.\r
+\r
+}\r
+\r
+\S{putty-manpage-saved-sessions} SAVED SESSIONS\r
+\r
+Saved sessions are stored in a \cw{.putty/sessions} subdirectory in\r
+your home directory.\r
+\r
+\S{putty-manpage-more-information} MORE INFORMATION\r
+\r
+For more information on PuTTY, it's probably best to go and look at\r
+the manual on the web page:\r
+\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/putty/}\r
+\r
+\S{putty-manpage-bugs} BUGS\r
+\r
+This man page isn't terribly complete.\r
diff --git a/putty/DOC/MANCFG.BUT b/putty/DOC/MANCFG.BUT
new file mode 100644 (file)
index 0000000..37723bd
--- /dev/null
@@ -0,0 +1,3 @@
+\cfg{man-mindepth}{2}\r
+\r
+\C{not-shown} Chapter title which is not shown\r
diff --git a/putty/DOC/MANPAGES.BUT b/putty/DOC/MANPAGES.BUT
new file mode 100644 (file)
index 0000000..5fe4a76
--- /dev/null
@@ -0,0 +1,3 @@
+\A{man-pages} Man pages for Unix PuTTY\r
+\r
+This appendix contains all the man pages for Unix PuTTY.\r
diff --git a/putty/DOC/PAGEANT.BUT b/putty/DOC/PAGEANT.BUT
new file mode 100644 (file)
index 0000000..04715ed
--- /dev/null
@@ -0,0 +1,273 @@
+\define{versionidpageant} \versionid $Id: pageant.but 6610 2006-03-14 11:21:59Z jacob $\r
+\r
+\C{pageant} Using \i{Pageant} for authentication\r
+\r
+\cfg{winhelp-topic}{pageant.general}\r
+\r
+Pageant is an SSH \i{authentication agent}. It holds your \i{private key}s\r
+in memory, already decoded, so that you can use them often\r
+\I{passwordless login}without needing to type a \i{passphrase}.\r
+\r
+\H{pageant-start} Getting started with Pageant\r
+\r
+Before you run Pageant, you need to have a private key in \c{*.\i{PPK}}\r
+format. See \k{pubkey} to find out how to generate and use one.\r
+\r
+When you run Pageant, it will put an icon of a computer wearing a\r
+hat into the \ii{System tray}. It will then sit and do nothing, until you\r
+load a private key into it.\r
+\r
+If you click the Pageant icon with the right mouse button, you will\r
+see a menu. Select \q{View Keys} from this menu. The Pageant main\r
+window will appear. (You can also bring this window up by\r
+double-clicking on the Pageant icon.)\r
+\r
+The Pageant window contains a list box. This shows the private keys\r
+Pageant is holding. When you start Pageant, it has no keys, so the\r
+list box will be empty. After you add one or more keys, they will\r
+show up in the list box.\r
+\r
+To add a key to Pageant, press the \q{Add Key} button. Pageant will\r
+bring up a file dialog, labelled \q{Select Private Key File}. Find\r
+your private key file in this dialog, and press \q{Open}.\r
+\r
+Pageant will now load the private key. If the key is protected by a\r
+passphrase, Pageant will ask you to type the passphrase. When the\r
+key has been loaded, it will appear in the list in the Pageant\r
+window.\r
+\r
+Now start PuTTY and open an SSH session to a site that accepts your\r
+key. PuTTY will notice that Pageant is running, retrieve the key\r
+automatically from Pageant, and use it to authenticate. You can now\r
+open as many PuTTY sessions as you like without having to type your\r
+passphrase again.\r
+\r
+(PuTTY can be configured not to try to use Pageant, but it will try\r
+by default. See \k{config-ssh-tryagent} and\r
+\k{using-cmdline-agentauth} for more information.)\r
+\r
+When you want to shut down Pageant, click the right button on the\r
+Pageant icon in the System tray, and select \q{Exit} from the menu.\r
+Closing the Pageant main window does \e{not} shut down Pageant.\r
+\r
+\H{pageant-mainwin} The Pageant main window\r
+\r
+The Pageant main window appears when you left-click on the Pageant\r
+system tray icon, or alternatively right-click and select \q{View\r
+Keys} from the menu. You can use it to keep track of what keys are\r
+currently loaded into Pageant, and to add new ones or remove the\r
+existing keys.\r
+\r
+\S{pageant-mainwin-keylist} The key list box\r
+\r
+\cfg{winhelp-topic}{pageant.keylist}\r
+\r
+The large list box in the Pageant main window lists the private keys\r
+that are currently loaded into Pageant. The list might look\r
+something like this:\r
+\r
+\c ssh1    1024 22:c3:68:3b:09:41:36:c3:39:83:91:ae:71:b2:0f:04 k1\r
+\c ssh-rsa 1023 74:63:08:82:95:75:e1:7c:33:31:bb:cb:00:c0:89:8b k2\r
+\r
+For each key, the list box will tell you:\r
+\r
+\b The type of the key. Currently, this can be \c{ssh1} (an RSA key\r
+for use with the SSH-1 protocol), \c{ssh-rsa} (an RSA key for use\r
+with the SSH-2 protocol), or \c{ssh-dss} (a DSA key for use with\r
+the SSH-2 protocol).\r
+\r
+\b The size (in bits) of the key.\r
+\r
+\b The \I{key fingerprint}fingerprint for the public key. This should be\r
+the same fingerprint given by PuTTYgen, and (hopefully) also the same\r
+fingerprint shown by remote utilities such as \i\c{ssh-keygen} when\r
+applied to your \c{authorized_keys} file.\r
+\r
+\b The comment attached to the key.\r
+\r
+\S{pageant-mainwin-addkey} The \q{Add Key} button\r
+\r
+\cfg{winhelp-topic}{pageant.addkey}\r
+\r
+To add a key to Pageant by reading it out of a local disk file,\r
+press the \q{Add Key} button in the Pageant main window, or\r
+alternatively right-click on the Pageant icon in the system tray and\r
+select \q{Add Key} from there.\r
+\r
+Pageant will bring up a file dialog, labelled \q{Select Private Key\r
+File}. Find your private key file in this dialog, and press\r
+\q{Open}. If you want to add more than one key at once, you can\r
+select multiple files using Shift-click (to select several adjacent\r
+files) or Ctrl-click (to select non-adjacent files).\r
+\r
+Pageant will now load the private key(s). If a key is protected by a\r
+passphrase, Pageant will ask you to type the passphrase.\r
+\r
+(This is not the only way to add a private key to Pageant. You can\r
+also add one from a remote system by using agent forwarding; see\r
+\k{pageant-forward} for details.)\r
+\r
+\S{pageant-mainwin-remkey} The \q{Remove Key} button\r
+\r
+\cfg{winhelp-topic}{pageant.remkey}\r
+\r
+If you need to remove a key from Pageant, select that key in the\r
+list box, and press the \q{Remove Key} button. Pageant will remove\r
+the key from its memory.\r
+\r
+You can apply this to keys you added using the \q{Add Key} button,\r
+or to keys you added remotely using agent forwarding (see\r
+\k{pageant-forward}); it makes no difference.\r
+\r
+\H{pageant-cmdline} The Pageant command line\r
+\r
+Pageant can be made to do things automatically when it starts up, by\r
+\I{command-line arguments}specifying instructions on its command line.\r
+If you're starting Pageant from the Windows GUI, you can arrange this\r
+by editing the properties of the \i{Windows shortcut} that it was\r
+started from.\r
+\r
+If Pageant is already running, invoking it again with the options\r
+below causes actions to be performed with the existing instance, not a\r
+new one.\r
+\r
+\S{pageant-cmdline-loadkey} Making Pageant automatically load keys\r
+on startup\r
+\r
+Pageant can automatically load one or more private keys when it\r
+starts up, if you provide them on the Pageant command line. Your\r
+command line might then look like:\r
+\r
+\c C:\PuTTY\pageant.exe d:\main.ppk d:\secondary.ppk\r
+\r
+If the keys are stored encrypted, Pageant will request the\r
+passphrases on startup.\r
+\r
+If Pageant is already running, this syntax loads keys into the\r
+existing Pageant.\r
+\r
+\S{pageant-cmdline-command} Making Pageant run another program\r
+\r
+You can arrange for Pageant to start another program once it has\r
+initialised itself and loaded any keys specified on its command\r
+line. This program (perhaps a PuTTY, or a WinCVS making use of\r
+Plink, or whatever) will then be able to use the keys Pageant has\r
+loaded.\r
+\r
+You do this by specifying the \I{-c-pageant}\c{-c} option followed\r
+by the command, like this:\r
+\r
+\c C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe\r
+\r
+\H{pageant-forward} Using \i{agent forwarding}\r
+\r
+Agent forwarding is a mechanism that allows applications on your SSH\r
+server machine to talk to the agent on your client machine.\r
+\r
+Note that at present, agent forwarding in SSH-2 is only available\r
+when your SSH server is \i{OpenSSH}. The \i\cw{ssh.com} server uses a\r
+different agent protocol, which PuTTY does not yet support.\r
+\r
+To enable agent forwarding, first start Pageant. Then set up a PuTTY\r
+SSH session in which \q{Allow agent forwarding} is enabled (see\r
+\k{config-ssh-agentfwd}). Open the session as normal. (Alternatively,\r
+you can use the \c{-A} command line option; see\r
+\k{using-cmdline-agent} for details.)\r
+\r
+If this has worked, your applications on the server should now have\r
+access to a Unix domain socket which the SSH server will forward\r
+back to PuTTY, and PuTTY will forward on to the agent. To check that\r
+this has actually happened, you can try this command on Unix server\r
+machines:\r
+\r
+\c unixbox:~$ echo $SSH_AUTH_SOCK\r
+\c /tmp/ssh-XXNP18Jz/agent.28794\r
+\c unixbox:~$\r
+\r
+If the result line comes up blank, agent forwarding has not been\r
+enabled at all.\r
+\r
+Now if you run \c{ssh} on the server and use it to connect through\r
+to another server that accepts one of the keys in Pageant, you\r
+should be able to log in without a password:\r
+\r
+\c unixbox:~$ ssh -v otherunixbox\r
+\c [...]\r
+\c debug: next auth method to try is publickey\r
+\c debug: userauth_pubkey_agent: trying agent key my-putty-key\r
+\c debug: ssh-userauth2 successful: method publickey\r
+\c [...]\r
+\r
+If you enable agent forwarding on \e{that} SSH connection as well\r
+(see the manual for your server-side SSH client to find out how to\r
+do this), your authentication keys will still be available on the\r
+next machine you connect to - two SSH connections away from where\r
+they're actually stored.\r
+\r
+In addition, if you have a private key on one of the SSH servers,\r
+you can send it all the way back to Pageant using the local\r
+\i\c{ssh-add} command:\r
+\r
+\c unixbox:~$ ssh-add ~/.ssh/id_rsa\r
+\c Need passphrase for /home/fred/.ssh/id_rsa\r
+\c Enter passphrase for /home/fred/.ssh/id_rsa:\r
+\c Identity added: /home/fred/.ssh/id_rsa (/home/simon/.ssh/id_rsa)\r
+\c unixbox:~$\r
+\r
+and then it's available to every machine that has agent forwarding\r
+available (not just the ones downstream of the place you added it).\r
+\r
+\H{pageant-security} Security considerations\r
+\r
+\I{security risk}Using Pageant for public-key authentication gives you the\r
+convenience of being able to open multiple SSH sessions without\r
+having to type a passphrase every time, but also gives you the\r
+security benefit of never storing a decrypted private key on disk.\r
+Many people feel this is a good compromise between security and\r
+convenience.\r
+\r
+It \e{is} a compromise, however. Holding your decrypted private keys\r
+in Pageant is better than storing them in easy-to-find disk files,\r
+but still less secure than not storing them anywhere at all. This is\r
+for two reasons:\r
+\r
+\b Windows unfortunately provides no way to protect pieces of memory\r
+from being written to the system \i{swap file}. So if Pageant is holding\r
+your private keys for a long period of time, it's possible that\r
+decrypted private key data may be written to the system swap file,\r
+and an attacker who gained access to your hard disk later on might\r
+be able to recover that data. (However, if you stored an unencrypted\r
+key in a disk file they would \e{certainly} be able to recover it.)\r
+\r
+\b Although, like most modern operating systems, Windows prevents\r
+programs from accidentally accessing one another's memory space, it\r
+does allow programs to access one another's memory space\r
+deliberately, for special purposes such as debugging. This means\r
+that if you allow a virus, trojan, or other malicious program on to\r
+your Windows system while Pageant is running, it could access the\r
+memory of the Pageant process, extract your decrypted authentication\r
+keys, and send them back to its master.\r
+\r
+Similarly, use of agent \e{forwarding} is a security improvement on\r
+other methods of one-touch authentication, but not perfect. Holding\r
+your keys in Pageant on your Windows box has a security advantage\r
+over holding them on the remote server machine itself (either in an\r
+agent or just unencrypted on disk), because if the server machine\r
+ever sees your unencrypted private key then the sysadmin or anyone\r
+who cracks the machine can steal the keys and pretend to be you for\r
+as long as they want.\r
+\r
+However, the sysadmin of the server machine can always pretend to be\r
+you \e{on that machine}. So if you forward your agent to a server\r
+machine, then the sysadmin of that machine can access the forwarded\r
+agent connection and request signatures from your private keys, and\r
+can therefore log in to other machines as you. They can only do this\r
+to a limited extent - when the agent forwarding disappears they lose\r
+the ability - but using Pageant doesn't actually \e{prevent} the\r
+sysadmin (or hackers) on the server from doing this.\r
+\r
+Therefore, if you don't trust the sysadmin of a server machine, you\r
+should \e{never} use agent forwarding to that machine. (Of course\r
+you also shouldn't store private keys on that machine, type\r
+passphrases into it, or log into other machines from it in any way\r
+at all; Pageant is hardly unique in this respect.)\r
diff --git a/putty/DOC/PGPKEYS.BUT b/putty/DOC/PGPKEYS.BUT
new file mode 100644 (file)
index 0000000..87f80db
--- /dev/null
@@ -0,0 +1,141 @@
+\define{versionidpgpkeys} \versionid $Id: pgpkeys.but 5598 2005-04-05 19:36:25Z simon $\r
+\r
+\A{pgpkeys} PuTTY download keys and signatures\r
+\r
+\cfg{winhelp-topic}{pgpfingerprints}\r
+\r
+\I{verifying new versions}We create \i{PGP signatures} for all the PuTTY\r
+files distributed from our web site, so that users can be confident\r
+that the files have not been tampered with. Here we identify\r
+our public keys, and explain our signature policy so you can have an\r
+accurate idea of what each signature guarantees.\r
+This description is provided as both a web page on the PuTTY site, and\r
+an appendix in the PuTTY manual.\r
+\r
+As of release 0.58, all of the PuTTY executables contain fingerprint\r
+material (usually accessed via the \i\c{-pgpfp} command-line\r
+option), such that if you have an executable you trust, you can use\r
+it to establish a trust path, for instance to a newer version\r
+downloaded from the Internet.\r
+\r
+(Note that none of the keys, signatures, etc mentioned here have\r
+anything to do with keys used with SSH - they are purely for verifying\r
+the origin of files distributed by the PuTTY team.)\r
+\r
+\H{pgpkeys-pubkey} Public keys\r
+\r
+We supply two complete sets of keys. We supply a set of RSA keys,\r
+compatible with both \W{http://www.gnupg.org/}{GnuPG} and PGP2,\r
+and also a set of DSA keys compatible with GnuPG.\r
+\r
+In each format, we have three keys:\r
+\r
+\b A Development Snapshots key, used to sign the nightly builds.\r
+\r
+\b A Releases key, used to sign actual releases.\r
+\r
+\b A Master Key. The Master Key is used to sign the other two keys, and\r
+they sign it in return.\r
+\r
+Therefore, we have six public keys in total:\r
+\r
+\b RSA:\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-rsa.asc}{Master Key},\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-rsa.asc}{Release key},\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-rsa.asc}{Snapshot key}\r
+\r
+\lcont{\r
+Master Key: 1024-bit; \I{PGP key fingerprint}fingerprint:\r
+\cw{8F\_15\_97\_DA\_25\_30\_AB\_0D\_\_88\_D1\_92\_54\_11\_CF\_0C\_4C}\r
+}\r
+\r
+\b DSA:\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-dsa.asc}{Master Key},\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-dsa.asc}{Release key},\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-dsa.asc}{Snapshot key}\r
+\r
+\lcont{\r
+Master Key: 1024-bit; fingerprint:\r
+\cw{313C\_3E76\_4B74\_C2C5\_F2AE\_\_83A8\_4F5E\_6DF5\_6A93\_B34E}\r
+}\r
+\r
+\H{pgpkeys-security} Security details\r
+\r
+The various keys have various different security levels. This\r
+section explains what those security levels are, and how far you can\r
+expect to trust each key.\r
+\r
+\S{pgpkeys-snapshot} The Development Snapshots keys\r
+\r
+These keys are stored \e{without passphrases}. This is\r
+necessary, because the snapshots are generated every night without\r
+human intervention, so nobody would be able to type a passphrase.\r
+\r
+The actual snapshots are built on a team member's home Windows box.\r
+The keys themselves are stored on an independently run Unix box\r
+(the same one that hosts our Subversion repository). After\r
+being built, the binaries are uploaded to this Unix box and then\r
+signed automatically.\r
+\r
+Therefore, a signature from one of the Development Snapshots keys\r
+\e{DOES} protect you against:\r
+\r
+\b People tampering with the PuTTY binaries between the PuTTY web site\r
+and you.\r
+\r
+But it \e{DOES NOT} protect you against:\r
+\r
+\b People tampering with the binaries before they are uploaded to the\r
+independent Unix box.\r
+\r
+\b The sysadmin of the independent Unix box using his root privilege to\r
+steal the private keys and abuse them, or tampering with the\r
+binaries before they are signed.\r
+\r
+\b Somebody getting root on the Unix box.\r
+\r
+Of course, we don't believe any of those things is very likely. We\r
+know our sysadmin personally and trust him (both to be competent and\r
+to be non-malicious), and we take all reasonable precautions to\r
+guard the build machine. But when you see a signature, you should\r
+always be certain of precisely what it guarantees and precisely what\r
+it does not.\r
+\r
+\S{pgpkeys-release} The Releases keys\r
+\r
+The Release keys have passphrases and we can be more careful about\r
+how we use them.\r
+\r
+The Release keys are kept safe on the developers' own local\r
+machines, and only used to sign releases that have been built by\r
+hand. A signature from a Release key protects you from almost any\r
+plausible attack.\r
+\r
+(Some of the developers' machines have cable modem connections and\r
+might in theory be crackable, but of course the private keys are\r
+still encrypted, so the crack would have to go unnoticed for long\r
+enough to steal a passphrase.)\r
+\r
+\S{pgpkeys-master} The Master Keys\r
+\r
+The Master Keys sign almost nothing. Their purpose is to bind the\r
+other keys together and certify that they are all owned by the same\r
+people and part of the same integrated setup. The only signatures\r
+produced by the Master Keys, \e{ever}, should be the signatures\r
+on the other keys.\r
+\r
+We intend to arrange for the Master Keys to sign each other, to\r
+certify that the DSA keys and RSA keys are part of the same setup.\r
+We have not yet got round to this at the time of writing.\r
+\r
+We have collected a few third-party signatures on the Master Keys,\r
+in order to increase the chances that you can find a suitable trust\r
+path to them. We intend to collect more. (Note that the keys on the\r
+keyservers appear to have also collected some signatures from people\r
+who haven't performed any verification of the Master Keys.) \r
+\r
+We have uploaded our various keys to public keyservers, so that\r
+even if you don't know any of the people who have signed our\r
+keys, you can still be reasonably confident that an attacker would\r
+find it hard to substitute fake keys on all the public keyservers at\r
+once.\r
diff --git a/putty/DOC/PLINK.BUT b/putty/DOC/PLINK.BUT
new file mode 100644 (file)
index 0000000..bb293c2
--- /dev/null
@@ -0,0 +1,296 @@
+\define{versionidplink} \versionid $Id: plink.but 9202 2011-07-12 18:26:18Z simon $\r
+\r
+\C{plink} Using the command-line connection tool \i{Plink}\r
+\r
+\i{Plink} (PuTTY Link) is a command-line connection tool similar to\r
+UNIX \c{ssh}. It is mostly used for \i{automated operations}, such as\r
+making CVS access a repository on a remote server.\r
+\r
+Plink is probably not what you want if you want to run an\r
+\i{interactive session} in a console window.\r
+\r
+\H{plink-starting} Starting Plink\r
+\r
+Plink is a command line application. This means that you cannot just\r
+double-click on its icon to run it and instead you have to bring up\r
+a \i{console window}. In Windows 95, 98, and ME, this is called an\r
+\q{MS-DOS Prompt}, and in Windows NT, 2000, and XP, it is called a\r
+\q{Command Prompt}. It should be available from the Programs section\r
+of your Start Menu.\r
+\r
+In order to use Plink, the file \c{plink.exe} will need either to be\r
+on your \i{\c{PATH}} or in your current directory. To add the\r
+directory containing Plink to your \c{PATH} environment variable,\r
+type into the console window:\r
+\r
+\c set PATH=C:\path\to\putty\directory;%PATH%\r
+\r
+This will only work for the lifetime of that particular console\r
+window.  To set your \c{PATH} more permanently on Windows NT, 2000,\r
+and XP, use the Environment tab of the System Control Panel.  On\r
+Windows 95, 98, and ME, you will need to edit your \i\c{AUTOEXEC.BAT}\r
+to include a \c{set} command like the one above.\r
+\r
+\H{plink-usage} Using Plink\r
+\r
+This section describes the basics of how to use Plink for\r
+interactive logins and for automated processes.\r
+\r
+Once you've got a console window to type into, you can just type\r
+\c{plink} on its own to bring up a usage message.  This tells you the\r
+version of Plink you're using, and gives you a brief summary of how to\r
+use Plink:\r
+\r
+\c Z:\sysosd>plink\r
+\c PuTTY Link: command-line connection utility\r
+\c Release 0.61\r
+\c Usage: plink [options] [user@]host [command]\r
+\c        ("host" can also be a PuTTY saved session name)\r
+\c Options:\r
+\c   -V        print version information and exit\r
+\c   -pgpfp    print PGP key fingerprints and exit\r
+\c   -v        show verbose messages\r
+\c   -load sessname  Load settings from saved session\r
+\c   -ssh -telnet -rlogin -raw -serial\r
+\c             force use of a particular protocol\r
+\c   -P port   connect to specified port\r
+\c   -l user   connect with specified username\r
+\c   -batch    disable all interactive prompts\r
+\c The following options only apply to SSH connections:\r
+\c   -pw passw login with specified password\r
+\c   -D [listen-IP:]listen-port\r
+\c             Dynamic SOCKS-based port forwarding\r
+\c   -L [listen-IP:]listen-port:host:port\r
+\c             Forward local port to remote address\r
+\c   -R [listen-IP:]listen-port:host:port\r
+\c             Forward remote port to local address\r
+\c   -X -x     enable / disable X11 forwarding\r
+\c   -A -a     enable / disable agent forwarding\r
+\c   -t -T     enable / disable pty allocation\r
+\c   -1 -2     force use of particular protocol version\r
+\c   -4 -6     force use of IPv4 or IPv6\r
+\c   -C        enable compression\r
+\c   -i key    private key file for authentication\r
+\c   -noagent  disable use of Pageant\r
+\c   -agent    enable use of Pageant\r
+\c   -m file   read remote command(s) from file\r
+\c   -s        remote command is an SSH subsystem (SSH-2 only)\r
+\c   -N        don't start a shell/command (SSH-2 only)\r
+\c   -nc host:port\r
+\c             open tunnel in place of session (SSH-2 only)\r
+\c   -sercfg configuration-string (e.g. 19200,8,n,1,X)\r
+\c             Specify the serial configuration (serial only)\r
+\r
+Once this works, you are ready to use Plink.\r
+\r
+\S{plink-usage-interactive} Using Plink for interactive logins\r
+\r
+To make a simple interactive connection to a remote server, just\r
+type \c{plink} and then the host name:\r
+\r
+\c Z:\sysosd>plink login.example.com\r
+\c\r
+\c Debian GNU/Linux 2.2 flunky.example.com\r
+\c flunky login:\r
+\r
+You should then be able to log in as normal and run a session. The\r
+output sent by the server will be written straight to your command\r
+prompt window, which will most likely not interpret terminal \i{control\r
+codes} in the way the server expects it to. So if you run any\r
+full-screen applications, for example, you can expect to see strange\r
+characters appearing in your window. Interactive connections like\r
+this are not the main point of Plink.\r
+\r
+In order to connect with a different protocol, you can give the\r
+command line options \c{-ssh}, \c{-telnet}, \c{-rlogin} or \c{-raw}.\r
+To make an SSH connection, for example:\r
+\r
+\c Z:\sysosd>plink -ssh login.example.com\r
+\c login as:\r
+\r
+If you have already set up a PuTTY saved session, then instead of\r
+supplying a host name, you can give the saved session name. This\r
+allows you to use public-key authentication, specify a user name,\r
+and use most of the other features of PuTTY:\r
+\r
+\c Z:\sysosd>plink my-ssh-session\r
+\c Sent username "fred"\r
+\c Authenticating with public key "fred@winbox"\r
+\c Last login: Thu Dec  6 19:25:33 2001 from :0.0\r
+\c fred@flunky:~$\r
+\r
+(You can also use the \c{-load} command-line option to load a saved\r
+session; see \k{using-cmdline-load}. If you use \c{-load}, the saved\r
+session exists, and it specifies a hostname, you cannot also specify a\r
+\c{host} or \c{user@host} argument - it will be treated as part of the\r
+remote command.)\r
+\r
+\S{plink-usage-batch} Using Plink for automated connections\r
+\r
+More typically Plink is used with the SSH protocol, to enable you to\r
+talk directly to a program running on the server. To do this you\r
+have to ensure Plink is \e{using} the SSH protocol. You can do this\r
+in several ways:\r
+\r
+\b Use the \c{-ssh} option as described in\r
+\k{plink-usage-interactive}.\r
+\r
+\b Set up a PuTTY saved session that describes the server you are\r
+connecting to, and that also specifies the protocol as SSH.\r
+\r
+\b Set the Windows environment variable \i\c{PLINK_PROTOCOL} to the\r
+word \c{ssh}.\r
+\r
+Usually Plink is not invoked directly by a user, but run\r
+automatically by another process. Therefore you typically do not\r
+want Plink to prompt you for a user name or a password.\r
+\r
+Next, you are likely to need to avoid the various interactive\r
+prompts Plink can produce. You might be prompted to verify the host\r
+key of the server you're connecting to, to enter a user name, or to\r
+enter a password.\r
+\r
+To avoid being prompted for the server host key when using Plink for\r
+an automated connection, you should first make a \e{manual}\r
+connection (using either of PuTTY or Plink) to the same server,\r
+verify the host key (see \k{gs-hostkey} for more information), and\r
+select Yes to add the host key to the Registry. After that, Plink\r
+commands connecting to that server should not give a host key prompt\r
+unless the host key changes.\r
+\r
+To avoid being prompted for a user name, you can:\r
+\r
+\b Use the \c{-l} option to specify a user name on the command line.\r
+For example, \c{plink login.example.com -l fred}.\r
+\r
+\b Set up a PuTTY saved session that describes the server you are\r
+connecting to, and that also specifies the username to log in as\r
+(see \k{config-username}).\r
+\r
+To avoid being prompted for a password, you should almost certainly\r
+set up \i{public-key authentication}. (See \k{pubkey} for a general\r
+introduction to public-key authentication.) Again, you can do this\r
+in two ways:\r
+\r
+\b Set up a PuTTY saved session that describes the server you are\r
+connecting to, and that also specifies a private key file (see\r
+\k{config-ssh-privkey}). For this to work without prompting, your\r
+private key will need to have no passphrase.\r
+\r
+\b Store the private key in Pageant. See \k{pageant} for further\r
+information.\r
+\r
+Once you have done all this, you should be able to run a remote\r
+command on the SSH server machine and have it execute automatically\r
+with no prompting:\r
+\r
+\c Z:\sysosd>plink login.example.com -l fred echo hello, world\r
+\c hello, world\r
+\c\r
+\c Z:\sysosd>\r
+\r
+Or, if you have set up a saved session with all the connection\r
+details:\r
+\r
+\c Z:\sysosd>plink mysession echo hello, world\r
+\c hello, world\r
+\c\r
+\c Z:\sysosd>\r
+\r
+Then you can set up other programs to run this Plink command and\r
+talk to it as if it were a process on the server machine.\r
+\r
+\S{plink-options} Plink command line options\r
+\r
+Plink accepts all the general command line options supported by the\r
+PuTTY tools. See \k{using-general-opts} for a description of these\r
+options.\r
+\r
+Plink also supports some of its own options. The following sections\r
+describe Plink's specific command-line options.\r
+\r
+\S2{plink-option-batch} \I{-batch-plink}\c{-batch}: disable all\r
+interactive prompts\r
+\r
+If you use the \c{-batch} option, Plink will never give an\r
+interactive prompt while establishing the connection. If the\r
+server's host key is invalid, for example (see \k{gs-hostkey}), then\r
+the connection will simply be abandoned instead of asking you what\r
+to do next.\r
+\r
+This may help Plink's behaviour when it is used in automated\r
+scripts: using \c{-batch}, if something goes wrong at connection\r
+time, the batch job will fail rather than hang.\r
+\r
+\S2{plink-option-s} \I{-s-plink}\c{-s}: remote command is SSH subsystem\r
+\r
+If you specify the \c{-s} option, Plink passes the specified command\r
+as the name of an SSH \q{\i{subsystem}} rather than an ordinary command\r
+line.\r
+\r
+(This option is only meaningful with the SSH-2 protocol.)\r
+\r
+\H{plink-batch} Using Plink in \i{batch files} and \i{scripts}\r
+\r
+Once you have set up Plink to be able to log in to a remote server\r
+without any interactive prompting (see \k{plink-usage-batch}), you\r
+can use it for lots of scripting and batch purposes. For example, to\r
+start a backup on a remote machine, you might use a command like:\r
+\r
+\c plink root@myserver /etc/backups/do-backup.sh\r
+\r
+Or perhaps you want to fetch all system log lines relating to a\r
+particular web area:\r
+\r
+\c plink mysession grep /~fred/ /var/log/httpd/access.log > fredlog\r
+\r
+Any non-interactive command you could usefully run on the server\r
+command line, you can run in a batch file using Plink in this way.\r
+\r
+\H{plink-cvs} Using Plink with \i{CVS}\r
+\r
+To use Plink with CVS, you need to set the environment variable\r
+\i\c{CVS_RSH} to point to Plink:\r
+\r
+\c set CVS_RSH=\path\to\plink.exe\r
+\r
+You also need to arrange to be able to connect to a remote host\r
+without any interactive prompts, as described in\r
+\k{plink-usage-batch}.\r
+\r
+You should then be able to run CVS as follows:\r
+\r
+\c cvs -d :ext:user@sessionname:/path/to/repository co module\r
+\r
+If you specified a username in your saved session, you don't even\r
+need to specify the \q{user} part of this, and you can just say:\r
+\r
+\c cvs -d :ext:sessionname:/path/to/repository co module\r
+\r
+\H{plink-wincvs} Using Plink with \i{WinCVS}\r
+\r
+Plink can also be used with WinCVS.  Firstly, arrange for Plink to be\r
+able to connect to a remote host non-interactively, as described in\r
+\k{plink-usage-batch}.\r
+\r
+Then, in WinCVS, bring up the \q{Preferences} dialogue box from the\r
+\e{Admin} menu, and switch to the \q{Ports} tab. Tick the box there\r
+labelled \q{Check for an alternate \cw{rsh} name} and in the text\r
+entry field to the right enter the full path to \c{plink.exe}.\r
+Select \q{OK} on the \q{Preferences} dialogue box.\r
+\r
+Next, select \q{Command Line} from the WinCVS \q{Admin} menu, and type \r
+a CVS command as in \k{plink-cvs}, for example:\r
+\r
+\c cvs -d :ext:user@hostname:/path/to/repository co module\r
+\r
+or (if you're using a saved session):\r
+\r
+\c cvs -d :ext:user@sessionname:/path/to/repository co module\r
+\r
+Select the folder you want to check out to with the \q{Change Folder}\r
+button, and click \q{OK} to check out your module.  Once you've got\r
+modules checked out, WinCVS will happily invoke plink from the GUI for\r
+CVS operations.\r
+\r
+\# \H{plink-whatelse} Using Plink with... ?\r
diff --git a/putty/DOC/PSCP.BUT b/putty/DOC/PSCP.BUT
new file mode 100644 (file)
index 0000000..14064a9
--- /dev/null
@@ -0,0 +1,318 @@
+\define{versionidpscp} \versionid $Id: pscp.but 9202 2011-07-12 18:26:18Z simon $\r
+\r
+\#FIXME: Need examples\r
+\r
+\C{pscp} Using \i{PSCP} to transfer files securely\r
+\r
+\i{PSCP}, the PuTTY Secure Copy client, is a tool for \i{transferring files}\r
+securely between computers using an SSH connection.\r
+\r
+If you have an SSH-2 server, you might prefer PSFTP (see \k{psftp})\r
+for interactive use. PSFTP does not in general work with SSH-1\r
+servers, however.\r
+\r
+\H{pscp-starting} Starting PSCP\r
+\r
+PSCP is a command line application.  This means that you cannot just\r
+double-click on its icon to run it and instead you have to bring up a\r
+\i{console window}.  With Windows 95, 98, and ME, this is called an\r
+\q{MS-DOS Prompt} and with Windows NT, 2000, and XP, it is called a\r
+\q{Command Prompt}.  It should be available from the Programs section\r
+of your \i{Start Menu}.\r
+\r
+To start PSCP it will need either to be on your \i{\c{PATH}} or in your\r
+current directory.  To add the directory containing PSCP to your\r
+\c{PATH} environment variable, type into the console window:\r
+\r
+\c set PATH=C:\path\to\putty\directory;%PATH%\r
+\r
+This will only work for the lifetime of that particular console\r
+window.  To set your \c{PATH} more permanently on Windows NT, 2000,\r
+and XP, use the Environment tab of the System Control Panel.  On\r
+Windows 95, 98, and ME, you will need to edit your \i\c{AUTOEXEC.BAT}\r
+to include a \c{set} command like the one above.\r
+\r
+\H{pscp-usage} PSCP Usage\r
+\r
+Once you've got a console window to type into, you can just type\r
+\c{pscp} on its own to bring up a usage message.  This tells you the\r
+version of PSCP you're using, and gives you a brief summary of how to\r
+use PSCP:\r
+\r
+\c Z:\owendadmin>pscp\r
+\c PuTTY Secure Copy client\r
+\c Release 0.61\r
+\c Usage: pscp [options] [user@]host:source target\r
+\c        pscp [options] source [source...] [user@]host:target\r
+\c        pscp [options] -ls [user@]host:filespec\r
+\c Options:\r
+\c   -V        print version information and exit\r
+\c   -pgpfp    print PGP key fingerprints and exit\r
+\c   -p        preserve file attributes\r
+\c   -q        quiet, don't show statistics\r
+\c   -r        copy directories recursively\r
+\c   -v        show verbose messages\r
+\c   -load sessname  Load settings from saved session\r
+\c   -P port   connect to specified port\r
+\c   -l user   connect with specified username\r
+\c   -pw passw login with specified password\r
+\c   -1 -2     force use of particular SSH protocol version\r
+\c   -4 -6     force use of IPv4 or IPv6\r
+\c   -C        enable compression\r
+\c   -i key    private key file for authentication\r
+\c   -noagent  disable use of Pageant\r
+\c   -agent    enable use of Pageant\r
+\c   -batch    disable all interactive prompts\r
+\c   -unsafe   allow server-side wildcards (DANGEROUS)\r
+\c   -sftp     force use of SFTP protocol\r
+\c   -scp      force use of SCP protocol\r
+\r
+(PSCP's interface is much like the Unix \c{scp} command, if you're\r
+familiar with that.)\r
+\r
+\S{pscp-usage-basics} The basics\r
+\r
+To \I{receiving files}receive (a) file(s) from a remote server: \r
+\r
+\c pscp [options] [user@]host:source target\r
+\r
+So to copy the file \c{/etc/hosts} from the server \c{example.com} as\r
+user \c{fred} to the file \c{c:\\temp\\example-hosts.txt}, you would type:\r
+\r
+\c pscp fred@example.com:/etc/hosts c:\temp\example-hosts.txt\r
+\r
+To \I{sending files}send (a) file(s) to a remote server: \r
+\r
+\c pscp [options] source [source...] [user@]host:target\r
+\r
+So to copy the local file \c{c:\\documents\\foo.txt} to the server\r
+\c{example.com} as user \c{fred} to the file \c{/tmp/foo} you would\r
+type:\r
+\r
+\c pscp c:\documents\foo.txt fred@example.com:/tmp/foo\r
+\r
+You can use \i{wildcards} to transfer multiple files in either\r
+direction, like this:\r
+\r
+\c pscp c:\documents\*.doc fred@example.com:docfiles\r
+\c pscp fred@example.com:source/*.c c:\source\r
+\r
+However, in the second case (using a wildcard for multiple remote\r
+files) you may see a warning saying something like \q{warning:\r
+remote host tried to write to a file called \cq{terminal.c} when we\r
+requested a file called \cq{*.c}. If this is a wildcard, consider\r
+upgrading to SSH-2 or using the \cq{-unsafe} option. Renaming of\r
+this file has been disallowed}.\r
+\r
+This is due to a \I{security risk}fundamental insecurity in the old-style\r
+\i{SCP protocol}: the client sends the wildcard string (\c{*.c}) to the\r
+server, and the server sends back a sequence of file names that\r
+match the wildcard pattern. However, there is nothing to stop the\r
+server sending back a \e{different} pattern and writing over one of\r
+your other files: if you request \c{*.c}, the server might send back\r
+the file name \c{AUTOEXEC.BAT} and install a virus for you. Since\r
+the wildcard matching rules are decided by the server, the client\r
+cannot reliably verify that the filenames sent back match the\r
+pattern.\r
+\r
+PSCP will attempt to use the newer \i{SFTP} protocol (part of SSH-2)\r
+where possible, which does not suffer from this security flaw. If\r
+you are talking to an SSH-2 server which supports SFTP, you will\r
+never see this warning. (You can force use of the SFTP protocol,\r
+if available, with \c{-sftp} - see \k{pscp-usage-options-backend}.)\r
+\r
+If you really need to use a server-side wildcard with an SSH-1\r
+server, you can use the \i\c{-unsafe} command line option with PSCP:\r
+\r
+\c pscp -unsafe fred@example.com:source/*.c c:\source\r
+\r
+This will suppress the warning message and the file transfer will\r
+happen. However, you should be aware that by using this option you\r
+are giving the server the ability to write to \e{any} file in the\r
+target directory, so you should only use this option if you trust\r
+the server administrator not to be malicious (and not to let the\r
+server machine be cracked by malicious people). Alternatively, do\r
+any such download in a newly created empty directory. (Even in\r
+\q{unsafe} mode, PSCP will still protect you against the server\r
+trying to get out of that directory using pathnames including\r
+\cq{..}.)\r
+\r
+\S2{pscp-usage-basics-user} \c{user}\r
+\r
+The \i{login name} on the remote server. If this is omitted, and \c{host}\r
+is a PuTTY saved session, PSCP will use any username specified by that \r
+saved session.  Otherwise, PSCP will attempt to use the local Windows\r
+username.\r
+\r
+\S2{pscp-usage-basics-host} \I{hostname}\c{host}\r
+\r
+The name of the remote server, or the name of an existing PuTTY saved\r
+session. In the latter case, the session's settings for hostname, port\r
+number, cipher type and username will be used.\r
+\r
+\S2{pscp-usage-basics-source} \c{source}\r
+\r
+One or more source files. \ii{Wildcards} are allowed.  The syntax of\r
+wildcards depends on the system to which they apply, so if you are\r
+copying \e{from} a Windows system \e{to} a UNIX system, you should use \r
+Windows wildcard syntax (e.g. \c{*.*}), but if you are copying \e{from} \r
+a UNIX system \e{to} a Windows system, you would use the wildcard\r
+syntax allowed by your UNIX shell (e.g. \c{*}).\r
+\r
+If the source is a remote server and you do not specify a full\r
+pathname (in UNIX, a pathname beginning with a \c{/} (slash)\r
+character), what you specify as a source will be interpreted relative\r
+to your \i{home directory} on the remote server.\r
+\r
+\S2{pscp-usage-basics-target} \c{target}\r
+\r
+The filename or directory to put the file(s).  When copying from a\r
+remote server to a local host, you may wish simply to place the\r
+file(s) in the current directory.  To do this, you should specify a\r
+target of \c{.}.  For example:\r
+\r
+\c pscp fred@example.com:/home/tom/.emacs .\r
+\r
+...would copy \c{/home/tom/.emacs} on the remote server to the current \r
+directory.\r
+\r
+As with the \c{source} parameter, if the target is on a remote server\r
+and is not a full path name, it is interpreted relative to your home\r
+directory on the remote server.\r
+\r
+\S{pscp-usage-options} Options\r
+\r
+PSCP accepts all the general command line options supported by the\r
+PuTTY tools, except the ones which make no sense in a file transfer\r
+utility. See \k{using-general-opts} for a description of these\r
+options. (The ones not supported by PSCP are clearly marked.)\r
+\r
+PSCP also supports some of its own options. The following sections\r
+describe PSCP's specific command-line options.\r
+\r
+\S2{pscp-usage-options-ls}\I{-ls-PSCP}\c{-ls} \I{listing files}list remote files\r
+\r
+If the \c{-ls} option is given, no files are transferred; instead,\r
+remote files are listed. Only a hostname specification and\r
+optional remote file specification need be given. For example:\r
+\r
+\c pscp -ls fred@example.com:dir1\r
+\r
+The SCP protocol does not contain within itself a means of listing\r
+files. If SCP is in use, this option therefore assumes that the\r
+server responds appropriately to the command \c{ls\_-la};\r
+this may not work with all servers.\r
+\r
+If SFTP is in use, this option should work with all servers.\r
+\r
+\S2{pscp-usage-options-p}\I{-p-PSCP}\c{-p} \i{preserve file attributes}\r
+\r
+By default, files copied with PSCP are \i{timestamp}ed with the date and\r
+time they were copied.  The \c{-p} option preserves the original\r
+timestamp on copied files.\r
+\r
+\S2{pscp-usage-options-q}\I{-q-PSCP}\c{-q} quiet, don't show \i{statistics}\r
+\r
+By default, PSCP displays a meter displaying the progress of the\r
+current transfer:\r
+\r
+\c mibs.tar          |   168 kB |  84.0 kB/s | ETA: 00:00:13 |  13%\r
+\r
+The fields in this display are (from left to right), filename, size\r
+(in kilobytes) of file transferred so far, estimate of how fast the\r
+file is being transferred (in kilobytes per second), estimated time\r
+that the transfer will be complete, and percentage of the file so far\r
+transferred.  The \c{-q} option to PSCP suppresses the printing of\r
+these statistics.\r
+\r
+\S2{pscp-usage-options-r}\I{-r-PSCP}\c{-r} copies directories \i{recursive}ly\r
+\r
+By default, PSCP will only copy files.  Any directories you specify to\r
+copy will be skipped, as will their contents.  The \c{-r} option tells\r
+PSCP to descend into any directories you specify, and to copy them and \r
+their contents.  This allows you to use PSCP to transfer whole\r
+directory structures between machines.\r
+\r
+\S2{pscp-usage-options-batch}\I{-batch-PSCP}\c{-batch} avoid interactive prompts\r
+\r
+If you use the \c{-batch} option, PSCP will never give an\r
+interactive prompt while establishing the connection. If the\r
+server's host key is invalid, for example (see \k{gs-hostkey}), then\r
+the connection will simply be abandoned instead of asking you what\r
+to do next.\r
+\r
+This may help PSCP's behaviour when it is used in automated\r
+scripts: using \c{-batch}, if something goes wrong at connection\r
+time, the batch job will fail rather than hang.\r
+\r
+\S2{pscp-usage-options-backend}\i\c{-sftp}, \i\c{-scp} force use of\r
+particular protocol\r
+\r
+As mentioned in \k{pscp-usage-basics}, there are two different file\r
+transfer protocols in use with SSH. Despite its name, PSCP (like many\r
+other ostensible \cw{scp} clients) can use either of these protocols.\r
+\r
+The older \i{SCP protocol} does not have a written specification and\r
+leaves a lot of detail to the server platform. \ii{Wildcards} are expanded\r
+on the server. The simple design means that any wildcard specification\r
+supported by the server platform (such as brace expansion) can be\r
+used, but also leads to interoperability issues such as with filename\r
+quoting (for instance, where filenames contain spaces), and also the\r
+security issue described in \k{pscp-usage-basics}.\r
+\r
+The newer \i{SFTP} protocol, which is usually associated with SSH-2\r
+servers, is specified in a more platform independent way, and leaves\r
+issues such as wildcard syntax up to the client. (PuTTY's SFTP\r
+wildcard syntax is described in \k{psftp-wildcards}.) This makes it\r
+more consistent across platforms, more suitable for scripting and\r
+automation, and avoids security issues with wildcard matching.\r
+\r
+Normally PSCP will attempt to use the SFTP protocol, and only fall\r
+back to the SCP protocol if SFTP is not available on the server.\r
+\r
+The \c{-scp} option forces PSCP to use the SCP protocol or quit.\r
+\r
+The \c{-sftp} option forces PSCP to use the SFTP protocol or quit.\r
+When this option is specified, PSCP looks harder for an SFTP server,\r
+which may allow use of SFTP with SSH-1 depending on server setup.\r
+\r
+\S{pscp-retval} \ii{Return value}\r
+\r
+PSCP returns an \i\cw{ERRORLEVEL} of zero (success) only if the files\r
+were correctly transferred. You can test for this in a \i{batch file},\r
+using code such as this:\r
+\r
+\c pscp file*.* user@hostname:\r
+\c if errorlevel 1 echo There was an error\r
+\r
+\S{pscp-pubkey} Using \i{public key authentication} with PSCP\r
+\r
+Like PuTTY, PSCP can authenticate using a public key instead of a\r
+password. There are three ways you can do this.\r
+\r
+Firstly, PSCP can use PuTTY saved sessions in place of hostnames\r
+(see \k{pscp-usage-basics-host}). So you would do this:\r
+\r
+\b Run PuTTY, and create a PuTTY saved session (see\r
+\k{config-saving}) which specifies your private key file (see\r
+\k{config-ssh-privkey}). You will probably also want to specify a\r
+username to log in as (see \k{config-username}).\r
+\r
+\b In PSCP, you can now use the name of the session instead of a\r
+hostname: type \c{pscp sessionname:file localfile}, where\r
+\c{sessionname} is replaced by the name of your saved session.\r
+\r
+Secondly, you can supply the name of a private key file on the command\r
+line, with the \c{-i} option. See \k{using-cmdline-identity} for more\r
+information.\r
+\r
+Thirdly, PSCP will attempt to authenticate using Pageant if Pageant\r
+is running (see \k{pageant}). So you would do this:\r
+\r
+\b Ensure Pageant is running, and has your private key stored in it.\r
+\r
+\b Specify a user and host name to PSCP as normal. PSCP will\r
+automatically detect Pageant and try to use the keys within it.\r
+\r
+For more general information on public-key authentication, see\r
+\k{pubkey}.\r
diff --git a/putty/DOC/PSFTP.BUT b/putty/DOC/PSFTP.BUT
new file mode 100644 (file)
index 0000000..b7730fb
--- /dev/null
@@ -0,0 +1,593 @@
+\define{versionidpsftp} \versionid $Id: psftp.but 8325 2008-11-24 18:19:55Z jacob $\r
+\r
+\C{psftp} Using \i{PSFTP} to transfer files securely\r
+\r
+\i{PSFTP}, the PuTTY SFTP client, is a tool for \i{transferring files}\r
+securely between computers using an SSH connection.\r
+\r
+PSFTP differs from PSCP in the following ways:\r
+\r
+\b PSCP should work on virtually every SSH server. PSFTP uses the\r
+new \i{SFTP} protocol, which is a feature of SSH-2 only. (PSCP will also\r
+use this protocol if it can, but there is an SSH-1 equivalent it can\r
+fall back to if it cannot.)\r
+\r
+\b PSFTP allows you to run an interactive file transfer session,\r
+much like the Windows \i\c{ftp} program. You can list the contents of\r
+directories, browse around the file system, issue multiple \c{get}\r
+and \c{put} commands, and eventually log out. By contrast, PSCP is\r
+designed to do a single file transfer operation and immediately\r
+terminate.\r
+\r
+\H{psftp-starting} Starting PSFTP\r
+\r
+The usual way to start PSFTP is from a command prompt, much like\r
+PSCP. To do this, it will need either to be on your \i{\c{PATH}} or\r
+in your current directory.  To add the directory containing PSFTP to\r
+your \c{PATH} environment variable, type into the console window:\r
+\r
+\c set PATH=C:\path\to\putty\directory;%PATH%\r
+\r
+Unlike PSCP, however, PSFTP has no complex command-line syntax; you\r
+just specify a host name and perhaps a user name:\r
+\r
+\c psftp server.example.com\r
+\r
+or perhaps\r
+\r
+\c psftp fred@server.example.com\r
+\r
+Alternatively, if you just type \c{psftp} on its own (or\r
+double-click the PSFTP icon in the Windows GUI), you will see the\r
+PSFTP prompt, and a message telling you PSFTP has not connected to\r
+any server:\r
+\r
+\c C:\>psftp\r
+\c psftp: no hostname specified; use "open host.name" to connect\r
+\c psftp>\r
+\r
+At this point you can type \c{open server.example.com} or \c{open\r
+fred@server.example.com} to start a session.\r
+\r
+PSFTP accepts all the general command line options supported by the\r
+PuTTY tools, except the ones which make no sense in a file transfer\r
+utility. See \k{using-general-opts} for a description of these\r
+options. (The ones not supported by PSFTP are clearly marked.)\r
+\r
+PSFTP also supports some of its own options. The following sections\r
+describe PSFTP's specific command-line options.\r
+\r
+\S{psftp-option-b} \I{-b-PSFTP}\c{-b}: specify a file containing batch commands\r
+\r
+In normal operation, PSFTP is an interactive program which displays\r
+a command line and accepts commands from the keyboard.\r
+\r
+If you need to do automated tasks with PSFTP, you would probably\r
+prefer to \I{batch scripts in PSFTP}specify a set of commands in\r
+advance and have them executed automatically. The \c{-b} option\r
+allows you to do this. You use it with a file name containing batch\r
+commands. For example, you might create a file called \c{myscript.scr}\r
+containing lines like this:\r
+\r
+\c cd /home/ftp/users/jeff\r
+\c del jam-old.tar.gz\r
+\c ren jam.tar.gz jam-old.tar.gz\r
+\c put jam.tar.gz\r
+\c chmod a+r jam.tar.gz\r
+\r
+and then you could run the script by typing\r
+\r
+\c psftp user@hostname -b myscript.scr\r
+\r
+When you run a batch script in this way, PSFTP will abort the script\r
+if any command fails to complete successfully. To change this\r
+behaviour, you can add the \c{-be} option (\k{psftp-option-be}).\r
+\r
+PSFTP will terminate after it finishes executing the batch script.\r
+\r
+\S{psftp-option-bc} \I{-bc-PSFTP}\c{-bc}: display batch commands as they are run\r
+\r
+The \c{-bc} option alters what PSFTP displays while processing a\r
+batch script specified with \c{-b}. With the \c{-bc} option, PSFTP\r
+will display prompts and commands just as if the commands had been\r
+typed at the keyboard. So instead of seeing this:\r
+\r
+\c C:\>psftp fred@hostname -b batchfile\r
+\c Sent username "fred"\r
+\c Remote working directory is /home/fred\r
+\c Listing directory /home/fred/lib\r
+\c drwxrwsr-x    4 fred     fred         1024 Sep  6 10:42 .\r
+\c drwxr-sr-x   25 fred     fred         2048 Dec 14 09:36 ..\r
+\c drwxrwsr-x    3 fred     fred         1024 Apr 17  2000 jed\r
+\c lrwxrwxrwx    1 fred     fred           24 Apr 17  2000 timber\r
+\c drwxrwsr-x    2 fred     fred         1024 Mar 13  2000 trn\r
+\r
+you might see this:\r
+\r
+\c C:\>psftp fred@hostname -bc -b batchfile\r
+\c Sent username "fred"\r
+\c Remote working directory is /home/fred\r
+\c psftp> dir lib\r
+\c Listing directory /home/fred/lib\r
+\c drwxrwsr-x    4 fred     fred         1024 Sep  6 10:42 .\r
+\c drwxr-sr-x   25 fred     fred         2048 Dec 14 09:36 ..\r
+\c drwxrwsr-x    3 fred     fred         1024 Apr 17  2000 jed\r
+\c lrwxrwxrwx    1 fred     fred           24 Apr 17  2000 timber\r
+\c drwxrwsr-x    2 fred     fred         1024 Mar 13  2000 trn\r
+\c psftp> quit\r
+\r
+\S{psftp-option-be} \I{-be-PSFTP}\c{-be}: continue batch processing on errors\r
+\r
+When running a batch file, this additional option causes PSFTP to\r
+continue processing even if a command fails to complete successfully.\r
+\r
+You might want this to happen if you wanted to delete a file and\r
+didn't care if it was already not present, for example.\r
+\r
+\S{psftp-usage-options-batch} \I{-batch-PSFTP}\c{-batch}: avoid\r
+interactive prompts\r
+\r
+If you use the \c{-batch} option, PSFTP will never give an\r
+interactive prompt while establishing the connection. If the\r
+server's host key is invalid, for example (see \k{gs-hostkey}), then\r
+the connection will simply be abandoned instead of asking you what\r
+to do next.\r
+\r
+This may help PSFTP's behaviour when it is used in automated\r
+scripts: using \c{-batch}, if something goes wrong at connection\r
+time, the batch job will fail rather than hang.\r
+\r
+\H{psftp-commands} Running PSFTP\r
+\r
+Once you have started your PSFTP session, you will see a \c{psftp>}\r
+prompt. You can now type commands to perform file-transfer\r
+functions. This section lists all the available commands.\r
+\r
+Any line starting with a \cw{#} will be treated as a \i{comment}\r
+and ignored.\r
+\r
+\S{psftp-quoting} \I{quoting, in PSFTP}General quoting rules for PSFTP commands\r
+\r
+Most PSFTP commands are considered by the PSFTP command interpreter\r
+as a sequence of words, separated by spaces. For example, the\r
+command \c{ren oldfilename newfilename} splits up into three words:\r
+\c{ren} (the command name), \c{oldfilename} (the name of the file to\r
+be renamed), and \c{newfilename} (the new name to give the file).\r
+\r
+Sometimes you will need to specify \I{spaces in filenames}file names\r
+that \e{contain} spaces. In order to do this, you can surround\r
+the file name with double quotes. This works equally well for\r
+local file names and remote file names:\r
+\r
+\c psftp> get "spacey file name.txt" "save it under this name.txt"\r
+\r
+The double quotes themselves will not appear as part of the file\r
+names; they are removed by PSFTP and their only effect is to stop\r
+the spaces inside them from acting as word separators.\r
+\r
+If you need to \e{use} a double quote (on some types of remote\r
+system, such as Unix, you are allowed to use double quotes in file\r
+names), you can do this by doubling it. This works both inside and\r
+outside double quotes. For example, this command\r
+\r
+\c psftp> ren ""this"" "a file with ""quotes"" in it"\r
+\r
+will take a file whose current name is \c{"this"} (with a double\r
+quote character at the beginning and the end) and rename it to a\r
+file whose name is \c{a file with "quotes" in it}.\r
+\r
+(The one exception to the PSFTP quoting rules is the \c{!} command,\r
+which passes its command line straight to Windows without splitting\r
+it up into words at all. See \k{psftp-cmd-pling}.)\r
+\r
+\S{psftp-wildcards} Wildcards in PSFTP\r
+\r
+Several commands in PSFTP support \q{\i{wildcards}} to select multiple\r
+files.\r
+\r
+For \e{local} file specifications (such as the first argument to\r
+\c{put}), wildcard rules for the local operating system are used. For\r
+instance, PSFTP running on Windows might require the use of \c{*.*}\r
+where PSFTP on Unix would need \c{*}.\r
+\r
+For \e{remote} file specifications (such as the first argument to\r
+\c{get}), PSFTP uses a standard wildcard syntax (similar to \i{POSIX}\r
+wildcards):\r
+\r
+\b \c{*} matches any sequence of characters (including a zero-length\r
+sequence).\r
+\r
+\b \c{?} matches exactly one character.\r
+\r
+\b \c{[abc]} matches exactly one character which can be \cw{a},\r
+\cw{b}, or \cw{c}.\r
+\r
+\lcont{\r
+\r
+\c{[a-z]} matches any character in the range \cw{a} to \cw{z}.\r
+\r
+\c{[^abc]} matches a single character that is \e{not} \cw{a}, \cw{b},\r
+or \cw{c}.\r
+\r
+Special cases: \c{[-a]} matches a literal hyphen (\cw{-}) or \cw{a};\r
+\c{[^-a]} matches all other characters. \c{[a^]} matches a literal\r
+caret (\cw{^}) or \cw{a}.\r
+\r
+}\r
+\r
+\b \c{\\} (backslash) before any of the above characters (or itself)\r
+removes that character's special meaning.\r
+\r
+A leading period (\cw{.}) on a filename is not treated specially,\r
+unlike in some Unix contexts; \c{get *} will fetch all files, whether\r
+or not they start with a leading period.\r
+\r
+\S{psftp-cmd-open} The \c{open} command: start a session\r
+\r
+If you started PSFTP by double-clicking in the GUI, or just by\r
+typing \c{psftp} at the command line, you will need to open a\r
+connection to an SFTP server before you can issue any other\r
+commands (except \c{help} and \c{quit}).\r
+\r
+To create a connection, type \c{open host.name}, or if you need to\r
+specify a user name as well you can type \c{open user@host.name}.\r
+You can optionally specify a port as well:\r
+\c{open user@host.name 22}.\r
+\r
+Once you have issued this command, you will not be able to issue it\r
+again, \e{even} if the command fails (for example, if you mistype\r
+the host name or the connection times out). So if the connection is\r
+not opened successfully, PSFTP will terminate immediately.\r
+\r
+\S{psftp-cmd-quit} The \c{quit} command: end your session\r
+\r
+When you have finished your session, type the command \c{quit} to\r
+close the connection, terminate PSFTP and return to the command line\r
+(or just close the PSFTP console window if you started it from the\r
+GUI).\r
+\r
+You can also use the \c{bye} and \c{exit} commands, which have\r
+exactly the same effect.\r
+\r
+\S{psftp-cmd-close} The \c{close} command: close your connection\r
+\r
+If you just want to close the network connection but keep PSFTP\r
+running, you can use the \c{close} command. You can then use the\r
+\c{open} command to open a new connection.\r
+\r
+\S{psftp-cmd-help} The \c{help} command: get quick online help\r
+\r
+If you type \c{help}, PSFTP will give a short list of the available\r
+commands.\r
+\r
+If you type \c{help} with a command name - for example, \c{help get}\r
+- then PSFTP will give a short piece of help on that particular\r
+command.\r
+\r
+\S{psftp-cmd-cd} The \c{cd} and \c{pwd} commands: changing the\r
+remote \i{working directory}\r
+\r
+PSFTP maintains a notion of your \q{working directory} on the\r
+server. This is the default directory that other commands will\r
+operate on. For example, if you type \c{get filename.dat} then PSFTP\r
+will look for \c{filename.dat} in your remote working directory on\r
+the server.\r
+\r
+To change your remote working directory, use the \c{cd} command. If\r
+you don't provide an argument, \c{cd} will return you to your home\r
+directory on the server (more precisely, the remote directory you were\r
+in at the start of the connection).\r
+\r
+To display your current remote working directory, type \c{pwd}.\r
+\r
+\S{psftp-cmd-lcd} The \c{lcd} and \c{lpwd} commands: changing the\r
+local \i{working directory}\r
+\r
+As well as having a working directory on the remote server, PSFTP\r
+also has a working directory on your local machine (just like any\r
+other Windows process). This is the default local directory that\r
+other commands will operate on. For example, if you type \c{get\r
+filename.dat} then PSFTP will save the resulting file as\r
+\c{filename.dat} in your local working directory.\r
+\r
+To change your local working directory, use the \c{lcd} command. To\r
+display your current local working directory, type \c{lpwd}.\r
+\r
+\S{psftp-cmd-get} The \c{get} command: fetch a file from the server\r
+\r
+To \i{download a file} from the server and store it on your local PC,\r
+you use the \c{get} command.\r
+\r
+In its simplest form, you just use this with a file name:\r
+\r
+\c get myfile.dat\r
+\r
+If you want to store the file locally under a different name,\r
+specify the local file name after the remote one:\r
+\r
+\c get myfile.dat newname.dat\r
+\r
+This will fetch the file on the server called \c{myfile.dat}, but\r
+will save it to your local machine under the name \c{newname.dat}.\r
+\r
+To fetch an entire directory \i{recursive}ly, you can use the \c{-r}\r
+option:\r
+\r
+\c get -r mydir\r
+\c get -r mydir newname\r
+\r
+(If you want to fetch a file whose name starts with a hyphen, you\r
+may have to use the \c{--} special argument, which stops \c{get}\r
+from interpreting anything as a switch after it. For example,\r
+\cq{get -- -silly-name-}.)\r
+\r
+\S{psftp-cmd-put} The \c{put} command: send a file to the server\r
+\r
+To \i{upload a file} to the server from your local PC, you use the\r
+\c{put} command.\r
+\r
+In its simplest form, you just use this with a file name:\r
+\r
+\c put myfile.dat\r
+\r
+If you want to store the file remotely under a different name,\r
+specify the remote file name after the local one:\r
+\r
+\c put myfile.dat newname.dat\r
+\r
+This will send the local file called \c{myfile.dat}, but will store\r
+it on the server under the name \c{newname.dat}.\r
+\r
+To send an entire directory \i{recursive}ly, you can use the \c{-r}\r
+option:\r
+\r
+\c put -r mydir\r
+\c put -r mydir newname\r
+\r
+(If you want to send a file whose name starts with a hyphen, you may\r
+have to use the \c{--} special argument, which stops \c{put} from\r
+interpreting anything as a switch after it. For example, \cq{put --\r
+-silly-name-}.)\r
+\r
+\S{psftp-cmd-mgetput} The \c{mget} and \c{mput} commands: fetch or\r
+send multiple files\r
+\r
+\c{mget} works almost exactly like \c{get}, except that it allows\r
+you to specify more than one file to fetch at once. You can do this\r
+in two ways:\r
+\r
+\b by giving two or more explicit file names (\cq{mget file1.txt\r
+file2.txt})\r
+\r
+\b by using a wildcard (\cq{mget *.txt}).\r
+\r
+Every argument to \c{mget} is treated as the name of a file to fetch\r
+(unlike \c{get}, which will interpret at most one argument like\r
+that, and a second argument will be treated as an alternative name\r
+under which to store the retrieved file), or a \i{wildcard} expression\r
+matching more than one file.\r
+\r
+The \c{-r} and \c{--} options from \c{get} are also available with\r
+\c{mget}.\r
+\r
+\c{mput} is similar to \c{put}, with the same differences.\r
+\r
+\S{psftp-cmd-regetput} The \c{reget} and \c{reput} commands:\r
+\i{resuming file transfers}\r
+\r
+If a file transfer fails half way through, and you end up with half\r
+the file stored on your disk, you can resume the file transfer using\r
+the \c{reget} and \c{reput} commands. These work exactly like the\r
+\c{get} and \c{put} commands, but they check for the presence of the\r
+half-written destination file and start transferring from where the\r
+last attempt left off.\r
+\r
+The syntax of \c{reget} and \c{reput} is exactly the same as the\r
+syntax of \c{get} and \c{put}:\r
+\r
+\c reget myfile.dat\r
+\c reget myfile.dat newname.dat\r
+\c reget -r mydir\r
+\r
+These commands are intended mainly for resuming interrupted transfers.\r
+They assume that the remote file or directory structure has not\r
+changed in any way; if there have been changes, you may end up with\r
+corrupted files. In particular, the \c{-r} option will not pick up\r
+changes to files or directories already transferred in full.\r
+\r
+\S{psftp-cmd-dir} The \c{dir} command: \I{listing files}list remote files\r
+\r
+To list the files in your remote working directory, just type\r
+\c{dir}.\r
+\r
+You can also list the contents of a different directory by typing\r
+\c{dir} followed by the directory name:\r
+\r
+\c dir /home/fred\r
+\c dir sources\r
+\r
+And you can list a subset of the contents of a directory by\r
+providing a wildcard:\r
+\r
+\c dir /home/fred/*.txt\r
+\c dir sources/*.c\r
+\r
+The \c{ls} command works exactly the same way as \c{dir}.\r
+\r
+\S{psftp-cmd-chmod} The \c{chmod} command: change permissions on\r
+remote files\r
+\r
+\I{changing permissions on files}PSFTP\r
+allows you to modify the file permissions on files and\r
+directories on the server. You do this using the \c{chmod} command,\r
+which works very much like the Unix \c{chmod} command.\r
+\r
+The basic syntax is \c{chmod modes file}, where \c{modes} represents\r
+a modification to the file permissions, and \c{file} is the filename\r
+to modify. You can specify multiple files or wildcards. For example:\r
+\r
+\c chmod go-rwx,u+w privatefile\r
+\c chmod a+r public*\r
+\c chmod 640 groupfile1 groupfile2\r
+\r
+The \c{modes} parameter can be a set of octal digits in the Unix\r
+style. (If you don't know what this means, you probably don't want\r
+to be using it!) Alternatively, it can be a list of permission\r
+modifications, separated by commas. Each modification consists of:\r
+\r
+\b The people affected by the modification. This can be \c{u} (the\r
+owning user), \c{g} (members of the owning group), or \c{o}\r
+(everybody else - \q{others}), or some combination of those. It can\r
+also be \c{a} (\q{all}) to affect everybody at once.\r
+\r
+\b A \c{+} or \c{-} sign, indicating whether permissions are to be\r
+added or removed.\r
+\r
+\b The actual permissions being added or removed. These can be\r
+\I{read permission}\c{r} (permission to read the file),\r
+\I{write permission}\c{w} (permission to write to the file), and\r
+\I{execute permission}\c{x} (permission to execute the file, or in\r
+the case of a directory, permission to access files within the\r
+directory).\r
+\r
+So the above examples would do:\r
+\r
+\b The first example: \c{go-rwx} removes read, write and execute\r
+permissions for members of the owning group and everybody else (so\r
+the only permissions left are the ones for the file owner). \c{u+w}\r
+adds write permission for the file owner.\r
+\r
+\b The second example: \c{a+r} adds read permission for everybody to\r
+all files and directories starting with \q{public}.\r
+\r
+In addition to all this, there are a few extra special cases for\r
+\i{Unix} systems. On non-Unix systems these are unlikely to be useful:\r
+\r
+\b You can specify \c{u+s} and \c{u-s} to add or remove the Unix\r
+\i{set-user-ID bit}. This is typically only useful for special purposes;\r
+refer to your Unix documentation if you're not sure about it.\r
+\r
+\b You can specify \c{g+s} and \c{g-s} to add or remove the Unix\r
+\i{set-group-ID bit}. On a file, this works similarly to the set-user-ID\r
+bit (see your Unix documentation again); on a directory it ensures\r
+that files created in the directory are accessible by members of the\r
+group that owns the directory.\r
+\r
+\b You can specify \c{+t} and \c{-t} to add or remove the Unix\r
+\q{\i{sticky bit}}. When applied to a directory, this means that the\r
+owner of a file in that directory can delete the file (whereas\r
+normally only the owner of the \e{directory} would be allowed to).\r
+\r
+\S{psftp-cmd-del} The \c{del} command: delete remote files\r
+\r
+To \I{deleting files}delete a file on the server, type \c{del} and\r
+then the filename or filenames:\r
+\r
+\c del oldfile.dat\r
+\c del file1.txt file2.txt\r
+\c del *.o\r
+\r
+Files will be deleted without further prompting, even if multiple files\r
+are specified.\r
+\r
+\c{del} will only delete files. You cannot use it to delete\r
+directories; use \c{rmdir} for that.\r
+\r
+The \c{rm} command works exactly the same way as \c{del}.\r
+\r
+\S{psftp-cmd-mkdir} The \c{mkdir} command: create remote directories\r
+\r
+To \i{create a directory} on the server, type \c{mkdir} and then the\r
+directory name:\r
+\r
+\c mkdir newstuff\r
+\r
+You can specify multiple directories to create at once:\r
+\r
+\c mkdir dir1 dir2 dir3\r
+\r
+\S{psftp-cmd-rmdir} The \c{rmdir} command: remove remote directories\r
+\r
+To \i{remove a directory} on the server, type \c{rmdir} and then the\r
+directory name or names:\r
+\r
+\c rmdir oldstuff\r
+\c rmdir *.old ancient\r
+\r
+Directories will be deleted without further prompting, even if\r
+multiple directories are specified.\r
+\r
+Most SFTP servers will probably refuse to remove a directory if the\r
+directory has anything in it, so you will need to delete the\r
+contents first.\r
+\r
+\S{psftp-cmd-mv} The \c{mv} command: move and \i{rename remote files}\r
+\r
+To rename a single file on the server, type \c{mv}, then the current\r
+file name, and then the new file name:\r
+\r
+\c mv oldfile newname\r
+\r
+You can also move the file into a different directory and change the\r
+name:\r
+\r
+\c mv oldfile dir/newname\r
+\r
+To move one or more files into an existing subdirectory, specify the\r
+files (using wildcards if desired), and then the destination\r
+directory:\r
+\r
+\c mv file dir\r
+\c mv file1 dir1/file2 dir2\r
+\c mv *.c *.h ..\r
+\r
+The \c{rename} and \c{ren} commands work exactly the same way as\r
+\c{mv}.\r
+\r
+\S{psftp-cmd-pling} The \c{!} command: run a \i{local Windows command}\r
+\r
+You can run local Windows commands using the \c{!} command. This is\r
+the only PSFTP command that is not subject to the command quoting\r
+rules given in \k{psftp-quoting}. If any command line begins with\r
+the \c{!} character, then the rest of the line will be passed\r
+straight to Windows without further translation.\r
+\r
+For example, if you want to move an existing copy of a file out of\r
+the way before downloading an updated version, you might type:\r
+\r
+\c psftp> !ren myfile.dat myfile.bak\r
+\c psftp> get myfile.dat\r
+\r
+using the Windows \c{ren} command to rename files on your local PC.\r
+\r
+\H{psftp-pubkey} Using \i{public key authentication} with PSFTP\r
+\r
+Like PuTTY, PSFTP can authenticate using a public key instead of a\r
+password. There are three ways you can do this.\r
+\r
+Firstly, PSFTP can use PuTTY saved sessions in place of hostnames.\r
+So you might do this:\r
+\r
+\b Run PuTTY, and create a PuTTY saved session (see\r
+\k{config-saving}) which specifies your private key file (see\r
+\k{config-ssh-privkey}). You will probably also want to specify a\r
+username to log in as (see \k{config-username}).\r
+\r
+\b In PSFTP, you can now use the name of the session instead of a\r
+hostname: type \c{psftp sessionname}, where \c{sessionname} is\r
+replaced by the name of your saved session.\r
+\r
+Secondly, you can supply the name of a private key file on the command\r
+line, with the \c{-i} option. See \k{using-cmdline-identity} for more\r
+information.\r
+\r
+Thirdly, PSFTP will attempt to authenticate using Pageant if Pageant\r
+is running (see \k{pageant}). So you would do this:\r
+\r
+\b Ensure Pageant is running, and has your private key stored in it.\r
+\r
+\b Specify a user and host name to PSFTP as normal. PSFTP will\r
+automatically detect Pageant and try to use the keys within it.\r
+\r
+For more general information on public-key authentication, see\r
+\k{pubkey}.\r
diff --git a/putty/DOC/PUBKEY.BUT b/putty/DOC/PUBKEY.BUT
new file mode 100644 (file)
index 0000000..580c6ee
--- /dev/null
@@ -0,0 +1,442 @@
+\define{versionidpubkey} \versionid $Id: pubkey.but 8607 2009-07-12 12:02:58Z simon $\r
+\r
+\C{pubkey} Using public keys for SSH authentication\r
+\r
+\H{pubkey-intro} \ii{Public key authentication} - an introduction\r
+\r
+Public key authentication is an alternative means of identifying\r
+yourself to a login server, instead of typing a password. It is more\r
+secure and more flexible, but more difficult to set up.\r
+\r
+In conventional password authentication, you prove you are who you\r
+claim to be by proving that you know the correct password. The only\r
+way to prove you know the password is to tell the server what you\r
+think the password is. This means that if the server has been\r
+hacked, or \i\e{spoofed} (see \k{gs-hostkey}), an attacker can learn\r
+your password.\r
+\r
+Public key authentication solves this problem. You generate a \i\e{key\r
+pair}, consisting of a \i{public key} (which everybody is allowed to\r
+know) and a \i{private key} (which you keep secret and do not give to\r
+anybody). The private key is able to generate \i\e{signatures}.\r
+A signature created using your private key cannot be forged by\r
+anybody who does not have that key; but anybody who has your public\r
+key can verify that a particular signature is genuine.\r
+\r
+So you generate a key pair on your own computer, and you copy the\r
+public key to the server. Then, when the server asks you to prove\r
+who you are, PuTTY can generate a signature using your private key.\r
+The server can verify that signature (since it has your public key)\r
+and allow you to log in. Now if the server is hacked or spoofed, the\r
+attacker does not gain your private key or password; they only gain\r
+one signature. And signatures cannot be re-used, so they have gained\r
+nothing.\r
+\r
+There is a problem with this: if your private key is stored\r
+unprotected on your own computer, then anybody who gains access to\r
+\e{that} will be able to generate signatures as if they were you. So\r
+they will be able to log in to your server under your account. For\r
+this reason, your private key is usually \i\e{encrypted} when it is\r
+stored on your local machine, using a \i{passphrase} of your choice. In\r
+order to generate a signature, PuTTY must decrypt the key, so you\r
+have to type your passphrase.\r
+\r
+This can make public-key authentication less convenient than\r
+password authentication: every time you log in to the server,\r
+instead of typing a short password, you have to type a longer\r
+passphrase. One solution to this is to use an \i\e{authentication\r
+agent}, a separate program which holds decrypted private keys and\r
+generates signatures on request. PuTTY's authentication agent is\r
+called \i{Pageant}. When you begin a Windows session, you start Pageant\r
+and load your private key into it (typing your passphrase once). For\r
+the rest of your session, you can start PuTTY any number of times\r
+and Pageant will automatically generate signatures without you\r
+having to do anything. When you close your Windows session, Pageant\r
+shuts down, without ever having stored your decrypted private key on\r
+disk. Many people feel this is a good compromise between security\r
+and convenience. See \k{pageant} for further details.\r
+\r
+There is more than one \i{public-key algorithm} available. The most\r
+common is \i{RSA}, but others exist, notably \i{DSA} (otherwise known as\r
+DSS), the USA's federal Digital Signature Standard. The key types\r
+supported by PuTTY are described in \k{puttygen-keytype}.\r
+\r
+\H{pubkey-puttygen} Using \i{PuTTYgen}, the PuTTY key generator\r
+\r
+\cfg{winhelp-topic}{puttygen.general}\r
+\r
+PuTTYgen is a key generator. It \I{generating keys}generates pairs of\r
+public and private keys to be used with PuTTY, PSCP, and Plink, as well\r
+as the PuTTY authentication agent, Pageant (see \k{pageant}).  PuTTYgen\r
+generates RSA and DSA keys.\r
+\r
+When you run PuTTYgen you will see a window where you have two\r
+choices: \q{Generate}, to generate a new public/private key pair, or\r
+\q{Load} to load in an existing private key.\r
+\r
+\S{puttygen-generating} Generating a new key\r
+\r
+This is a general outline of the procedure for generating a new key\r
+pair. The following sections describe the process in more detail.\r
+\r
+\b First, you need to select which type of key you want to generate,\r
+and also select the strength of the key. This is described in more\r
+detail in \k{puttygen-keytype} and\r
+\k{puttygen-strength}.\r
+\r
+\b Then press the \q{Generate} button, to actually generate the key.\r
+\K{puttygen-generate} describes this step.\r
+\r
+\b Once you have generated the key, select a comment field\r
+(\k{puttygen-comment}) and a passphrase (\k{puttygen-passphrase}).\r
+\r
+\b Now you're ready to save the private key to disk; press the\r
+\q{Save private key} button. (See \k{puttygen-savepriv}).\r
+\r
+Your key pair is now ready for use. You may also want to copy the\r
+public key to your server, either by copying it out of the \q{Public\r
+key for pasting into authorized_keys file} box (see\r
+\k{puttygen-pastekey}), or by using the \q{Save public key} button\r
+(\k{puttygen-savepub}). However, you don't need to do this\r
+immediately; if you want, you can load the private key back into\r
+PuTTYgen later (see \k{puttygen-load}) and the public key will be\r
+available for copying and pasting again.\r
+\r
+\K{pubkey-gettingready} describes the typical process of configuring\r
+PuTTY to attempt public-key authentication, and configuring your SSH\r
+server to accept it.\r
+\r
+\S{puttygen-keytype} Selecting the type of key\r
+\r
+\cfg{winhelp-topic}{puttygen.keytype}\r
+\r
+Before generating a key pair using PuTTYgen, you need to select\r
+which type of key you need. PuTTYgen currently supports three types\r
+of key:\r
+\r
+\b An \i{RSA} key for use with the SSH-1 protocol.\r
+\r
+\b An RSA key for use with the SSH-2 protocol.\r
+\r
+\b A \i{DSA} key for use with the SSH-2 protocol.\r
+\r
+The SSH-1 protocol only supports RSA keys; if you will be connecting\r
+using the SSH-1 protocol, you must select the first key type or your\r
+key will be completely useless.\r
+\r
+The SSH-2 protocol supports more than one key type. The two types\r
+supported by PuTTY are RSA and DSA.\r
+\r
+The PuTTY developers \e{strongly} recommend you use RSA.\r
+\I{security risk}\i{DSA} has an intrinsic weakness which makes it very\r
+easy to create a signature which contains enough information to give\r
+away the \e{private} key!\r
+This would allow an attacker to pretend to be you for any number of\r
+future sessions. PuTTY's implementation has taken very careful\r
+precautions to avoid this weakness, but we cannot be 100% certain we\r
+have managed it, and if you have the choice we strongly recommend\r
+using RSA keys instead.\r
+\r
+If you really need to connect to an SSH server which only supports\r
+DSA, then you probably have no choice but to use DSA. If you do use\r
+DSA, we recommend you do not use the same key to authenticate with\r
+more than one server.\r
+\r
+\S{puttygen-strength} Selecting the size (strength) of the key\r
+\r
+\cfg{winhelp-topic}{puttygen.bits}\r
+\r
+The \q{Number of bits} input box allows you to choose the strength\r
+of the key PuTTYgen will generate.\r
+\r
+Currently 1024 bits should be sufficient for most purposes.\r
+\r
+Note that an RSA key is generated by finding two primes of half the\r
+length requested, and then multiplying them together. For example,\r
+if you ask PuTTYgen for a 1024-bit RSA key, it will create two\r
+512-bit primes and multiply them. The result of this multiplication\r
+might be 1024 bits long, or it might be only 1023; so you may not\r
+get the exact length of key you asked for. This is perfectly normal,\r
+and you do not need to worry. The lengths should only ever differ by\r
+one, and there is no perceptible drop in security as a result.\r
+\r
+DSA keys are not created by multiplying primes together, so they\r
+should always be exactly the length you asked for.\r
+\r
+\S{puttygen-generate} The \q{Generate} button\r
+\r
+\cfg{winhelp-topic}{puttygen.generate}\r
+\r
+Once you have chosen the type of key you want, and the strength of\r
+the key, press the \q{Generate} button and PuTTYgen will begin the\r
+process of actually generating the key.\r
+\r
+First, a progress bar will appear and PuTTYgen will ask you to move\r
+the mouse around to generate randomness. Wave the mouse in circles\r
+over the blank area in the PuTTYgen window, and the progress bar\r
+will gradually fill up as PuTTYgen collects enough randomness. You\r
+don't need to wave the mouse in particularly imaginative patterns\r
+(although it can't hurt); PuTTYgen will collect enough randomness\r
+just from the fine detail of \e{exactly} how far the mouse has moved\r
+each time Windows samples its position.\r
+\r
+When the progress bar reaches the end, PuTTYgen will begin creating\r
+the key. The progress bar will reset to the start, and gradually\r
+move up again to track the progress of the key generation. It will\r
+not move evenly, and may occasionally slow down to a stop; this is\r
+unfortunately unavoidable, because key generation is a random\r
+process and it is impossible to reliably predict how long it will\r
+take.\r
+\r
+When the key generation is complete, a new set of controls will\r
+appear in the window to indicate this.\r
+\r
+\S{puttygen-fingerprint} The \q{\ii{Key fingerprint}} box\r
+\r
+\cfg{winhelp-topic}{puttygen.fingerprint}\r
+\r
+The \q{Key fingerprint} box shows you a fingerprint value for the\r
+generated key. This is derived cryptographically from the \e{public}\r
+key value, so it doesn't need to be kept secret.\r
+\r
+The fingerprint value is intended to be cryptographically secure, in\r
+the sense that it is computationally infeasible for someone to\r
+invent a second key with the same fingerprint, or to find a key with\r
+a particular fingerprint. So some utilities, such as the Pageant key\r
+list box (see \k{pageant-mainwin-keylist}) and the Unix \c{ssh-add}\r
+utility, will list key fingerprints rather than the whole public key.\r
+\r
+\S{puttygen-comment} Setting a comment for your key\r
+\r
+\cfg{winhelp-topic}{puttygen.comment}\r
+\r
+If you have more than one key and use them for different purposes,\r
+you don't need to memorise the key fingerprints in order to tell\r
+them apart. PuTTYgen allows you to enter a \e{comment} for your key,\r
+which will be displayed whenever PuTTY or Pageant asks you for the\r
+passphrase.\r
+\r
+The default comment format, if you don't specify one, contains the\r
+key type and the date of generation, such as \c{rsa-key-20011212}.\r
+Another commonly used approach is to use your name and the name of\r
+the computer the key will be used on, such as \c{simon@simons-pc}.\r
+\r
+To alter the key comment, just type your comment text into the\r
+\q{Key comment} box before saving the private key. If you want to\r
+change the comment later, you can load the private key back into\r
+PuTTYgen, change the comment, and save it again.\r
+\r
+\S{puttygen-passphrase} Setting a \i{passphrase} for your key\r
+\r
+\cfg{winhelp-topic}{puttygen.passphrase}\r
+\r
+The \q{Key passphrase} and \q{Confirm passphrase} boxes allow you to\r
+choose a passphrase for your key. The passphrase will be used to\r
+\i{encrypt} the key on disk, so you will not be able to use the key\r
+without first entering the passphrase.\r
+\r
+When you save the key, PuTTYgen will check that the \q{Key passphrase}\r
+and \q{Confirm passphrase} boxes both contain exactly the same\r
+passphrase, and will refuse to save the key otherwise.\r
+\r
+If you leave the passphrase fields blank, the key will be saved\r
+unencrypted. You should \e{not} do this without good reason; if you\r
+do, your private key file on disk will be all an attacker needs to\r
+gain access to any machine configured to accept that key. If you\r
+want to be able to \I{passwordless login}log in without having to\r
+type a passphrase every time, you should consider using Pageant\r
+(\k{pageant}) so that your decrypted key is only held in memory\r
+rather than on disk.\r
+\r
+Under special circumstances you may genuinely \e{need} to use a key\r
+with no passphrase; for example, if you need to run an automated\r
+batch script that needs to make an SSH connection, you can't be\r
+there to type the passphrase. In this case we recommend you generate\r
+a special key for each specific batch script (or whatever) that\r
+needs one, and on the server side you should arrange that each key\r
+is \e{restricted} so that it can only be used for that specific\r
+purpose. The documentation for your SSH server should explain how to\r
+do this (it will probably vary between servers).\r
+\r
+Choosing a good passphrase is difficult. Just as you shouldn't use a\r
+dictionary word as a password because it's easy for an attacker to\r
+run through a whole dictionary, you should not use a song lyric,\r
+quotation or other well-known sentence as a passphrase. \i{DiceWare}\r
+(\W{http://www.diceware.com/}\cw{www.diceware.com}) recommends using\r
+at least five words each generated randomly by rolling five dice,\r
+which gives over 2^64 possible passphrases and is probably not a bad\r
+scheme. If you want your passphrase to make grammatical sense, this\r
+cuts down the possibilities a lot and you should use a longer one as\r
+a result.\r
+\r
+\e{Do not forget your passphrase}. There is no way to recover it.\r
+\r
+\S{puttygen-savepriv} Saving your private key to a disk file\r
+\r
+\cfg{winhelp-topic}{puttygen.savepriv}\r
+\r
+Once you have generated a key, set a comment field and set a\r
+passphrase, you are ready to save your private key to disk.\r
+\r
+Press the \q{Save private key} button. PuTTYgen will put up a dialog\r
+box asking you where to save the file. Select a directory, type in a\r
+file name, and press \q{Save}.\r
+\r
+This file is in PuTTY's native format (\c{*.\i{PPK}}); it is the one you\r
+will need to tell PuTTY to use for authentication (see\r
+\k{config-ssh-privkey}) or tell Pageant to load (see\r
+\k{pageant-mainwin-addkey}).\r
+\r
+\S{puttygen-savepub} Saving your public key to a disk file\r
+\r
+\cfg{winhelp-topic}{puttygen.savepub}\r
+\r
+RFC 4716 specifies a \I{SSH-2 public key format}standard format for\r
+storing SSH-2 public keys on disk. Some SSH servers (such as\r
+\i\cw{ssh.com}'s) require a public key in this format in order to accept\r
+authentication with the corresponding private key. (Others, such as\r
+OpenSSH, use a different format; see \k{puttygen-pastekey}.)\r
+\r
+To save your public key in the SSH-2 standard format, press the\r
+\q{Save public key} button in PuTTYgen. PuTTYgen will put up a\r
+dialog box asking you where to save the file. Select a directory,\r
+type in a file name, and press \q{Save}.\r
+\r
+You will then probably want to copy the public key file to your SSH\r
+server machine. See \k{pubkey-gettingready} for general instructions\r
+on configuring public-key authentication once you have generated a\r
+key.\r
+\r
+If you use this option with an SSH-1 key, the file PuTTYgen saves\r
+will contain exactly the same text that appears in the \q{Public key\r
+for pasting} box. This is the only existing standard for SSH-1\r
+public keys.\r
+\r
+\S{puttygen-pastekey} \q{Public key for pasting into \i{authorized_keys\r
+file}}\r
+\r
+\cfg{winhelp-topic}{puttygen.pastekey}\r
+\r
+All SSH-1 servers require your public key to be given to it in a\r
+one-line format before it will accept authentication with your\r
+private key. The \i{OpenSSH} server also requires this for SSH-2.\r
+\r
+The \q{Public key for pasting into authorized_keys file} gives the\r
+public-key data in the correct one-line format. Typically you will\r
+want to select the entire contents of the box using the mouse, press\r
+Ctrl+C to copy it to the clipboard, and then paste the data into a\r
+PuTTY session which is already connected to the server.\r
+\r
+See \k{pubkey-gettingready} for general instructions on configuring\r
+public-key authentication once you have generated a key.\r
+\r
+\S{puttygen-load} Reloading a private key\r
+\r
+\cfg{winhelp-topic}{puttygen.load}\r
+\r
+PuTTYgen allows you to load an existing private key file into\r
+memory. If you do this, you can then change the passphrase and\r
+comment before saving it again; you can also make extra copies of\r
+the public key.\r
+\r
+To load an existing key, press the \q{Load} button. PuTTYgen will\r
+put up a dialog box where you can browse around the file system and\r
+find your key file. Once you select the file, PuTTYgen will ask you\r
+for a passphrase (if necessary) and will then display the key\r
+details in the same way as if it had just generated the key.\r
+\r
+If you use the Load command to load a foreign key format, it will\r
+work, but you will see a message box warning you that the key you\r
+have loaded is not a PuTTY native key. See \k{puttygen-conversions}\r
+for information about importing foreign key formats.\r
+\r
+\S{puttygen-conversions} Dealing with private keys in other formats\r
+\r
+\cfg{winhelp-topic}{puttygen.conversions}\r
+\r
+Most SSH-1 clients use a standard format for storing private keys on\r
+disk. PuTTY uses this format as well; so if you have generated an\r
+SSH-1 private key using OpenSSH or \cw{ssh.com}'s client, you can use\r
+it with PuTTY, and vice versa.\r
+\r
+However, SSH-2 private keys have no standard format. \I{OpenSSH private\r
+key format}OpenSSH and \I{ssh.com private key format}\cw{ssh.com} have\r
+different formats, and PuTTY's is different again.\r
+So a key generated with one client cannot immediately be used with\r
+another.\r
+\r
+Using the \I{importing keys}\q{Import} command from the \q{Conversions}\r
+menu, PuTTYgen can load SSH-2 private keys in OpenSSH's format and\r
+\cw{ssh.com}'s format. Once you have loaded one of these key types, you\r
+can then save it back out as a PuTTY-format key (\c{*.\i{PPK}}) so that\r
+you can use it with the PuTTY suite. The passphrase will be unchanged by this\r
+process (unless you deliberately change it). You may want to change\r
+the key comment before you save the key, since OpenSSH's SSH-2 key\r
+format contains no space for a comment and \cw{ssh.com}'s default\r
+comment format is long and verbose.\r
+\r
+PuTTYgen can also \i{export private keys} in OpenSSH format and in\r
+\cw{ssh.com} format. To do so, select one of the \q{Export} options\r
+from the \q{Conversions} menu. Exporting a key works exactly like\r
+saving it (see \k{puttygen-savepriv}) - you need to have typed your\r
+passphrase in beforehand, and you will be warned if you are about to\r
+save a key without a passphrase.\r
+\r
+Note that since only SSH-2 keys come in different formats, the export\r
+options are not available if you have generated an SSH-1 key.\r
+\r
+\H{pubkey-gettingready} Getting ready for public key authentication\r
+\r
+Connect to your SSH server using PuTTY with the SSH protocol. When the\r
+connection succeeds you will be prompted for your user name and\r
+password to login. Once logged in, you must configure the server to\r
+accept your public key for authentication:\r
+\r
+\b If your server is using the SSH-1 protocol, you should change\r
+into the \i\c{.ssh} directory and open the file \i\c{authorized_keys}\r
+with your favourite editor. (You may have to create this file if\r
+this is the first key you have put in it). Then switch to the\r
+PuTTYgen window, select all of the text in the \q{Public key for\r
+pasting into authorized_keys file} box (see \k{puttygen-pastekey}),\r
+and copy it to the clipboard (\c{Ctrl+C}). Then, switch back to the\r
+PuTTY window and insert the data into the open file, making sure it\r
+ends up all on one line. Save the file.\r
+\r
+\b If your server is \i{OpenSSH} and is using the SSH-2 protocol, you\r
+should follow the same instructions, except that in earlier versions\r
+of OpenSSH 2 the file might be called \c{authorized_keys2}. (In\r
+modern versions the same \c{authorized_keys} file is used for both\r
+SSH-1 and SSH-2 keys.)\r
+\r
+\b If your server is \i\cw{ssh.com}'s product and is using SSH-2, you\r
+need to save a \e{public} key file from PuTTYgen (see\r
+\k{puttygen-savepub}), and copy that into the \i\c{.ssh2} directory on\r
+the server. Then you should go into that \c{.ssh2} directory, and edit\r
+(or create) a file called \c{authorization}. In this file you should\r
+put a line like \c{Key mykey.pub}, with \c{mykey.pub} replaced by the\r
+name of your key file.\r
+\r
+\b For other SSH server software, you should refer to the manual for\r
+that server.\r
+\r
+You may also need to ensure that your home directory, your \c{.ssh}\r
+directory, and any other files involved (such as\r
+\c{authorized_keys}, \c{authorized_keys2} or \c{authorization}) are\r
+not group-writable or world-writable. You can typically do this by\r
+using a command such as\r
+\r
+\c chmod go-w $HOME $HOME/.ssh $HOME/.ssh/authorized_keys\r
+\r
+Your server should now be configured to accept authentication using\r
+your private key. Now you need to configure PuTTY to \e{attempt}\r
+authentication using your private key. You can do this in any of\r
+three ways:\r
+\r
+\b Select the private key in PuTTY's configuration. See\r
+\k{config-ssh-privkey} for details.\r
+\r
+\b Specify the key file on the command line with the \c{-i} option.\r
+See \k{using-cmdline-identity} for details.\r
+\r
+\b Load the private key into Pageant (see \k{pageant}). In this case\r
+PuTTY will automatically try to use it for authentication if it can.\r
diff --git a/putty/DOC/SITE.BUT b/putty/DOC/SITE.BUT
new file mode 100644 (file)
index 0000000..dc8c32e
--- /dev/null
@@ -0,0 +1,4 @@
+\# Additional configuration for the version of the PuTTY docs\r
+\# actually published as HTML on the website.\r
+\r
+\cfg{xhtml-head-end}{<link rel='stylesheet' href='sitestyle.css' type='text/css' />}\r
diff --git a/putty/DOC/SSHNAMES.BUT b/putty/DOC/SSHNAMES.BUT
new file mode 100644 (file)
index 0000000..f8bb4f7
--- /dev/null
@@ -0,0 +1,64 @@
+\define{versionidsshnames} \versionid $Id$\r
+\r
+\A{sshnames} SSH-2 names specified for PuTTY\r
+\r
+There are various parts of the SSH-2 protocol where things are specified\r
+using a textual name.  Names ending in \cw{@putty.projects.tartarus.org}\r
+are reserved for allocation by the PuTTY team.  Allocated names are\r
+documented here.\r
+\r
+\H{sshnames-channel} Connection protocol channel request names\r
+\r
+These names can be sent in a \cw{SSH_MSG_CHANNEL_REQUEST} message.\r
+\r
+\dt \cw{simple@putty.projects.tartarus.org}\r
+\r
+\dd This is sent by a client to announce that it will not have more than\r
+one channel open at a time in the current connection (that one being\r
+the one the request is sent on).  The intention is that the server,\r
+knowing this, can set the window on that one channel to something very\r
+large, and leave flow control to TCP.  There is no message-specific data.\r
+\r
+\dt \cw{winadj@putty.projects.tartarus.org}\r
+\r
+\dd PuTTY sends this request along with some\r
+\cw{SSH_MSG_CHANNEL_WINDOW_ADJUST} messages as part of its window-size\r
+tuning.  It can be sent on any type of channel.  There is no\r
+message-specific data. Servers MUST treat it as an unrecognised request\r
+and respond with \cw{SSH_MSG_CHANNEL_FAILURE}.\r
+\r
+\H{sshnames-kex} Key exchange method names\r
+\r
+\dt \cw{rsa-sha1-draft-00@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa-sha256-draft-00@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa1024-sha1-draft-01@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa1024-sha256-draft-01@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa2048-sha256-draft-01@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa1024-sha1-draft-02@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa2048-sha512-draft-02@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa1024-sha1-draft-03@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa2048-sha256-draft-03@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa1024-sha1-draft-04@putty.projects.tartarus.org}\r
+\r
+\dt \cw{rsa2048-sha256-draft-04@putty.projects.tartarus.org}\r
+\r
+\dd These appeared in various drafts of what eventually became RFC\_4432.\r
+They have been superseded by \cw{rsa1024-sha1} and \cw{rsa2048-sha256}.\r
+\r
+\H{sshnames-encrypt} Encryption algorithm names\r
+\r
+\dt \cw{arcfour128-draft-00@putty.projects.tartarus.org}\r
+\r
+\dt \cw{arcfour256-draft-00@putty.projects.tartarus.org}\r
+\r
+\dd These were used in drafts of what eventually became RFC\_4345.\r
+They have been superseded by \cw{arcfour128} and \cw{arcfour256}.\r
diff --git a/putty/DOC/UDP.BUT b/putty/DOC/UDP.BUT
new file mode 100644 (file)
index 0000000..6635654
--- /dev/null
@@ -0,0 +1,389 @@
+\# This file is so named for tradition's sake: it contains what we\r
+\# always used to refer to, before they were written down, as\r
+\# PuTTY's `unwritten design principles'. It has nothing to do with\r
+\# the User Datagram Protocol.\r
+\r
+\define{versionidudp} \versionid $Id: udp.but 5525 2005-03-19 02:29:57Z jacob $\r
+\r
+\A{udp} PuTTY hacking guide\r
+\r
+This appendix lists a selection of the design principles applying to\r
+the PuTTY source code. If you are planning to send code\r
+contributions, you should read this first.\r
+\r
+\H{udp-portability} Cross-OS portability\r
+\r
+Despite Windows being its main area of fame, PuTTY is no longer a\r
+Windows-only application suite. It has a working Unix port; a Mac\r
+port is in progress; more ports may or may not happen at a later\r
+date.\r
+\r
+Therefore, embedding Windows-specific code in core modules such as\r
+\cw{ssh.c} is not acceptable. We went to great lengths to \e{remove}\r
+all the Windows-specific stuff from our core modules, and to shift\r
+it out into Windows-specific modules. Adding large amounts of\r
+Windows-specific stuff in parts of the code that should be portable\r
+is almost guaranteed to make us reject a contribution.\r
+\r
+The PuTTY source base is divided into platform-specific modules and\r
+platform-generic modules. The Unix-specific modules are all in the\r
+\c{unix} subdirectory; the Mac-specific modules are in the \c{mac}\r
+subdirectory; the Windows-specific modules are in the \c{windows}\r
+subdirectory.\r
+\r
+All the modules in the main source directory - notably \e{all} of\r
+the code for the various back ends - are platform-generic. We want\r
+to keep them that way.\r
+\r
+This also means you should stick to what you are guaranteed by\r
+ANSI/ISO C (that is, the original C89/C90 standard, not C99). Try\r
+not to make assumptions about the precise size of basic types such\r
+as \c{int} and \c{long int}; don't use pointer casts to do\r
+endianness-dependent operations, and so on.\r
+\r
+(There are one or two aspects of ANSI C portability which we\r
+\e{don't} care about. In particular, we expect PuTTY to be compiled\r
+on 32-bit architectures \e{or bigger}; so it's safe to assume that\r
+\c{int} is at least 32 bits wide, not just the 16 you are guaranteed\r
+by ANSI C.  Similarly, we assume that the execution character\r
+encoding is a superset of the printable characters of ASCII, though\r
+we don't assume the numeric values of control characters,\r
+particularly \cw{'\\n'} and \cw{'\\r'}.)\r
+\r
+\H{udp-multi-backend} Multiple backends treated equally\r
+\r
+PuTTY is not an SSH client with some other stuff tacked on the side.\r
+PuTTY is a generic, multiple-backend, remote VT-terminal client\r
+which happens to support one backend which is larger, more popular\r
+and more useful than the rest. Any extra feature which can possibly\r
+be general across all backends should be so: localising features\r
+unnecessarily into the SSH back end is a design error. (For example,\r
+we had several code submissions for proxy support which worked by\r
+hacking \cw{ssh.c}. Clearly this is completely wrong: the\r
+\cw{network.h} abstraction is the place to put it, so that it will\r
+apply to all back ends equally, and indeed we eventually put it\r
+there after another contributor sent a better patch.)\r
+\r
+The rest of PuTTY should try to avoid knowing anything about\r
+specific back ends if at all possible. To support a feature which is\r
+only available in one network protocol, for example, the back end\r
+interface should be extended in a general manner such that \e{any}\r
+back end which is able to provide that feature can do so. If it so\r
+happens that only one back end actually does, that's just the way it\r
+is, but it shouldn't be relied upon by any code.\r
+\r
+\H{udp-globals} Multiple sessions per process on some platforms\r
+\r
+Some ports of PuTTY - notably the in-progress Mac port - are\r
+constrained by the operating system to run as a single process\r
+potentially managing multiple sessions.\r
+\r
+Therefore, the platform-independent parts of PuTTY never use global\r
+variables to store per-session data. The global variables that do\r
+exist are tolerated because they are not specific to a particular\r
+login session: \c{flags} defines properties that are expected to\r
+apply equally to \e{all} the sessions run by a single PuTTY process,\r
+the random number state in \cw{sshrand.c} and the timer list in\r
+\cw{timing.c} serve all sessions equally, and so on. But most data\r
+is specific to a particular network session, and is therefore stored\r
+in dynamically allocated data structures, and pointers to these\r
+structures are passed around between functions.\r
+\r
+Platform-specific code can reverse this decision if it likes. The\r
+Windows code, for historical reasons, stores most of its data as\r
+global variables. That's OK, because \e{on Windows} we know there is\r
+only one session per PuTTY process, so it's safe to do that. But\r
+changes to the platform-independent code should avoid introducing\r
+global variables, unless they are genuinely cross-session.\r
+\r
+\H{udp-pure-c} C, not C++\r
+\r
+PuTTY is written entirely in C, not in C++.\r
+\r
+We have made \e{some} effort to make it easy to compile our code\r
+using a C++ compiler: notably, our \c{snew}, \c{snewn} and\r
+\c{sresize} macros explicitly cast the return values of \cw{malloc}\r
+and \cw{realloc} to the target type. (This has type checking\r
+advantages even in C: it means you never accidentally allocate the\r
+wrong size piece of memory for the pointer type you're assigning it\r
+to. C++ friendliness is really a side benefit.)\r
+\r
+We want PuTTY to continue being pure C, at least in the\r
+platform-independent parts and the currently existing ports. Patches\r
+which switch the Makefiles to compile it as C++ and start using\r
+classes will not be accepted. Also, in particular, we disapprove of\r
+\cw{//} comments, at least for the moment. (Perhaps once C99 becomes\r
+genuinely widespread we might be more lenient.)\r
+\r
+The one exception: a port to a new platform may use languages other\r
+than C if they are necessary to code on that platform. If your\r
+favourite PDA has a GUI with a C++ API, then there's no way you can\r
+do a port of PuTTY without using C++, so go ahead and use it. But\r
+keep the C++ restricted to that platform's subdirectory; if your\r
+changes force the Unix or Windows ports to be compiled as C++, they\r
+will be unacceptable to us.\r
+\r
+\H{udp-security} Security-conscious coding\r
+\r
+PuTTY is a network application and a security application. Assume\r
+your code will end up being fed deliberately malicious data by\r
+attackers, and try to code in a way that makes it unlikely to be a\r
+security risk.\r
+\r
+In particular, try not to use fixed-size buffers for variable-size\r
+data such as strings received from the network (or even the user).\r
+We provide functions such as \cw{dupcat} and \cw{dupprintf}, which\r
+dynamically allocate buffers of the right size for the string they\r
+construct. Use these wherever possible.\r
+\r
+\H{udp-multi-compiler} Independence of specific compiler\r
+\r
+Windows PuTTY can currently be compiled with any of four Windows\r
+compilers: MS Visual C, Borland's freely downloadable C compiler,\r
+the Cygwin / \cw{mingw32} GNU tools, and \cw{lcc-win32}.\r
+\r
+This is a really useful property of PuTTY, because it means people\r
+who want to contribute to the coding don't depend on having a\r
+specific compiler; so they don't have to fork out money for MSVC if\r
+they don't already have it, but on the other hand if they \e{do}\r
+have it they also don't have to spend effort installing \cw{gcc}\r
+alongside it. They can use whichever compiler they happen to have\r
+available, or install whichever is cheapest and easiest if they\r
+don't have one.\r
+\r
+Therefore, we don't want PuTTY to start depending on which compiler\r
+you're using. Using GNU extensions to the C language, for example,\r
+would ruin this useful property (not that anyone's ever tried it!);\r
+and more realistically, depending on an MS-specific library function\r
+supplied by the MSVC C library (\cw{_snprintf}, for example) is a\r
+mistake, because that function won't be available under the other\r
+compilers. Any function supplied in an official Windows DLL as part\r
+of the Windows API is fine, and anything defined in the C library\r
+standard is also fine, because those should be available\r
+irrespective of compilation environment. But things in between,\r
+available as non-standard library and language extensions in only\r
+one compiler, are disallowed.\r
+\r
+(\cw{_snprintf} in particular should be unnecessary, since we\r
+provide \cw{dupprintf}; see \k{udp-security}.)\r
+\r
+Compiler independence should apply on all platforms, of course, not\r
+just on Windows.\r
+\r
+\H{udp-small} Small code size\r
+\r
+PuTTY is tiny, compared to many other Windows applications. And it's\r
+easy to install: it depends on no DLLs, no other applications, no\r
+service packs or system upgrades. It's just one executable. You\r
+install that executable wherever you want to, and run it.\r
+\r
+We want to keep both these properties - the small size, and the ease\r
+of installation - if at all possible. So code contributions that\r
+depend critically on external DLLs, or that add a huge amount to the\r
+code size for a feature which is only useful to a small minority of\r
+users, are likely to be thrown out immediately.\r
+\r
+We do vaguely intend to introduce a DLL plugin interface for PuTTY,\r
+whereby seriously large extra features can be implemented in plugin\r
+modules. The important thing, though, is that those DLLs will be\r
+\e{optional}; if PuTTY can't find them on startup, it should run\r
+perfectly happily and just won't provide those particular features.\r
+A full installation of PuTTY might one day contain ten or twenty\r
+little DLL plugins, which would cut down a little on the ease of\r
+installation - but if you really needed ease of installation you\r
+\e{could} still just install the one PuTTY binary, or just the DLLs\r
+you really needed, and it would still work fine.\r
+\r
+Depending on \e{external} DLLs is something we'd like to avoid if at\r
+all possible (though for some purposes, such as complex SSH\r
+authentication mechanisms, it may be unavoidable). If it can't be\r
+avoided, the important thing is to follow the same principle of\r
+graceful degradation: if a DLL can't be found, then PuTTY should run\r
+happily and just not supply the feature that depended on it.\r
+\r
+\H{udp-single-threaded} Single-threaded code\r
+\r
+PuTTY and its supporting tools, or at least the vast majority of\r
+them, run in only one OS thread.\r
+\r
+This means that if you're devising some piece of internal mechanism,\r
+there's no need to use locks to make sure it doesn't get called by\r
+two threads at once. The only way code can be called re-entrantly is\r
+by recursion.\r
+\r
+That said, most of Windows PuTTY's network handling is triggered off\r
+Windows messages requested by \cw{WSAAsyncSelect()}, so if you call\r
+\cw{MessageBox()} deep within some network event handling code you\r
+should be aware that you might be re-entered if a network event\r
+comes in and is passed on to our window procedure by the\r
+\cw{MessageBox()} message loop.\r
+\r
+Also, the front ends (in particular Windows Plink) can use multiple\r
+threads if they like. However, Windows Plink keeps \e{very} tight\r
+control of its auxiliary threads, and uses them pretty much\r
+exclusively as a form of \cw{select()}. Pretty much all the code\r
+outside \cw{windows/winplink.c} is \e{only} ever called from the one\r
+primary thread; the others just loop round blocking on file handles\r
+and send messages to the main thread when some real work needs\r
+doing. This is not considered a portability hazard because that bit\r
+of \cw{windows/winplink.c} will need rewriting on other platforms in\r
+any case.\r
+\r
+One important consequence of this: PuTTY has only one thread in\r
+which to do everything. That \q{everything} may include managing\r
+more than one login session (\k{udp-globals}), managing multiple\r
+data channels within an SSH session, responding to GUI events even\r
+when nothing is happening on the network, and responding to network\r
+requests from the server (such as repeat key exchange) even when the\r
+program is dealing with complex user interaction such as the\r
+re-configuration dialog box. This means that \e{almost none} of the\r
+PuTTY code can safely block.\r
+\r
+\H{udp-keystrokes} Keystrokes sent to the server wherever possible\r
+\r
+In almost all cases, PuTTY sends keystrokes to the server. Even\r
+weird keystrokes that you think should be hot keys controlling\r
+PuTTY. Even Alt-F4 or Alt-Space, for example. If a keystroke has a\r
+well-defined escape sequence that it could usefully be sending to\r
+the server, then it should do so, or at the very least it should be\r
+configurably able to do so.\r
+\r
+To unconditionally turn a key combination into a hot key to control\r
+PuTTY is almost always a design error. If a hot key is really truly\r
+required, then try to find a key combination for it which \e{isn't}\r
+already used in existing PuTTYs (either it sends nothing to the\r
+server, or it sends the same thing as some other combination). Even\r
+then, be prepared for the possibility that one day that key\r
+combination might end up being needed to send something to the\r
+server - so make sure that there's an alternative way to invoke\r
+whatever PuTTY feature it controls.\r
+\r
+\H{udp-640x480} 640\u00D7{x}480 friendliness in configuration panels\r
+\r
+There's a reason we have lots of tiny configuration panels instead\r
+of a few huge ones, and that reason is that not everyone has a\r
+1600\u00D7{x}1200 desktop. 640\u00D7{x}480 is still a viable\r
+resolution for running Windows (and indeed it's still the default if\r
+you start up in safe mode), so it's still a resolution we care\r
+about.\r
+\r
+Accordingly, the PuTTY configuration box, and the PuTTYgen control\r
+window, are deliberately kept just small enough to fit comfortably\r
+on a 640\u00D7{x}480 display. If you're adding controls to either of\r
+these boxes and you find yourself wanting to increase the size of\r
+the whole box, \e{don't}. Split it into more panels instead.\r
+\r
+\H{udp-makefiles-auto} Automatically generated \cw{Makefile}s\r
+\r
+PuTTY is intended to compile on multiple platforms, and with\r
+multiple compilers. It would be horrifying to try to maintain a\r
+single \cw{Makefile} which handled all possible situations, and just\r
+as painful to try to directly maintain a set of matching\r
+\cw{Makefile}s for each different compilation environment.\r
+\r
+Therefore, we have moved the problem up by one level. In the PuTTY\r
+source archive is a file called \c{Recipe}, which lists which source\r
+files combine to produce which binaries; and there is also a script\r
+called \cw{mkfiles.pl}, which reads \c{Recipe} and writes out the\r
+real \cw{Makefile}s. (The script also reads all the source files and\r
+analyses their dependencies on header files, so we get an extra\r
+benefit from doing it this way, which is that we can supply correct\r
+dependency information even in environments where it's difficult to\r
+set up an automated \c{make depend} phase.)\r
+\r
+You should \e{never} edit any of the PuTTY \cw{Makefile}s directly.\r
+They are not stored in our source repository at all. They are\r
+automatically generated by \cw{mkfiles.pl} from the file \c{Recipe}.\r
+\r
+If you need to add a new object file to a particular binary, the\r
+right thing to do is to edit \c{Recipe} and re-run \cw{mkfiles.pl}.\r
+This will cause the new object file to be added in every tool that\r
+requires it, on every platform where it matters, in every\r
+\cw{Makefile} to which it is relevant, \e{and} to get all the\r
+dependency data right.\r
+\r
+If you send us a patch that modifies one of the \cw{Makefile}s, you\r
+just waste our time, because we will have to convert it into a\r
+change to \c{Recipe}. If you send us a patch that modifies \e{all}\r
+of the \cw{Makefile}s, you will have wasted a lot of \e{your} time\r
+as well!\r
+\r
+(There is a comment at the top of every \cw{Makefile} in the PuTTY\r
+source archive saying this, but many people don't seem to read it,\r
+so it's worth repeating here.)\r
+\r
+\H{udp-ssh-coroutines} Coroutines in \cw{ssh.c}\r
+\r
+Large parts of the code in \cw{ssh.c} are structured using a set of\r
+macros that implement (something close to) Donald Knuth's\r
+\q{coroutines} concept in C.\r
+\r
+Essentially, the purpose of these macros are to arrange that a\r
+function can call \cw{crReturn()} to return to its caller, and the\r
+next time it is called control will resume from just after that\r
+\cw{crReturn} statement.\r
+\r
+This means that any local (automatic) variables declared in such a\r
+function will be corrupted every time you call \cw{crReturn}. If you\r
+need a variable to persist for longer than that, you \e{must} make\r
+it a field in one of the persistent state structures: either the\r
+local state structures \c{s} or \c{st} in each function, or the\r
+backend-wide structure \c{ssh}.\r
+\r
+See\r
+\W{http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}\c{http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}\r
+for a more in-depth discussion of what these macros are for and how\r
+they work.\r
+\r
+\H{udp-compile-once} Single compilation of each source file\r
+\r
+The PuTTY build system for any given platform works on the following\r
+very simple model:\r
+\r
+\b Each source file is compiled precisely once, to produce a single\r
+object file.\r
+\r
+\b Each binary is created by linking together some combination of\r
+those object files.\r
+\r
+Therefore, if you need to introduce functionality to a particular\r
+module which is only available in some of the tool binaries (for\r
+example, a cryptographic proxy authentication mechanism which needs\r
+to be left out of PuTTYtel to maintain its usability in\r
+crypto-hostile jurisdictions), the \e{wrong} way to do it is by\r
+adding \cw{#ifdef}s in (say) \cw{proxy.c}. This would require\r
+separate compilation of \cw{proxy.c} for PuTTY and PuTTYtel, which\r
+means that the entire \cw{Makefile}-generation architecture (see\r
+\k{udp-makefiles-auto}) would have to be significantly redesigned.\r
+Unless you are prepared to do that redesign yourself, \e{and}\r
+guarantee that it will still port to any future platforms we might\r
+decide to run on, you should not attempt this!\r
+\r
+The \e{right} way to introduce a feature like this is to put the new\r
+code in a separate source file, and (if necessary) introduce a\r
+second new source file defining the same set of functions, but\r
+defining them as stubs which don't provide the feature. Then the\r
+module whose behaviour needs to vary (\cw{proxy.c} in this example)\r
+can call the functions defined in these two modules, and it will\r
+either provide the new feature or not provide it according to which\r
+of your new modules it is linked with.\r
+\r
+Of course, object files are never shared \e{between} platforms; so\r
+it is allowable to use \cw{#ifdef} to select between platforms. This\r
+happens in \cw{puttyps.h} (choosing which of the platform-specific\r
+include files to use), and also in \cw{misc.c} (the Windows-specific\r
+\q{Minefield} memory diagnostic system). It should be used\r
+sparingly, though, if at all.\r
+\r
+\H{udp-perfection} Do as we say, not as we do\r
+\r
+The current PuTTY code probably does not conform strictly to \e{all}\r
+of the principles listed above. There may be the occasional\r
+SSH-specific piece of code in what should be a backend-independent\r
+module, or the occasional dependence on a non-standard X library\r
+function under Unix.\r
+\r
+This should not be taken as a licence to go ahead and violate the\r
+rules. Where we violate them ourselves, we're not happy about it,\r
+and we would welcome patches that fix any existing problems. Please\r
+try to help us make our code better, not worse!\r
diff --git a/putty/DOC/USING.BUT b/putty/DOC/USING.BUT
new file mode 100644 (file)
index 0000000..fa55ddd
--- /dev/null
@@ -0,0 +1,944 @@
+\define{versionidusing} \versionid $Id: using.but 9149 2011-04-08 15:52:02Z jacob $\r
+\r
+\C{using} Using PuTTY\r
+\r
+This chapter provides a general introduction to some more advanced\r
+features of PuTTY. For extreme detail and reference purposes,\r
+\k{config} is likely to contain more information.\r
+\r
+\H{using-session} During your session\r
+\r
+A lot of PuTTY's complexity and features are in the configuration\r
+panel. Once you have worked your way through that and started\r
+a session, things should be reasonably simple after that.\r
+Nevertheless, there are a few more useful features available.\r
+\r
+\S{using-selection} Copying and pasting text\r
+\r
+\I{copy and paste}Often in a PuTTY session you will find text on\r
+your terminal screen which you want to type in again. Like most\r
+other terminal emulators, PuTTY allows you to copy and paste the\r
+text rather than having to type it again. Also, copy and paste uses\r
+the \I{Windows clipboard}Windows \i{clipboard}, so that you can\r
+paste (for example) URLs into a web browser, or paste from a word\r
+processor or spreadsheet into your terminal session.\r
+\r
+PuTTY's copy and paste works entirely with the \i{mouse}. In order\r
+to copy text to the clipboard, you just click the \i{left mouse\r
+button} in the \i{terminal window}, and drag to \I{selecting text}select\r
+text. When you let go of the button, the text is \e{automatically}\r
+copied to the clipboard. You do not need to press Ctrl-C or\r
+Ctrl-Ins; in fact, if you do press Ctrl-C, PuTTY will send a Ctrl-C\r
+character down your session to the server where it will probably\r
+cause a process to be interrupted.\r
+\r
+Pasting is done using the right button (or the middle mouse button,\r
+if you have a \i{three-button mouse} and have set it up; see\r
+\k{config-mouse}). (Pressing \i{Shift-Ins}, or selecting \q{Paste}\r
+from the \I{right mouse button, with Ctrl}Ctrl+right-click\r
+\i{context menu}, have the same effect.) When\r
+you click the \i{right mouse button}, PuTTY will read whatever is in\r
+the Windows clipboard and paste it into your session, \e{exactly} as\r
+if it had been typed at the keyboard. (Therefore, be careful of\r
+pasting formatted text into an editor that does automatic indenting;\r
+you may find that the spaces pasted from the clipboard plus the\r
+spaces added by the editor add up to too many spaces and ruin the\r
+formatting. There is nothing PuTTY can do about this.)\r
+\r
+If you \i{double-click} the left mouse button, PuTTY will\r
+\I{selecting words}select a whole word. If you double-click, hold\r
+down the second click, and drag the mouse, PuTTY will select a\r
+sequence of whole words. (You can adjust precisely what PuTTY\r
+considers to be part of a word; see \k{config-charclasses}.)\r
+If you \e{triple}-click, or \i{triple-click} and drag, then\r
+PuTTY will \I{selecting lines}select a whole line or sequence of lines.\r
+\r
+If you want to select a \I{rectangular selection}rectangular region\r
+instead of selecting to the end of each line, you can do this by\r
+holding down Alt when you make your selection. You can also\r
+configure rectangular selection to be the default, and then holding\r
+down Alt gives the normal behaviour instead: see\r
+\k{config-rectselect} for details.\r
+\r
+(In some Unix environments, Alt+drag is intercepted by the window\r
+manager. Shift+Alt+drag should work for rectangular selection as\r
+well, so you could try that instead.)\r
+\r
+If you have a \i{middle mouse button}, then you can use it to\r
+\I{adjusting a selection}adjust an existing selection if you\r
+selected something slightly wrong. (If you have configured the\r
+middle mouse button to paste, then the right mouse button does this\r
+instead.) Click the button on the screen, and you can pick up the\r
+nearest end of the selection and drag it to somewhere else.\r
+\r
+It's possible for the server to ask to \I{mouse reporting}handle mouse\r
+clicks in the PuTTY window itself.  If this happens, the \i{mouse pointer}\r
+will turn into an arrow, and using the mouse to copy and paste will only\r
+work if you hold down Shift.  See \k{config-features-mouse} and\r
+\k{config-mouseshift} for details of this feature and how to configure\r
+it.\r
+\r
+\S{using-scrollback} \I{scrollback}Scrolling the screen back\r
+\r
+PuTTY keeps track of text that has scrolled up off the top of the\r
+terminal. So if something appears on the screen that you want to\r
+read, but it scrolls too fast and it's gone by the time you try to\r
+look for it, you can use the \i{scrollbar} on the right side of the\r
+window to look back up the session \i{history} and find it again.\r
+\r
+As well as using the scrollbar, you can also page the scrollback up\r
+and down by pressing \i{Shift-PgUp} and \i{Shift-PgDn}. You can\r
+scroll a line at a time using \i{Ctrl-PgUp} and \i{Ctrl-PgDn}. These\r
+are still available if you configure the scrollbar to be invisible.\r
+\r
+By default the last 200 lines scrolled off the top are\r
+preserved for you to look at. You can increase (or decrease) this\r
+value using the configuration box; see \k{config-scrollback}.\r
+\r
+\S{using-sysmenu} The \ii{System menu}\r
+\r
+If you click the left mouse button on the icon in the top left\r
+corner of PuTTY's terminal window, or click the right mouse button\r
+on the title bar, you will see the standard Windows system menu\r
+containing items like Minimise, Move, Size and Close.\r
+\r
+PuTTY's system menu contains extra program features in addition to\r
+the Windows standard options. These extra menu commands are\r
+described below.\r
+\r
+(These options are also available in a \i{context menu} brought up\r
+by holding Ctrl and clicking with the right mouse button anywhere\r
+in the \i{PuTTY window}.)\r
+\r
+\S2{using-eventlog} The PuTTY \i{Event Log}\r
+\r
+If you choose \q{Event Log} from the system menu, a small window\r
+will pop up in which PuTTY logs significant events during the\r
+connection. Most of the events in the log will probably take place\r
+during session startup, but a few can occur at any point in the\r
+session, and one or two occur right at the end.\r
+\r
+You can use the mouse to select one or more lines of the Event Log,\r
+and hit the Copy button to copy them to the \i{clipboard}. If you\r
+are reporting a bug, it's often useful to paste the contents of the\r
+Event Log into your bug report.\r
+\r
+\S2{using-specials} \ii{Special commands}\r
+\r
+Depending on the protocol used for the current session, there may be\r
+a submenu of \q{special commands}. These are protocol-specific\r
+tokens, such as a \q{break} signal, that can be sent down a\r
+connection in addition to normal data. Their precise effect is usually\r
+up to the server. Currently only Telnet, SSH, and serial connections\r
+have special commands.\r
+\r
+The \q{break} signal can also be invoked from the keyboard with\r
+\i{Ctrl-Break}.\r
+\r
+The following \I{Telnet special commands}special commands are\r
+available in Telnet:\r
+\r
+\b \I{Are You There, Telnet special command}Are You There\r
+\r
+\b \I{Break, Telnet special command}Break\r
+\r
+\b \I{Synch, Telnet special command}Synch\r
+\r
+\b \I{Erase Character, Telnet special command}Erase Character\r
+\r
+\lcont{\r
+PuTTY can also be configured to send this when the Backspace key is\r
+pressed; see \k{config-telnetkey}.\r
+}\r
+\r
+\b \I{Erase Line, Telnet special command}Erase Line\r
+\r
+\b \I{Go Ahead, Telnet special command}Go Ahead\r
+\r
+\b \I{No Operation, Telnet special command}No Operation\r
+\r
+\lcont{\r
+Should have no effect.\r
+}\r
+\r
+\b \I{Abort Process, Telnet special command}Abort Process\r
+\r
+\b \I{Abort Output, Telnet special command}Abort Output\r
+\r
+\b \I{Interrupt Process, Telnet special command}Interrupt Process\r
+\r
+\lcont{\r
+PuTTY can also be configured to send this when Ctrl-C is typed; see\r
+\k{config-telnetkey}.\r
+}\r
+\r
+\b \I{Suspend Process, Telnet special command}Suspend Process\r
+\r
+\lcont{\r
+PuTTY can also be configured to send this when Ctrl-Z is typed; see\r
+\k{config-telnetkey}.\r
+}\r
+\r
+\b \I{End Of Record, Telnet special command}End Of Record\r
+\r
+\b \I{End Of File, Telnet special command}End Of File\r
+\r
+In an SSH connection, the following \I{SSH special commands}special\r
+commands are available:\r
+\r
+\b \I{IGNORE message, SSH special command}\I{No-op, in SSH}\ii{IGNORE message}\r
+\r
+\lcont{\r
+Should have no effect.\r
+}\r
+\r
+\b \I{Repeat key exchange, SSH special command}Repeat key exchange\r
+\r
+\lcont{\r
+Only available in SSH-2. Forces a \i{repeat key exchange} immediately (and\r
+resets associated timers and counters). For more information about\r
+repeat key exchanges, see \k{config-ssh-kex-rekey}.\r
+}\r
+\r
+\b \I{Break, SSH special command}Break\r
+\r
+\lcont{\r
+Only available in SSH-2, and only during a session. Optional\r
+extension; may not be supported by server. PuTTY requests the server's\r
+default break length.\r
+}\r
+\r
+\b \I{Signal, SSH special command}Signals (SIGINT, SIGTERM etc)\r
+\r
+\lcont{\r
+Only available in SSH-2, and only during a session. Sends various\r
+POSIX signals. Not honoured by all servers.\r
+}\r
+\r
+With a serial connection, the only available special command is\r
+\I{Break, serial special command}\q{Break}.\r
+\r
+\S2{using-newsession} Starting new sessions\r
+\r
+PuTTY's system menu provides some shortcut ways to start new\r
+sessions:\r
+\r
+\b Selecting \i{\q{New Session}} will start a completely new\r
+instance of PuTTY, and bring up the configuration box as normal.\r
+\r
+\b Selecting \i{\q{Duplicate Session}} will start a session in a\r
+new window with precisely the same options as your current one -\r
+connecting to the same host using the same protocol, with all the\r
+same terminal settings and everything.\r
+\r
+\b In an inactive window, selecting \i{\q{Restart Session}} will\r
+do the same as \q{Duplicate Session}, but in the current window.\r
+\r
+\b The \i{\q{Saved Sessions} submenu} gives you quick access to any\r
+sets of stored session details you have previously saved. See\r
+\k{config-saving} for details of how to create saved sessions.\r
+\r
+\S2{using-changesettings} \I{settings, changing}Changing your\r
+session settings\r
+\r
+If you select \i{\q{Change Settings}} from the system menu, PuTTY will\r
+display a cut-down version of its initial configuration box. This\r
+allows you to adjust most properties of your current session. You\r
+can change the terminal size, the font, the actions of various\r
+keypresses, the colours, and so on.\r
+\r
+Some of the options that are available in the main configuration box\r
+are not shown in the cut-down Change Settings box. These are usually\r
+options which don't make sense to change in the middle of a session\r
+(for example, you can't switch from SSH to Telnet in mid-session).\r
+\r
+You can save the current settings to a saved session for future use\r
+from this dialog box. See \k{config-saving} for more on saved\r
+sessions.\r
+\r
+\S2{using-copyall} \i{Copy All to Clipboard}\r
+\r
+This system menu option provides a convenient way to copy the whole\r
+contents of the terminal screen (up to the last nonempty line) and\r
+scrollback to the \i{clipboard} in one go.\r
+\r
+\S2{reset-terminal} \I{scrollback, clearing}Clearing and\r
+\I{terminal, resetting}resetting the terminal\r
+\r
+The \i{\q{Clear Scrollback}} option on the system menu tells PuTTY\r
+to discard all the lines of text that have been kept after they\r
+scrolled off the top of the screen. This might be useful, for\r
+example, if you displayed sensitive information and wanted to make\r
+sure nobody could look over your shoulder and see it. (Note that\r
+this only prevents a casual user from using the scrollbar to view\r
+the information; the text is not guaranteed not to still be in\r
+PuTTY's memory.)\r
+\r
+The \i{\q{Reset Terminal}} option causes a full reset of the\r
+\i{terminal emulation}. A VT-series terminal is a complex piece of\r
+software and can easily get into a state where all the text printed\r
+becomes unreadable. (This can happen, for example, if you\r
+accidentally output a binary file to your terminal.) If this\r
+happens, selecting Reset Terminal should sort it out.\r
+\r
+\S2{using-fullscreen} \ii{Full screen} mode\r
+\r
+If you find the title bar on a maximised window to be ugly or\r
+distracting, you can select Full Screen mode to maximise PuTTY\r
+\q{even more}. When you select this, PuTTY will expand to fill the\r
+whole screen and its borders, title bar and scrollbar will\r
+disappear. (You can configure the scrollbar not to disappear in\r
+full-screen mode if you want to keep it; see \k{config-scrollback}.)\r
+\r
+When you are in full-screen mode, you can still access the \i{system\r
+menu} if you click the left mouse button in the \e{extreme} top left\r
+corner of the screen.\r
+\r
+\H{using-logging} Creating a \i{log file} of your \I{session\r
+log}session\r
+\r
+For some purposes you may find you want to log everything that\r
+appears on your screen. You can do this using the \q{Logging}\r
+panel in the configuration box.\r
+\r
+To begin a session log, select \q{Change Settings} from the system\r
+menu and go to the Logging panel. Enter a log file name, and select\r
+a logging mode. (You can log all session output including the\r
+terminal \i{control sequence}s, or you can just log the printable text.\r
+It depends what you want the log for.) Click \q{Apply} and your log\r
+will be started. Later on, you can go back to the Logging panel and\r
+select \q{Logging turned off completely} to stop logging; then PuTTY\r
+will close the log file and you can safely read it.\r
+\r
+See \k{config-logging} for more details and options.\r
+\r
+\H{using-translation} Altering your \i{character set} configuration\r
+\r
+If you find that special characters (\i{accented characters}, for\r
+example, or \i{line-drawing characters}) are not being displayed\r
+correctly in your PuTTY session, it may be that PuTTY is interpreting\r
+the characters sent by the server according to the wrong \e{character\r
+set}. There are a lot of different character sets available, so it's\r
+entirely possible for this to happen.\r
+\r
+If you click \q{Change Settings} and look at the \q{Translation}\r
+panel, you should see a large number of character sets which you can\r
+select, and other related options. Now all you need is to find out\r
+which of them you want! (See \k{config-translation} for more\r
+information.)\r
+\r
+\H{using-x-forwarding} Using \i{X11 forwarding} in SSH\r
+\r
+The SSH protocol has the ability to securely forward X Window System\r
+applications over your encrypted SSH connection, so that you can run\r
+an application on the SSH server machine and have it put its windows\r
+up on your local machine without sending any X network traffic in\r
+the clear.\r
+\r
+In order to use this feature, you will need an X display server for\r
+your Windows machine, such as Cygwin/X, X-Win32, or Exceed. This will probably\r
+install itself as display number 0 on your local machine; if it\r
+doesn't, the manual for the \i{X server} should tell you what it\r
+does do.\r
+\r
+You should then tick the \q{Enable X11 forwarding} box in the\r
+X11 panel (see \k{config-ssh-x11}) before starting your SSH\r
+session. The \i{\q{X display location}} box is blank by default, which\r
+means that PuTTY will try to use a sensible default such as \c{:0},\r
+which is the usual display location where your X server will be\r
+installed. If that needs changing, then change it.\r
+\r
+Now you should be able to log in to the SSH server as normal. To\r
+check that X forwarding has been successfully negotiated during\r
+connection startup, you can check the PuTTY Event Log (see\r
+\k{using-eventlog}). It should say something like this:\r
+\r
+\c 2001-12-05 17:22:01 Requesting X11 forwarding\r
+\c 2001-12-05 17:22:02 X11 forwarding enabled\r
+\r
+If the remote system is Unix or Unix-like, you should also be able\r
+to see that the \i{\c{DISPLAY} environment variable} has been set to\r
+point at display 10 or above on the SSH server machine itself:\r
+\r
+\c fred@unixbox:~$ echo $DISPLAY\r
+\c unixbox:10.0\r
+\r
+If this works, you should then be able to run X applications in the\r
+remote session and have them display their windows on your PC.\r
+\r
+For more options relating to X11 forwarding, see \k{config-ssh-x11}.\r
+\r
+\H{using-port-forwarding} Using \i{port forwarding} in SSH\r
+\r
+The SSH protocol has the ability to forward arbitrary \i{network\r
+connection}s over your encrypted SSH connection, to avoid the network\r
+traffic being sent in clear. For example, you could use this to\r
+connect from your home computer to a \i{POP-3} server on a remote\r
+machine without your POP-3 password being visible to network\r
+sniffers.\r
+\r
+In order to use port forwarding to \I{local port forwarding}connect\r
+from your local machine to a port on a remote server, you need to:\r
+\r
+\b Choose a \i{port number} on your local machine where PuTTY should\r
+listen for incoming connections. There are likely to be plenty of\r
+unused port numbers above 3000. (You can also use a local loopback\r
+address here; see below for more details.)\r
+\r
+\b Now, before you start your SSH connection, go to the Tunnels\r
+panel (see \k{config-ssh-portfwd}). Make sure the \q{Local} radio\r
+button is set. Enter the local port number into the \q{Source port}\r
+box. Enter the destination host name and port number into the\r
+\q{Destination} box, separated by a colon (for example,\r
+\c{popserver.example.com:110} to connect to a POP-3 server).\r
+\r
+\b Now click the \q{Add} button. The details of your port forwarding\r
+should appear in the list box.\r
+\r
+Now start your session and log in. (Port forwarding will not be\r
+enabled until after you have logged in; otherwise it would be easy\r
+to perform completely anonymous network attacks, and gain access to\r
+anyone's virtual private network.) To check that PuTTY has set up\r
+the port forwarding correctly, you can look at the PuTTY Event Log\r
+(see \k{using-eventlog}). It should say something like this:\r
+\r
+\c 2001-12-05 17:22:10 Local port 3110 forwarding to\r
+\c          popserver.example.com:110\r
+\r
+Now if you connect to the source port number on your local PC, you\r
+should find that it answers you exactly as if it were the service\r
+running on the destination machine. So in this example, you could\r
+then configure an e-mail client to use \c{localhost:3110} as a POP-3\r
+server instead of \c{popserver.example.com:110}. (Of course, the\r
+forwarding will stop happening when your PuTTY session closes down.)\r
+\r
+You can also forward ports in the other direction: arrange for a\r
+particular port number on the \e{server} machine to be \I{remote\r
+port forwarding}forwarded back to your PC as a connection to a\r
+service on your PC or near it.\r
+To do this, just select the \q{Remote} radio button instead of the\r
+\q{Local} one. The \q{Source port} box will now specify a port\r
+number on the \e{server} (note that most servers will not allow you\r
+to use \I{privileged port}port numbers under 1024 for this purpose).\r
+\r
+An alternative way to forward local connections to remote hosts is\r
+to use \I{dynamic port forwarding}dynamic SOCKS proxying. In this\r
+mode, PuTTY acts as a SOCKS server, which SOCKS-aware programs can\r
+connect to and open forwarded connections to the destination of their\r
+choice, so this can be an alternative to long lists of static\r
+forwardings. To use this mode, you will need to select the \q{Dynamic}\r
+radio button instead of \q{Local}, and then you should not enter\r
+anything into the \q{Destination} box (it will be ignored). PuTTY will\r
+then listen for SOCKS connections on the port you have specified.\r
+Most \i{web browsers} can be configured to connect to this SOCKS proxy\r
+service; also, you can forward other PuTTY connections through it by\r
+setting up the Proxy control panel (see \k{config-proxy} for details).\r
+\r
+The source port for a forwarded connection usually does not accept\r
+connections from any machine except the \I{localhost}SSH client or\r
+server machine itself (for local and remote forwardings respectively).\r
+There are controls in the Tunnels panel to change this:\r
+\r
+\b The \q{Local ports accept connections from other hosts} option\r
+allows you to set up local-to-remote port forwardings (including\r
+dynamic port forwardings) in such a way that machines other than\r
+your client PC can connect to the forwarded port.\r
+\r
+\b The \q{Remote ports do the same} option does the same thing for\r
+remote-to-local port forwardings (so that machines other than the\r
+SSH server machine can connect to the forwarded port.) Note that\r
+this feature is only available in the SSH-2 protocol, and not all\r
+SSH-2 servers honour it (in \i{OpenSSH}, for example, it's usually\r
+disabled by default).\r
+\r
+You can also specify an \i{IP address} to \I{listen address}listen\r
+on. Typically a Windows machine can be asked to listen on any single\r
+IP address in the \cw{127.*.*.*} range, and all of these are\r
+\i{loopback address}es available only to the local machine. So if\r
+you forward (for example) \c{127.0.0.5:79} to a remote machine's\r
+\i\cw{finger} port, then you should be able to run commands such as\r
+\c{finger fred@127.0.0.5}.\r
+This can be useful if the program connecting to the forwarded port\r
+doesn't allow you to change the port number it uses. This feature is\r
+available for local-to-remote forwarded ports; SSH-1 is unable to\r
+support it for remote-to-local ports, while SSH-2 can support it in\r
+theory but servers will not necessarily cooperate.\r
+\r
+(Note that if you're using Windows XP Service Pack 2, you may need\r
+to obtain a fix from Microsoft in order to use addresses like\r
+\cw{127.0.0.5} - see \k{faq-alternate-localhost}.)\r
+\r
+For more options relating to port forwarding, see\r
+\k{config-ssh-portfwd}.\r
+\r
+If the connection you are forwarding over SSH is itself a second SSH\r
+connection made by another copy of PuTTY, you might find the\r
+\q{logical host name} configuration option useful to warn PuTTY of\r
+which host key it should be expecting. See \k{config-loghost} for\r
+details of this.\r
+\r
+\H{using-rawprot} Making \i{raw TCP connections}\r
+\r
+A lot of \I{debugging Internet protocols}Internet protocols are\r
+composed of commands and responses in plain text. For example,\r
+\i{SMTP} (the protocol used to transfer e-mail), \i{NNTP} (the\r
+protocol used to transfer Usenet news), and \i{HTTP} (the protocol\r
+used to serve Web pages) all consist of commands in readable plain\r
+text.\r
+\r
+Sometimes it can be useful to connect directly to one of these\r
+services and speak the protocol \q{by hand}, by typing protocol\r
+commands and watching the responses. On Unix machines, you can do\r
+this using the system's \c{telnet} command to connect to the right\r
+port number. For example, \c{telnet mailserver.example.com 25} might\r
+enable you to talk directly to the SMTP service running on a mail\r
+server.\r
+\r
+Although the Unix \c{telnet} program provides this functionality,\r
+the protocol being used is not really Telnet. Really there is no\r
+actual protocol at all; the bytes sent down the connection are\r
+exactly the ones you type, and the bytes shown on the screen are\r
+exactly the ones sent by the server. Unix \c{telnet} will attempt to\r
+detect or guess whether the service it is talking to is a real\r
+Telnet service or not; PuTTY prefers to be told for certain.\r
+\r
+In order to make a debugging connection to a service of this type,\r
+you simply select the fourth protocol name, \I{\q{Raw}\r
+protocol}\q{Raw}, from the \q{Protocol} buttons in the \q{Session}\r
+configuration panel. (See \k{config-hostname}.) You can then enter a\r
+host name and a port number, and make the connection.\r
+\r
+\H{using-serial} Connecting to a local serial line\r
+\r
+PuTTY can connect directly to a local serial line as an alternative\r
+to making a network connection. In this mode, text typed into the\r
+PuTTY window will be sent straight out of your computer's serial\r
+port, and data received through that port will be displayed in the\r
+PuTTY window. You might use this mode, for example, if your serial\r
+port is connected to another computer which has a serial connection.\r
+\r
+To make a connection of this type, simply select \q{Serial} from the\r
+\q{Connection type} radio buttons on the \q{Session} configuration\r
+panel (see \k{config-hostname}). The \q{Host Name} and \q{Port}\r
+boxes will transform into \q{Serial line} and \q{Speed}, allowing\r
+you to specify which serial line to use (if your computer has more\r
+than one) and what speed (baud rate) to use when transferring data.\r
+For further configuration options (data bits, stop bits, parity,\r
+flow control), you can use the \q{Serial} configuration panel (see\r
+\k{config-serial}).\r
+\r
+After you start up PuTTY in serial mode, you might find that you\r
+have to make the first move, by sending some data out of the serial\r
+line in order to notify the device at the other end that someone is\r
+there for it to talk to. This probably depends on the device. If you\r
+start up a PuTTY serial session and nothing appears in the window,\r
+try pressing Return a few times and see if that helps.\r
+\r
+A serial line provides no well defined means for one end of the\r
+connection to notify the other that the connection is finished.\r
+Therefore, PuTTY in serial mode will remain connected until you\r
+close the window using the close button.\r
+\r
+\H{using-cmdline} The PuTTY command line\r
+\r
+PuTTY can be made to do various things without user intervention by\r
+supplying \i{command-line arguments} (e.g., from a \i{command prompt\r
+window}, or a \i{Windows shortcut}).\r
+\r
+\S{using-cmdline-session} Starting a session from the command line\r
+\r
+\I\c{-ssh}\I\c{-telnet}\I\c{-rlogin}\I\c{-raw}\I\c{-serial}These\r
+options allow you to bypass the configuration window and launch\r
+straight into a session.\r
+\r
+To start a connection to a server called \c{host}:\r
+\r
+\c putty.exe [-ssh | -telnet | -rlogin | -raw] [user@]host\r
+\r
+If this syntax is used, settings are taken from the \i{Default Settings}\r
+(see \k{config-saving}); \c{user} overrides these settings if\r
+supplied. Also, you can specify a protocol, which will override the\r
+default protocol (see \k{using-cmdline-protocol}).\r
+\r
+For telnet sessions, the following alternative syntax is supported\r
+(this makes PuTTY suitable for use as a URL handler for \i{telnet\r
+URLs} in \i{web browsers}):\r
+\r
+\c putty.exe telnet://host[:port]/\r
+\r
+To start a connection to a serial port, e.g. COM1:\r
+\r
+\c putty.exe -serial com1\r
+\r
+In order to start an existing saved session called \c{sessionname},\r
+use the \c{-load} option (described in \k{using-cmdline-load}).\r
+\r
+\c putty.exe -load "session name"\r
+\r
+\S{using-cleanup} \i\c{-cleanup}\r
+\r
+\cfg{winhelp-topic}{options.cleanup}\r
+\r
+If invoked with the \c{-cleanup} option, rather than running as\r
+normal, PuTTY will remove its \I{removing registry entries}registry\r
+entries and \i{random seed file} from the local machine (after\r
+confirming with the user).\r
+\r
+Note that on \i{multi-user systems}, \c{-cleanup} only removes\r
+registry entries and files associated with the currently logged-in\r
+user.\r
+\r
+\S{using-general-opts} Standard command-line options\r
+\r
+PuTTY and its associated tools support a range of command-line\r
+options, most of which are consistent across all the tools. This\r
+section lists the available options in all tools. Options which are\r
+specific to a particular tool are covered in the chapter about that\r
+tool.\r
+\r
+\S2{using-cmdline-load} \i\c{-load}: load a saved session\r
+\r
+\I{saved sessions, loading from command line}The \c{-load} option\r
+causes PuTTY to load configuration details out of a saved session.\r
+If these details include a host name, then this option is all you\r
+need to make PuTTY start a session.\r
+\r
+You need double quotes around the session name if it contains spaces.\r
+\r
+If you want to create a \i{Windows shortcut} to start a PuTTY saved\r
+session, this is the option you should use: your shortcut should\r
+call something like\r
+\r
+\c d:\path\to\putty.exe -load "my session"\r
+\r
+(Note that PuTTY itself supports an alternative form of this option,\r
+for backwards compatibility. If you execute \i\c{putty @sessionname}\r
+it will have the same effect as \c{putty -load "sessionname"}. With\r
+the \c{@} form, no double quotes are required, and the \c{@} sign\r
+must be the very first thing on the command line. This form of the\r
+option is deprecated.)\r
+\r
+\S2{using-cmdline-protocol} Selecting a protocol: \c{-ssh},\r
+\c{-telnet}, \c{-rlogin}, \c{-raw} \c{-serial}\r
+\r
+To choose which protocol you want to connect with, you can use one\r
+of these options:\r
+\r
+\b \i\c{-ssh} selects the SSH protocol.\r
+\r
+\b \i\c{-telnet} selects the Telnet protocol.\r
+\r
+\b \i\c{-rlogin} selects the Rlogin protocol.\r
+\r
+\b \i\c{-raw} selects the raw protocol.\r
+\r
+\b \i\c{-serial} selects a serial connection.\r
+\r
+These options are not available in the file transfer tools PSCP and\r
+PSFTP (which only work with the SSH protocol).\r
+\r
+These options are equivalent to the \i{protocol selection} buttons\r
+in the Session panel of the PuTTY configuration box (see\r
+\k{config-hostname}).\r
+\r
+\S2{using-cmdline-v} \i\c{-v}: increase verbosity\r
+\r
+\I{verbose mode}Most of the PuTTY tools can be made to tell you more\r
+about what they are doing by supplying the \c{-v} option. If you are\r
+having trouble when making a connection, or you're simply curious,\r
+you can turn this switch on and hope to find out more about what is\r
+happening.\r
+\r
+\S2{using-cmdline-l} \i\c{-l}: specify a \i{login name}\r
+\r
+You can specify the user name to log in as on the remote server\r
+using the \c{-l} option. For example, \c{plink login.example.com -l\r
+fred}.\r
+\r
+These options are equivalent to the username selection box in the\r
+Connection panel of the PuTTY configuration box (see\r
+\k{config-username}).\r
+\r
+\S2{using-cmdline-portfwd} \I{-L-upper}\c{-L}, \I{-R-upper}\c{-R}\r
+and \I{-D-upper}\c{-D}: set up \i{port forwardings}\r
+\r
+As well as setting up port forwardings in the PuTTY configuration\r
+(see \k{config-ssh-portfwd}), you can also set up forwardings on the\r
+command line. The command-line options work just like the ones in\r
+Unix \c{ssh} programs.\r
+\r
+To \I{local port forwarding}forward a local port (say 5110) to a\r
+remote destination (say \cw{popserver.example.com} port 110), you\r
+can write something like one of these:\r
+\r
+\c putty -L 5110:popserver.example.com:110 -load mysession\r
+\c plink mysession -L 5110:popserver.example.com:110\r
+\r
+To forward a \I{remote port forwarding}remote port to a local\r
+destination, just use the \c{-R} option instead of \c{-L}:\r
+\r
+\c putty -R 5023:mytelnetserver.myhouse.org:23 -load mysession\r
+\c plink mysession -R 5023:mytelnetserver.myhouse.org:23\r
+\r
+To \I{listen address}specify an IP address for the listening end of the\r
+tunnel, prepend it to the argument:\r
+\r
+\c plink -L 127.0.0.5:23:localhost:23 myhost\r
+\r
+To set up \I{dynamic port forwarding}SOCKS-based dynamic port\r
+forwarding on a local port, use the \c{-D} option. For this one you\r
+only have to pass the port number:\r
+\r
+\c putty -D 4096 -load mysession\r
+\r
+For general information on port forwarding, see\r
+\k{using-port-forwarding}.\r
+\r
+These options are not available in the file transfer tools PSCP and\r
+PSFTP.\r
+\r
+\S2{using-cmdline-m} \i\c{-m}: \I{reading commands from a file}read\r
+a remote command or script from a file\r
+\r
+The \i\c{-m} option performs a similar function to the \q{\ii{Remote\r
+command}} box in the SSH panel of the PuTTY configuration box (see\r
+\k{config-command}). However, the \c{-m} option expects to be given\r
+a local file name, and it will read a command from that file.\r
+\r
+With some servers (particularly Unix systems), you can even put\r
+multiple lines in this file and execute more than one command in\r
+sequence, or a whole shell script; but this is arguably an abuse, and\r
+cannot be expected to work on all servers. In particular, it is known\r
+\e{not} to work with certain \q{embedded} servers, such as \i{Cisco}\r
+routers.\r
+\r
+This option is not available in the file transfer tools PSCP and\r
+PSFTP.\r
+\r
+\S2{using-cmdline-p} \I{-P-upper}\c{-P}: specify a \i{port number}\r
+\r
+The \c{-P} option is used to specify the port number to connect to. If\r
+you have a Telnet server running on port 9696 of a machine instead of\r
+port 23, for example:\r
+\r
+\c putty -telnet -P 9696 host.name\r
+\c plink -telnet -P 9696 host.name\r
+\r
+(Note that this option is more useful in Plink than in PuTTY,\r
+because in PuTTY you can write \c{putty -telnet host.name 9696} in\r
+any case.)\r
+\r
+This option is equivalent to the port number control in the Session\r
+panel of the PuTTY configuration box (see \k{config-hostname}).\r
+\r
+\S2{using-cmdline-pw} \i\c{-pw}: specify a \i{password}\r
+\r
+A simple way to automate a remote login is to supply your password\r
+on the command line. This is \e{not recommended} for reasons of\r
+security. If you possibly can, we recommend you set up public-key\r
+authentication instead. See \k{pubkey} for details.\r
+\r
+Note that the \c{-pw} option only works when you are using the SSH\r
+protocol. Due to fundamental limitations of Telnet and Rlogin, these\r
+protocols do not support automated password authentication.\r
+\r
+\S2{using-cmdline-agentauth} \i\c{-agent} and \i\c{-noagent}:\r
+control use of Pageant for authentication\r
+\r
+The \c{-agent} option turns on SSH authentication using Pageant, and\r
+\c{-noagent} turns it off. These options are only meaningful if you\r
+are using SSH.\r
+\r
+See \k{pageant} for general information on \i{Pageant}.\r
+\r
+These options are equivalent to the agent authentication checkbox in\r
+the Auth panel of the PuTTY configuration box (see\r
+\k{config-ssh-tryagent}).\r
+\r
+\S2{using-cmdline-agent} \I{-A-upper}\c{-A} and \i\c{-a}: control \i{agent\r
+forwarding}\r
+\r
+The \c{-A} option turns on SSH agent forwarding, and \c{-a} turns it\r
+off. These options are only meaningful if you are using SSH.\r
+\r
+See \k{pageant} for general information on \i{Pageant}, and\r
+\k{pageant-forward} for information on agent forwarding. Note that\r
+there is a security risk involved with enabling this option; see\r
+\k{pageant-security} for details.\r
+\r
+These options are equivalent to the agent forwarding checkbox in the\r
+Auth panel of the PuTTY configuration box (see \k{config-ssh-agentfwd}).\r
+\r
+These options are not available in the file transfer tools PSCP and\r
+PSFTP.\r
+\r
+\S2{using-cmdline-x11} \I{-X-upper}\c{-X} and \i\c{-x}: control \i{X11\r
+forwarding}\r
+\r
+The \c{-X} option turns on X11 forwarding in SSH, and \c{-x} turns\r
+it off. These options are only meaningful if you are using SSH.\r
+\r
+For information on X11 forwarding, see \k{using-x-forwarding}.\r
+\r
+These options are equivalent to the X11 forwarding checkbox in the\r
+X11 panel of the PuTTY configuration box (see \k{config-ssh-x11}).\r
+\r
+These options are not available in the file transfer tools PSCP and\r
+PSFTP.\r
+\r
+\S2{using-cmdline-pty} \i\c{-t} and \I{-T-upper}\c{-T}: control\r
+\i{pseudo-terminal allocation}\r
+\r
+The \c{-t} option ensures PuTTY attempts to allocate a\r
+pseudo-terminal at the server, and \c{-T} stops it from allocating\r
+one. These options are only meaningful if you are using SSH.\r
+\r
+These options are equivalent to the \q{Don't allocate a\r
+pseudo-terminal} checkbox in the SSH panel of the PuTTY\r
+configuration box (see \k{config-ssh-pty}).\r
+\r
+These options are not available in the file transfer tools PSCP and\r
+PSFTP.\r
+\r
+\S2{using-cmdline-noshell} \I{-N-upper}\c{-N}: suppress starting a\r
+\I{suppressing remote shell}shell or command\r
+\r
+The \c{-N} option prevents PuTTY from attempting to start a shell or\r
+command on the remote server. You might want to use this option if\r
+you are only using the SSH connection for port forwarding, and your\r
+user account on the server does not have the ability to run a shell.\r
+\r
+This feature is only available in SSH protocol version 2 (since the\r
+version 1 protocol assumes you will always want to run a shell).\r
+\r
+This option is equivalent to the \q{Don't start a shell or command\r
+at all} checkbox in the SSH panel of the PuTTY configuration box\r
+(see \k{config-ssh-noshell}).\r
+\r
+This option is not available in the file transfer tools PSCP and\r
+PSFTP.\r
+\r
+\S2{using-cmdline-ncmode} \I{-nc}\c{-nc}: make a \i{remote network\r
+connection} in place of a remote shell or command\r
+\r
+The \c{-nc} option prevents Plink (or PuTTY) from attempting to\r
+start a shell or command on the remote server. Instead, it will\r
+instruct the remote server to open a network connection to a host\r
+name and port number specified by you, and treat that network\r
+connection as if it were the main session.\r
+\r
+You specify a host and port as an argument to the \c{-nc} option,\r
+with a colon separating the host name from the port number, like\r
+this:\r
+\r
+\c plink host1.example.com -nc host2.example.com:1234\r
+\r
+You might want to use this feature if you needed to make an SSH\r
+connection to a target host which you can only reach by going\r
+through a proxy host, and rather than using port forwarding you\r
+prefer to use the local proxy feature (see \k{config-proxy-type} for\r
+more about local proxies). In this situation you might select\r
+\q{Local} proxy type, set your local proxy command to be \cq{plink\r
+%proxyhost -nc %host:%port}, enter the target host name on the\r
+Session panel, and enter the directly reachable proxy host name on\r
+the Proxy panel.\r
+\r
+This feature is only available in SSH protocol version 2 (since the\r
+version 1 protocol assumes you will always want to run a shell). It\r
+is not available in the file transfer tools PSCP and PSFTP. It is\r
+available in PuTTY itself, although it is unlikely to be very useful\r
+in any tool other than Plink. Also, \c{-nc} uses the same server\r
+functionality as port forwarding, so it will not work if your server\r
+administrator has disabled port forwarding.\r
+\r
+(The option is named \c{-nc} after the Unix program\r
+\W{http://www.vulnwatch.org/netcat/}\c{nc}, short for \q{netcat}.\r
+The command \cq{plink host1 -nc host2:port} is very similar in\r
+functionality to \cq{plink host1 nc host2 port}, which invokes\r
+\c{nc} on the server and tells it to connect to the specified\r
+destination. However, Plink's built-in \c{-nc} option does not\r
+depend on the \c{nc} program being installed on the server.)\r
+\r
+\S2{using-cmdline-compress} \I{-C-upper}\c{-C}: enable \i{compression}\r
+\r
+The \c{-C} option enables compression of the data sent across the\r
+network. This option is only meaningful if you are using SSH.\r
+\r
+This option is equivalent to the \q{Enable compression} checkbox in\r
+the SSH panel of the PuTTY configuration box (see\r
+\k{config-ssh-comp}).\r
+\r
+\S2{using-cmdline-sshprot} \i\c{-1} and \i\c{-2}: specify an \i{SSH\r
+protocol version}\r
+\r
+The \c{-1} and \c{-2} options force PuTTY to use version \I{SSH-1}1\r
+or version \I{SSH-2}2 of the SSH protocol. These options are only\r
+meaningful if you are using SSH.\r
+\r
+These options are equivalent to selecting your preferred SSH\r
+protocol version as \q{1 only} or \q{2 only} in the SSH panel of the\r
+PuTTY configuration box (see \k{config-ssh-prot}).\r
+\r
+\S2{using-cmdline-ipversion} \i\c{-4} and \i\c{-6}: specify an\r
+\i{Internet protocol version}\r
+\r
+The \c{-4} and \c{-6} options force PuTTY to use the older Internet\r
+protocol \i{IPv4} or the newer \i{IPv6} for most outgoing\r
+connections.\r
+\r
+These options are equivalent to selecting your preferred Internet\r
+protocol version as \q{IPv4} or \q{IPv6} in the Connection panel of\r
+the PuTTY configuration box (see \k{config-address-family}).\r
+\r
+\S2{using-cmdline-identity} \i\c{-i}: specify an SSH \i{private key}\r
+\r
+The \c{-i} option allows you to specify the name of a private key\r
+file in \c{*.\i{PPK}} format which PuTTY will use to authenticate with the\r
+server. This option is only meaningful if you are using SSH.\r
+\r
+For general information on \i{public-key authentication}, see\r
+\k{pubkey}.\r
+\r
+This option is equivalent to the \q{Private key file for\r
+authentication} box in the Auth panel of the PuTTY configuration box\r
+(see \k{config-ssh-privkey}).\r
+\r
+\S2{using-cmdline-loghost} \i\c{-loghost}: specify a \i{logical host\r
+name}\r
+\r
+This option overrides PuTTY's normal SSH host key caching policy by\r
+telling it the name of the host you expect your connection to end up\r
+at (in cases where this differs from the location PuTTY thinks it's\r
+connecting to). It can be a plain host name, or a host name followed\r
+by a colon and a port number. See \k{config-loghost} for more detail\r
+on this.\r
+\r
+\S2{using-cmdline-pgpfp} \i\c{-pgpfp}: display \i{PGP key fingerprint}s\r
+\r
+This option causes the PuTTY tools not to run as normal, but instead\r
+to display the fingerprints of the PuTTY PGP Master Keys, in order to\r
+aid with \i{verifying new versions}. See \k{pgpkeys} for more information.\r
+\r
+\S2{using-cmdline-sercfg} \i\c{-sercfg}: specify serial port\r
+\i{configuration}\r
+\r
+This option specifies the configuration parameters for the serial\r
+port (baud rate, stop bits etc). Its argument is interpreted as a\r
+comma-separated list of configuration options, which can be as\r
+follows:\r
+\r
+\b Any single digit from 5 to 9 sets the number of data bits.\r
+\r
+\b \cq{1}, \cq{1.5} or \cq{2} sets the number of stop bits.\r
+\r
+\b Any other numeric string is interpreted as a baud rate.\r
+\r
+\b A single lower-case letter specifies the parity: \cq{n} for none,\r
+\cq{o} for odd, \cq{e} for even, \cq{m} for mark and \cq{s} for space.\r
+\r
+\b A single upper-case letter specifies the flow control: \cq{N} for\r
+none, \cq{X} for XON/XOFF, \cq{R} for RTS/CTS and \cq{D} for\r
+DSR/DTR.\r
+\r
+For example, \cq{-sercfg 19200,8,n,1,N} denotes a baud rate of\r
+19200, 8 data bits, no parity, 1 stop bit and no flow control.\r
diff --git a/putty/DOC/VIDS.BUT b/putty/DOC/VIDS.BUT
new file mode 100644 (file)
index 0000000..7f35118
--- /dev/null
@@ -0,0 +1,36 @@
+\# Invoke the versionid macros defined in all the other manual\r
+\# chapter files.\r
+\r
+\versionidblurb\r
+\r
+\versionidintro\r
+\r
+\versionidgs\r
+\r
+\versionidusing\r
+\r
+\versionidconfig\r
+\r
+\versionidpscp\r
+\r
+\versionidpsftp\r
+\r
+\versionidplink\r
+\r
+\versionidpubkey\r
+\r
+\versionidpageant\r
+\r
+\versioniderrors\r
+\r
+\versionidfaq\r
+\r
+\versionidfeedback\r
+\r
+\versionidlicence\r
+\r
+\versionidudp\r
+\r
+\versionidpgpkeys\r
+\r
+\versionidindex\r
diff --git a/putty/ICONS/CICON.PL b/putty/ICONS/CICON.PL
new file mode 100644 (file)
index 0000000..7bce7d3
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/perl \r
+\r
+# Given a list of input PNGs, create a C source file containing a\r
+# const array of XPMs, named by a given C identifier.\r
+\r
+$id = shift @ARGV;\r
+$k = 0;\r
+@xpms = ();\r
+foreach $f (@ARGV) {\r
+  # XPM format is generated directly by ImageMagick, so that's easy\r
+  # enough. We just have to adjust the declaration line so that it\r
+  # has the right name, linkage and storage class.\r
+  @lines = ();\r
+  open XPM, "convert $f xpm:- |";\r
+  push @lines, $_ while <XPM>;\r
+  close XPM;\r
+  die "XPM from $f in unexpected format\n" unless $lines[1] =~ /^static.*\{$/;\r
+  $lines[1] = "static const char *const ${id}_$k"."[] = {\n";\r
+  $k++;\r
+  push @xpms, @lines, "\n";\r
+}\r
+\r
+# Now output.\r
+foreach $line (@xpms) { print $line; }\r
+print "const char *const *const ${id}[] = {\n";\r
+for ($i = 0; $i < $k; $i++) { print "    ${id}_$i,\n"; }\r
+print "};\n";\r
+print "const int n_${id} = $k;\n";\r
diff --git a/putty/ICONS/ICON.PL b/putty/ICONS/ICON.PL
new file mode 100644 (file)
index 0000000..aefd3fa
--- /dev/null
@@ -0,0 +1,270 @@
+#!/usr/bin/perl \r
+\r
+# Take a collection of input image files and convert them into a\r
+# multi-resolution Windows .ICO icon file.\r
+#\r
+# The input images can be treated as having four different colour\r
+# depths:\r
+#\r
+#  - 24-bit true colour\r
+#  - 8-bit with custom palette\r
+#  - 4-bit using the Windows 16-colour palette (see comment below\r
+#    for details)\r
+#  - 1-bit using black and white only.\r
+#\r
+# The images can be supplied in any input format acceptable to\r
+# ImageMagick, but their actual colour usage must already be\r
+# appropriate for the specified mode; this script will not do any\r
+# substantive conversion. So if an image intended to be used in 4-\r
+# or 1-bit mode contains any colour not in the appropriate fixed\r
+# palette, that's a fatal error; if an image to be used in 8-bit\r
+# mode contains more than 256 distinct colours, that's also a fatal\r
+# error.\r
+#\r
+# Command-line syntax is:\r
+#\r
+#   icon.pl -depth imagefile [imagefile...] [-depth imagefile [imagefile...]]\r
+#\r
+# where `-depth' is one of `-24', `-8', `-4' or `-1', and tells the\r
+# script how to treat all the image files given after that option\r
+# until the next depth option. For example, you might execute\r
+#\r
+#   icon.pl -24 48x48x24.png 32x32x24.png -8 32x32x8.png -1 monochrome.png\r
+#\r
+# to build an icon file containing two differently sized 24-bit\r
+# images, one 8-bit image and one black and white image.\r
+#\r
+# Windows .ICO files support a 1-bit alpha channel on all these\r
+# image types. That is, any pixel can be either opaque or fully\r
+# transparent, but not partially transparent. The alpha channel is\r
+# separate from the main image data, meaning that `transparent' is\r
+# not required to take up a palette entry. (So an 8-bit image can\r
+# have 256 distinct _opaque_ colours, plus transparent pixels as\r
+# well.) If the input images have alpha channels, they will be used\r
+# to determine which pixels of the icon are transparent, by simple\r
+# quantisation half way up (e.g. in a PNG image with an 8-bit alpha\r
+# channel, alpha values of 00-7F will be mapped to transparent\r
+# pixels, and 80-FF will become opaque).\r
+\r
+# The Windows 16-colour palette consists of:\r
+#  - the eight corners of the colour cube (000000, 0000FF, 00FF00,\r
+#    00FFFF, FF0000, FF00FF, FFFF00, FFFFFF)\r
+#  - dim versions of the seven non-black corners, at 128/255 of the\r
+#    brightness (000080, 008000, 008080, 800000, 800080, 808000,\r
+#    808080)\r
+#  - light grey at 192/255 of full brightness (C0C0C0).\r
+%win16pal = (\r
+    "\x00\x00\x00\x00" => 0,\r
+    "\x00\x00\x80\x00" => 1,\r
+    "\x00\x80\x00\x00" => 2,\r
+    "\x00\x80\x80\x00" => 3,\r
+    "\x80\x00\x00\x00" => 4,\r
+    "\x80\x00\x80\x00" => 5,\r
+    "\x80\x80\x00\x00" => 6,\r
+    "\xC0\xC0\xC0\x00" => 7,\r
+    "\x80\x80\x80\x00" => 8,\r
+    "\x00\x00\xFF\x00" => 9,\r
+    "\x00\xFF\x00\x00" => 10,\r
+    "\x00\xFF\xFF\x00" => 11,\r
+    "\xFF\x00\x00\x00" => 12,\r
+    "\xFF\x00\xFF\x00" => 13,\r
+    "\xFF\xFF\x00\x00" => 14,\r
+    "\xFF\xFF\xFF\x00" => 15,\r
+);\r
+@win16pal = sort { $win16pal{$a} <=> $win16pal{$b} } keys %win16pal;\r
+\r
+# The black and white palette consists of black (000000) and white\r
+# (FFFFFF), obviously.\r
+%win2pal = (\r
+    "\x00\x00\x00\x00" => 0,\r
+    "\xFF\xFF\xFF\x00" => 1,\r
+);\r
+@win2pal = sort { $win16pal{$a} <=> $win2pal{$b} } keys %win2pal;\r
+\r
+@hdr = ();\r
+@dat = ();\r
+\r
+$depth = undef;\r
+foreach $_ (@ARGV) {\r
+    if (/^-(24|8|4|1)$/) {\r
+       $depth = $1;\r
+    } elsif (defined $depth) {\r
+       &readicon($_, $depth);\r
+    } else {\r
+       $usage = 1;\r
+    }\r
+}\r
+if ($usage || length @hdr == 0) {\r
+    print "usage: icon.pl ( -24 | -8 | -4 | -1 ) image [image...]\n";\r
+    print "             [ ( -24 | -8 | -4 | -1 ) image [image...] ...]\n";\r
+    exit 0;\r
+}\r
+\r
+# Now write out the output icon file.\r
+print pack "vvv", 0, 1, scalar @hdr; # file-level header\r
+$filepos = 6 + 16 * scalar @hdr;\r
+for ($i = 0; $i < scalar @hdr; $i++) {\r
+    print $hdr[$i];\r
+    print pack "V", $filepos;\r
+    $filepos += length($dat[$i]);\r
+}\r
+for ($i = 0; $i < scalar @hdr; $i++) {\r
+    print $dat[$i];\r
+}\r
+\r
+sub readicon {\r
+    my $filename = shift @_;\r
+    my $depth = shift @_;\r
+    my $pix;\r
+    my $i;\r
+    my %pal;\r
+\r
+    # Determine the icon's width and height.\r
+    my $w = `identify -format %w $filename`;\r
+    my $h = `identify -format %h $filename`;\r
+\r
+    # Read the file in as RGBA data. We flip vertically at this\r
+    # point, to avoid having to do it ourselves (.BMP and hence\r
+    # .ICO are bottom-up).\r
+    my $data = [];\r
+    open IDATA, "convert -flip -depth 8 $filename rgba:- |";\r
+    push @$data, $rgb while (read IDATA,$rgb,4,0) == 4;\r
+    close IDATA;\r
+    # Check we have the right amount of data.\r
+    $xl = $w * $h;\r
+    $al = scalar @$data;\r
+    die "wrong amount of image data ($al, expected $xl) from $filename\n"\r
+      unless $al == $xl;\r
+\r
+    # Build the alpha channel now, so we can exclude transparent\r
+    # pixels from the palette analysis. We replace transparent\r
+    # pixels with undef in the data array.\r
+    #\r
+    # We quantise the alpha channel half way up, so that alpha of\r
+    # 0x80 or more is taken to be fully opaque and 0x7F or less is\r
+    # fully transparent. Nasty, but the best we can do without\r
+    # dithering (and don't even suggest we do that!).\r
+    my $x;\r
+    my $y;\r
+    my $alpha = "";\r
+\r
+    for ($y = 0; $y < $h; $y++) {\r
+       my $currbyte = 0, $currbits = 0;\r
+       for ($x = 0; $x < (($w+31)|31)-31; $x++) {\r
+           $pix = ($x < $w ? $data->[$y*$w+$x] : "\x00\x00\x00\xFF");\r
+           my @rgba = unpack "CCCC", $pix;\r
+           $currbyte <<= 1;\r
+           $currbits++;\r
+           if ($rgba[3] < 0x80) {\r
+               if ($x < $w) {\r
+                   $data->[$y*$w+$x] = undef;\r
+               }\r
+               $currbyte |= 1; # MS has the alpha channel inverted :-)\r
+           } else {\r
+               # Might as well flip RGBA into BGR0 while we're here.\r
+               if ($x < $w) {\r
+                   $data->[$y*$w+$x] = pack "CCCC",\r
+                     $rgba[2], $rgba[1], $rgba[0], 0;\r
+               }\r
+           }\r
+           if ($currbits >= 8) {\r
+               $alpha .= pack "C", $currbyte;\r
+               $currbits -= 8;\r
+           }\r
+       }\r
+    }\r
+\r
+    # For an 8-bit image, check we have at most 256 distinct\r
+    # colours, and build the palette.\r
+    %pal = ();\r
+    if ($depth == 8) {\r
+       my $palindex = 0;\r
+       foreach $pix (@$data) {\r
+           next unless defined $pix;\r
+           $pal{$pix} = $palindex++ unless defined $pal{$pix};\r
+       }\r
+       die "too many colours in 8-bit image $filename\n" unless $palindex <= 256;\r
+    } elsif ($depth == 4) {\r
+       %pal = %win16pal;\r
+    } elsif ($depth == 1) {\r
+       %pal = %win2pal;\r
+    }\r
+\r
+    my $raster = "";\r
+    if ($depth < 24) {\r
+       # For a non-24-bit image, flatten the image into one palette\r
+       # index per pixel.\r
+       $pad = 32 / $depth; # number of pixels to pad scanline to 4-byte align\r
+       $pmask = $pad-1;\r
+       for ($y = 0; $y < $h; $y++) {\r
+           my $currbyte = 0, $currbits = 0;\r
+           for ($x = 0; $x < (($w+$pmask)|$pmask)-$pmask; $x++) {\r
+               $currbyte <<= $depth;\r
+               $currbits += $depth;\r
+               if ($x < $w && defined ($pix = $data->[$y*$w+$x])) {\r
+                   if (!defined $pal{$pix}) {\r
+                        $pixhex = sprintf "%02x%02x%02x", unpack "CCC", $pix;\r
+                       die "illegal colour value $pixhex at pixel ($x,$y) in $filename\n";\r
+                   }\r
+                   $currbyte |= $pal{$pix};\r
+               }\r
+               if ($currbits >= 8) {\r
+                   $raster .= pack "C", $currbyte;\r
+                   $currbits -= 8;\r
+               }\r
+           }\r
+       }\r
+    } else {\r
+       # For a 24-bit image, reverse the order of the R,G,B values\r
+       # and stick a padding zero on the end.\r
+       #\r
+       # (In this loop we don't need to bother padding the\r
+       # scanline out to a multiple of four bytes, because every\r
+       # pixel takes four whole bytes anyway.)\r
+       for ($i = 0; $i < scalar @$data; $i++) {\r
+           if (defined $data->[$i]) {\r
+               $raster .= $data->[$i];\r
+           } else {\r
+               $raster .= "\x00\x00\x00\x00";\r
+           }\r
+       }\r
+       $depth = 32; # and adjust this\r
+    }\r
+\r
+    # Prepare the icon data. First the header...\r
+    my $data = pack "VVVvvVVVVVV",\r
+      40, # size of bitmap info header\r
+      $w, # icon width\r
+      $h*2, # icon height (x2 to indicate the subsequent alpha channel)\r
+      1, # 1 plane (common to all MS image formats)\r
+      $depth, # bits per pixel\r
+      0, # no compression\r
+      length $raster, # image size\r
+      0, 0, 0, 0; # resolution, colours used, colours important (ignored)\r
+    # ... then the palette ...\r
+    if ($depth <= 8) {\r
+       my $ncols = (1 << $depth);\r
+       my $palette = "\x00\x00\x00\x00" x $ncols;\r
+       foreach $i (keys %pal) {\r
+           substr($palette, $pal{$i}*4, 4) = $i;\r
+       }\r
+       $data .= $palette;\r
+    }\r
+    # ... the raster data we already had ready ...\r
+    $data .= $raster;\r
+    # ... and the alpha channel we already had as well.\r
+    $data .= $alpha;\r
+\r
+    # Prepare the header which will represent this image in the\r
+    # icon file.\r
+    my $header = pack "CCCCvvV",\r
+      $w, $h, # width and height (this time the real height)\r
+      1 << $depth, # number of colours, if less than 256\r
+      0, # reserved\r
+      1, # planes\r
+      $depth, # bits per pixel\r
+      length $data; # size of real icon data\r
+\r
+    push @hdr, $header;\r
+    push @dat, $data;\r
+}\r
diff --git a/putty/ICONS/MAKEFILE b/putty/ICONS/MAKEFILE
new file mode 100644 (file)
index 0000000..620fab4
--- /dev/null
@@ -0,0 +1,92 @@
+# Makefile for the PuTTY icon suite.\r
+\r
+ICONS = putty puttycfg puttygen pscp pageant pterm ptermcfg puttyins\r
+SIZES = 16 32 48\r
+\r
+MODE = # override to -it on command line for opaque testing\r
+\r
+PNGS = $(foreach I,$(ICONS),$(foreach S,$(SIZES),$(I)-$(S).png))\r
+MONOPNGS = $(foreach I,$(ICONS),$(foreach S,$(SIZES),$(I)-$(S)-mono.png))\r
+TRUEPNGS = $(foreach I,$(ICONS),$(foreach S,$(SIZES),$(I)-$(S)-true.png))\r
+\r
+ICOS = putty.ico puttygen.ico pscp.ico pageant.ico pageants.ico puttycfg.ico \\r
+       puttyins.ico\r
+CICONS = xpmputty.c xpmpucfg.c xpmpterm.c xpmptcfg.c\r
+\r
+base: icos cicons\r
+\r
+all: pngs monopngs base # truepngs currently disabled by default\r
+\r
+pngs: $(PNGS)\r
+monopngs: $(MONOPNGS)\r
+truepngs: $(TRUEPNGS)\r
+\r
+icos: $(ICOS)\r
+cicons: $(CICONS)\r
+\r
+install: icos cicons\r
+       cp $(ICOS) ../windows\r
+       cp $(CICONS) ../unix\r
+\r
+$(PNGS): %.png: mkicon.py\r
+       ./mkicon.py $(MODE) $(join $(subst -, ,$(basename $@)),_icon) $@\r
+\r
+$(MONOPNGS): %.png: mkicon.py\r
+       ./mkicon.py -2 $(MODE) $(join $(subst -, ,$(subst -mono,,$(basename $@))),_icon) $@\r
+\r
+$(TRUEPNGS): %.png: mkicon.py\r
+       ./mkicon.py -T $(MODE) $(join $(subst -, ,$(subst -true,,$(basename $@))),_icon) $@\r
+\r
+putty.ico: putty-16.png putty-32.png putty-48.png \\r
+           putty-16-mono.png putty-32-mono.png putty-48-mono.png\r
+       ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@\r
+\r
+puttycfg.ico: puttycfg-16.png puttycfg-32.png puttycfg-48.png \\r
+              puttycfg-16-mono.png puttycfg-32-mono.png puttycfg-48-mono.png\r
+       ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@\r
+\r
+puttygen.ico: puttygen-16.png puttygen-32.png puttygen-48.png \\r
+              puttygen-16-mono.png puttygen-32-mono.png puttygen-48-mono.png\r
+       ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@\r
+\r
+pageant.ico: pageant-16.png pageant-32.png pageant-48.png \\r
+             pageant-16-mono.png pageant-32-mono.png pageant-48-mono.png\r
+       ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@\r
+\r
+pageants.ico: pageant-16.png pageant-16-mono.png\r
+       ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@\r
+\r
+pscp.ico: pscp-16.png pscp-32.png pscp-48.png \\r
+          pscp-16-mono.png pscp-32-mono.png pscp-48-mono.png\r
+       ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@\r
+\r
+# Because the installer icon makes heavy use of brown when drawing\r
+# the cardboard box, it's worth having 8-bit versions of it in\r
+# addition to the 4- and 1-bit ones.\r
+puttyins.ico: puttyins-16.png puttyins-32.png puttyins-48.png \\r
+              puttyins-16-mono.png puttyins-32-mono.png \\r
+              puttyins-48-mono.png \\r
+              puttyins-16-true.png puttyins-32-true.png \\r
+              puttyins-48-true.png\r
+       ./icon.pl -8 $(filter %-true.png, $^) \\r
+                  -4 $(filter-out %-true.png, $(filter-out %-mono.png, $^)) \\r
+                  -1 $(filter %-mono.png, $^) > $@\r
+\r
+# Icon for the website. (This isn't linked into "make all".)\r
+website.ico: putty-16.png\r
+       ./icon.pl -4 $^ >$@\r
+\r
+xpmputty.c: putty-16.png putty-32.png putty-48.png\r
+       ./cicon.pl main_icon $^ > $@\r
+\r
+xpmpucfg.c: puttycfg-16.png puttycfg-32.png puttycfg-48.png\r
+       ./cicon.pl cfg_icon $^ > $@\r
+\r
+xpmpterm.c: pterm-16.png pterm-32.png pterm-48.png\r
+       ./cicon.pl main_icon $^ > $@\r
+\r
+xpmptcfg.c: ptermcfg-16.png ptermcfg-32.png ptermcfg-48.png\r
+       ./cicon.pl cfg_icon $^ > $@\r
+\r
+clean:\r
+       rm -f *.png *.ico *.c\r
diff --git a/putty/ICONS/MKICON.PY b/putty/ICONS/MKICON.PY
new file mode 100644 (file)
index 0000000..122eaaa
--- /dev/null
@@ -0,0 +1,1093 @@
+#!/usr/bin/env python\r
+\r
+import math\r
+\r
+# Python code which draws the PuTTY icon components at a range of\r
+# sizes.\r
+\r
+# TODO\r
+# ----\r
+#\r
+#  - use of alpha blending\r
+#     + try for variable-transparency borders\r
+#\r
+#  - can we integrate the Mac icons into all this? Do we want to?\r
+\r
+def pixel(x, y, colour, canvas):\r
+    canvas[(int(x),int(y))] = colour\r
+\r
+def overlay(src, x, y, dst):\r
+    x = int(x)\r
+    y = int(y)\r
+    for (sx, sy), colour in src.items():\r
+        dst[sx+x, sy+y] = blend(colour, dst.get((sx+x, sy+y), cT))\r
+\r
+def finalise(canvas):\r
+    for k in canvas.keys():\r
+        canvas[k] = finalisepix(canvas[k])\r
+\r
+def bbox(canvas):\r
+    minx, miny, maxx, maxy = None, None, None, None\r
+    for (x, y) in canvas.keys():\r
+        if minx == None:\r
+            minx, miny, maxx, maxy = x, y, x+1, y+1\r
+        else:\r
+            minx = min(minx, x)\r
+            miny = min(miny, y)\r
+            maxx = max(maxx, x+1)\r
+            maxy = max(maxy, y+1)\r
+    return (minx, miny, maxx, maxy)\r
+\r
+def topy(canvas):\r
+    miny = {}\r
+    for (x, y) in canvas.keys():\r
+        miny[x] = min(miny.get(x, y), y)\r
+    return miny\r
+\r
+def render(canvas, minx, miny, maxx, maxy):\r
+    w = maxx - minx\r
+    h = maxy - miny\r
+    ret = []\r
+    for y in range(h):\r
+        ret.append([outpix(cT)] * w)\r
+    for (x, y), colour in canvas.items():\r
+        if x >= minx and x < maxx and y >= miny and y < maxy:\r
+            ret[y-miny][x-minx] = outpix(colour)\r
+    return ret\r
+\r
+# Code to actually draw pieces of icon. These don't generally worry\r
+# about positioning within a canvas; they just draw at a standard\r
+# location, return some useful coordinates, and leave composition\r
+# to other pieces of code.\r
+\r
+sqrthash = {}\r
+def memoisedsqrt(x):\r
+    if not sqrthash.has_key(x):\r
+        sqrthash[x] = math.sqrt(x)\r
+    return sqrthash[x]\r
+\r
+BR, TR, BL, TL = range(4) # enumeration of quadrants for border()\r
+\r
+def border(canvas, thickness, squarecorners, out={}):\r
+    # I haven't yet worked out exactly how to do borders in a\r
+    # properly alpha-blended fashion.\r
+    #\r
+    # When you have two shades of dark available (half-dark H and\r
+    # full-dark F), the right sequence of circular border sections\r
+    # around a pixel x starts off with these two layouts:\r
+    #\r
+    #   H    F\r
+    #  HxH  FxF\r
+    #   H    F\r
+    #\r
+    # Where it goes after that I'm not entirely sure, but I'm\r
+    # absolutely sure those are the right places to start. However,\r
+    # every automated algorithm I've tried has always started off\r
+    # with the two layouts\r
+    #\r
+    #   H   HHH\r
+    #  HxH  HxH\r
+    #   H   HHH\r
+    #\r
+    # which looks much worse. This is true whether you do\r
+    # pixel-centre sampling (define an inner circle and an outer\r
+    # circle with radii differing by 1, set any pixel whose centre\r
+    # is inside the inner circle to F, any pixel whose centre is\r
+    # outside the outer one to nothing, interpolate between the two\r
+    # and round sensibly), _or_ whether you plot a notional circle\r
+    # of a given radius and measure the actual _proportion_ of each\r
+    # pixel square taken up by it.\r
+    #\r
+    # It's not clear what I should be doing to prevent this. One\r
+    # option is to attempt error-diffusion: Ian Jackson proved on\r
+    # paper that if you round each pixel's ideal value to the\r
+    # nearest of the available output values, then measure the\r
+    # error at each pixel, propagate that error outwards into the\r
+    # original values of the surrounding pixels, and re-round\r
+    # everything, you do get the correct second stage. However, I\r
+    # haven't tried it at a proper range of radii.\r
+    #\r
+    # Another option is that the automated mechanisms described\r
+    # above would be entirely adequate if it weren't for the fact\r
+    # that the human visual centres are adapted to detect\r
+    # horizontal and vertical lines in particular, so the only\r
+    # place you have to behave a bit differently is at the ends of\r
+    # the top and bottom row of pixels in the circle, and the top\r
+    # and bottom of the extreme columns.\r
+    #\r
+    # For the moment, what I have below is a very simple mechanism\r
+    # which always uses only one alpha level for any given border\r
+    # thickness, and which seems to work well enough for Windows\r
+    # 16-colour icons. Everything else will have to wait.\r
+\r
+    thickness = memoisedsqrt(thickness)\r
+\r
+    if thickness < 0.9:\r
+        darkness = 0.5\r
+    else:\r
+        darkness = 1\r
+    if thickness < 1: thickness = 1\r
+    thickness = round(thickness - 0.5) + 0.3\r
+\r
+    out["borderthickness"] = thickness\r
+\r
+    dmax = int(round(thickness))\r
+    if dmax < thickness: dmax = dmax + 1\r
+\r
+    cquadrant = [[0] * (dmax+1) for x in range(dmax+1)]\r
+    squadrant = [[0] * (dmax+1) for x in range(dmax+1)]\r
+\r
+    for x in range(dmax+1):\r
+        for y in range(dmax+1):\r
+            if max(x, y) < thickness:\r
+                squadrant[x][y] = darkness\r
+            if memoisedsqrt(x*x+y*y) < thickness:\r
+                cquadrant[x][y] = darkness\r
+\r
+    bvalues = {}\r
+    for (x, y), colour in canvas.items():\r
+        for dx in range(-dmax, dmax+1):\r
+            for dy in range(-dmax, dmax+1):\r
+                quadrant = 2 * (dx < 0) + (dy < 0)\r
+                if (x, y, quadrant) in squarecorners:\r
+                    bval = squadrant[abs(dx)][abs(dy)]\r
+                else:\r
+                    bval = cquadrant[abs(dx)][abs(dy)]\r
+                if bvalues.get((x+dx,y+dy),0) < bval:\r
+                    bvalues[(x+dx,y+dy)] = bval\r
+\r
+    for (x, y), value in bvalues.items():\r
+        if not canvas.has_key((x,y)):\r
+            canvas[(x,y)] = dark(value)\r
+\r
+def sysbox(size, out={}):\r
+    canvas = {}\r
+\r
+    # The system box of the computer.\r
+\r
+    height = int(round(3.6*size))\r
+    width = int(round(16.51*size))\r
+    depth = int(round(2*size))\r
+    highlight = int(round(1*size))\r
+    bothighlight = int(round(1*size))\r
+\r
+    out["sysboxheight"] = height\r
+\r
+    floppystart = int(round(19*size)) # measured in half-pixels\r
+    floppyend = int(round(29*size)) # measured in half-pixels\r
+    floppybottom = height - bothighlight\r
+    floppyrheight = 0.7 * size\r
+    floppyheight = int(round(floppyrheight))\r
+    if floppyheight < 1:\r
+        floppyheight = 1\r
+    floppytop = floppybottom - floppyheight\r
+\r
+    # The front panel is rectangular.\r
+    for x in range(width):\r
+        for y in range(height):\r
+            grey = 3\r
+            if x < highlight or y < highlight:\r
+                grey = grey + 1\r
+            if x >= width-highlight or y >= height-bothighlight:\r
+                grey = grey - 1\r
+            if y < highlight and x >= width-highlight:\r
+                v = (highlight-1-y) - (x-(width-highlight))\r
+                if v < 0:\r
+                    grey = grey - 1\r
+                elif v > 0:\r
+                    grey = grey + 1\r
+            if y >= floppytop and y < floppybottom and \\r
+            2*x+2 > floppystart and 2*x < floppyend:\r
+                if 2*x >= floppystart and 2*x+2 <= floppyend and \\r
+                floppyrheight >= 0.7:\r
+                    grey = 0\r
+                else:\r
+                    grey = 2\r
+            pixel(x, y, greypix(grey/4.0), canvas)\r
+\r
+    # The side panel is a parallelogram.\r
+    for x in range(depth):\r
+        for y in range(height):\r
+            pixel(x+width, y-(x+1), greypix(0.5), canvas)\r
+\r
+    # The top panel is another parallelogram.\r
+    for x in range(width-1):\r
+        for y in range(depth):\r
+            grey = 3\r
+            if x >= width-1 - highlight:\r
+                grey = grey + 1         \r
+            pixel(x+(y+1), -(y+1), greypix(grey/4.0), canvas)\r
+\r
+    # And draw a border.\r
+    border(canvas, size, [], out)\r
+\r
+    return canvas\r
+\r
+def monitor(size):\r
+    canvas = {}\r
+\r
+    # The computer's monitor.\r
+\r
+    height = int(round(9.55*size))\r
+    width = int(round(11.49*size))\r
+    surround = int(round(1*size))\r
+    botsurround = int(round(2*size))\r
+    sheight = height - surround - botsurround\r
+    swidth = width - 2*surround\r
+    depth = int(round(2*size))\r
+    highlight = int(round(math.sqrt(size)))\r
+    shadow = int(round(0.55*size))\r
+\r
+    # The front panel is rectangular.\r
+    for x in range(width):\r
+        for y in range(height):\r
+            if x >= surround and y >= surround and \\r
+            x < surround+swidth and y < surround+sheight:\r
+                # Screen.\r
+                sx = (float(x-surround) - swidth/3) / swidth\r
+                sy = (float(y-surround) - sheight/3) / sheight\r
+                shighlight = 1.0 - (sx*sx+sy*sy)*0.27\r
+                pix = bluepix(shighlight)\r
+                if x < surround+shadow or y < surround+shadow:\r
+                    pix = blend(cD, pix) # sharp-edged shadow on top and left\r
+            else:\r
+                # Complicated double bevel on the screen surround.\r
+\r
+                # First, the outer bevel. We compute the distance\r
+                # from this pixel to each edge of the front\r
+                # rectangle.\r
+                list = [\r
+                (x, +1),\r
+                (y, +1),\r
+                (width-1-x, -1),\r
+                (height-1-y, -1)\r
+                ]\r
+                # Now sort the list to find the distance to the\r
+                # _nearest_ edge, or the two joint nearest.\r
+                list.sort()\r
+                # If there's one nearest edge, that determines our\r
+                # bevel colour. If there are two joint nearest, our\r
+                # bevel colour is their shared one if they agree,\r
+                # and neutral otherwise.\r
+                outerbevel = 0\r
+                if list[0][0] < list[1][0] or list[0][1] == list[1][1]:\r
+                    if list[0][0] < highlight:\r
+                        outerbevel = list[0][1]\r
+\r
+                # Now, the inner bevel. We compute the distance\r
+                # from this pixel to each edge of the screen\r
+                # itself.\r
+                list = [\r
+                (surround-1-x, -1),\r
+                (surround-1-y, -1),\r
+                (x-(surround+swidth), +1),\r
+                (y-(surround+sheight), +1)\r
+                ]\r
+                # Now we sort to find the _maximum_ distance, which\r
+                # conveniently ignores any less than zero.\r
+                list.sort()\r
+                # And now the strategy is pretty much the same as\r
+                # above, only we're working from the opposite end\r
+                # of the list.\r
+                innerbevel = 0\r
+                if list[-1][0] > list[-2][0] or list[-1][1] == list[-2][1]:\r
+                    if list[-1][0] >= 0 and list[-1][0] < highlight:\r
+                        innerbevel = list[-1][1]\r
+\r
+                # Now we know the adjustment we want to make to the\r
+                # pixel's overall grey shade due to the outer\r
+                # bevel, and due to the inner one. We break a tie\r
+                # in favour of a light outer bevel, but otherwise\r
+                # add.\r
+                grey = 3\r
+                if outerbevel > 0 or outerbevel == innerbevel:\r
+                    innerbevel = 0\r
+                grey = grey + outerbevel + innerbevel\r
+\r
+                pix = greypix(grey / 4.0)\r
+\r
+            pixel(x, y, pix, canvas)\r
+\r
+    # The side panel is a parallelogram.\r
+    for x in range(depth):\r
+        for y in range(height):\r
+            pixel(x+width, y-x, greypix(0.5), canvas)\r
+\r
+    # The top panel is another parallelogram.\r
+    for x in range(width):\r
+        for y in range(depth-1):\r
+            pixel(x+(y+1), -(y+1), greypix(0.75), canvas)\r
+\r
+    # And draw a border.\r
+    border(canvas, size, [(0,int(height-1),BL)])\r
+\r
+    return canvas\r
+\r
+def computer(size):\r
+    # Monitor plus sysbox.\r
+    out = {}\r
+    m = monitor(size)\r
+    s = sysbox(size, out)\r
+    x = int(round((2+size/(size+1))*size))\r
+    y = int(out["sysboxheight"] + out["borderthickness"])\r
+    mb = bbox(m)\r
+    sb = bbox(s)\r
+    xoff = sb[0] - mb[0] + x\r
+    yoff = sb[3] - mb[3] - y\r
+    overlay(m, xoff, yoff, s)\r
+    return s\r
+\r
+def lightning(size):\r
+    canvas = {}\r
+\r
+    # The lightning bolt motif.\r
+\r
+    # We always want this to be an even number of pixels in height,\r
+    # and an odd number in width.\r
+    width = round(7*size) * 2 - 1\r
+    height = round(8*size) * 2\r
+\r
+    # The outer edge of each side of the bolt goes to this point.\r
+    outery = round(8.4*size)\r
+    outerx = round(11*size)\r
+\r
+    # And the inner edge goes to this point.\r
+    innery = height - 1 - outery\r
+    innerx = round(7*size)\r
+\r
+    for y in range(int(height)):\r
+        list = []\r
+        if y <= outery:\r
+            list.append(width-1-int(outerx * float(y) / outery + 0.3))\r
+        if y <= innery:\r
+            list.append(width-1-int(innerx * float(y) / innery + 0.3))\r
+        y0 = height-1-y\r
+        if y0 <= outery:\r
+            list.append(int(outerx * float(y0) / outery + 0.3))\r
+        if y0 <= innery:\r
+            list.append(int(innerx * float(y0) / innery + 0.3))\r
+        list.sort()\r
+        for x in range(int(list[0]), int(list[-1]+1)):\r
+            pixel(x, y, cY, canvas)\r
+\r
+    # And draw a border.\r
+    border(canvas, size, [(int(width-1),0,TR), (0,int(height-1),BL)])\r
+\r
+    return canvas\r
+\r
+def document(size):\r
+    canvas = {}\r
+\r
+    # The document used in the PSCP/PSFTP icon.\r
+\r
+    width = round(13*size)\r
+    height = round(16*size)\r
+\r
+    lineht = round(1*size)\r
+    if lineht < 1: lineht = 1\r
+    linespc = round(0.7*size)\r
+    if linespc < 1: linespc = 1\r
+    nlines = int((height-linespc)/(lineht+linespc))\r
+    height = nlines*(lineht+linespc)+linespc # round this so it fits better\r
+\r
+    # Start by drawing a big white rectangle.\r
+    for y in range(int(height)):\r
+        for x in range(int(width)):\r
+            pixel(x, y, cW, canvas)\r
+\r
+    # Now draw lines of text.\r
+    for line in range(nlines):\r
+        # Decide where this line of text begins.\r
+        if line == 0:\r
+            start = round(4*size)\r
+        elif line < 5*nlines/7:\r
+            start = round((line - (nlines/7)) * size)\r
+        else:\r
+            start = round(1*size)\r
+        if start < round(1*size):\r
+            start = round(1*size)\r
+        # Decide where it ends.\r
+        endpoints = [10, 8, 11, 6, 5, 7, 5]\r
+        ey = line * 6.0 / (nlines-1)\r
+        eyf = math.floor(ey)\r
+        eyc = math.ceil(ey)\r
+        exf = endpoints[int(eyf)]\r
+        exc = endpoints[int(eyc)]\r
+        if eyf == eyc:\r
+            end = exf\r
+        else:\r
+            end = exf * (eyc-ey) + exc * (ey-eyf)\r
+        end = round(end * size)\r
+\r
+        liney = height - (lineht+linespc) * (line+1)\r
+        for x in range(int(start), int(end)):\r
+            for y in range(int(lineht)):\r
+                pixel(x, y+liney, cK, canvas)\r
+\r
+    # And draw a border.\r
+    border(canvas, size, \\r
+    [(0,0,TL),(int(width-1),0,TR),(0,int(height-1),BL), \\r
+    (int(width-1),int(height-1),BR)])\r
+\r
+    return canvas\r
+\r
+def hat(size):\r
+    canvas = {}\r
+\r
+    # The secret-agent hat in the Pageant icon.\r
+\r
+    topa = [6]*9+[5,3,1,0,0,1,2,2,1,1,1,9,9,10,10,11,11,12,12]\r
+    topa = [round(x*size) for x in topa]\r
+    botl = round(topa[0]+2.4*math.sqrt(size))\r
+    botr = round(topa[-1]+2.4*math.sqrt(size))\r
+    width = round(len(topa)*size)\r
+\r
+    # Line equations for the top and bottom of the hat brim, in the\r
+    # form y=mx+c. c, of course, needs scaling by size, but m is\r
+    # independent of size.\r
+    brimm = 1.0 / 3.75\r
+    brimtopc = round(4*size/3)\r
+    brimbotc = round(10*size/3)\r
+\r
+    for x in range(int(width)):\r
+        xs = float(x) * (len(topa)-1) / (width-1)\r
+        xf = math.floor(xs)\r
+        xc = math.ceil(xs)\r
+        topf = topa[int(xf)]\r
+        topc = topa[int(xc)]\r
+        if xf == xc:\r
+            top = topf\r
+        else:\r
+            top = topf * (xc-xs) + topc * (xs-xf)\r
+        top = math.floor(top)\r
+        bot = round(botl + (botr-botl) * x/(width-1))\r
+\r
+        for y in range(int(top), int(bot)):\r
+            pixel(x, y, cK, canvas)\r
+\r
+    # Now draw the brim.\r
+    for x in range(int(width)):\r
+        brimtop = brimtopc + brimm * x\r
+        brimbot = brimbotc + brimm * x\r
+        for y in range(int(math.floor(brimtop)), int(math.ceil(brimbot))):\r
+            tophere = max(min(brimtop - y, 1), 0)\r
+            bothere = max(min(brimbot - y, 1), 0)\r
+            grey = bothere - tophere\r
+            # Only draw brim pixels over pixels which are (a) part\r
+            # of the main hat, and (b) not right on its edge.\r
+            if canvas.has_key((x,y)) and \\r
+            canvas.has_key((x,y-1)) and \\r
+            canvas.has_key((x,y+1)) and \\r
+            canvas.has_key((x-1,y)) and \\r
+            canvas.has_key((x+1,y)):\r
+                pixel(x, y, greypix(grey), canvas)\r
+\r
+    return canvas\r
+\r
+def key(size):\r
+    canvas = {}\r
+\r
+    # The key in the PuTTYgen icon.\r
+\r
+    keyheadw = round(9.5*size)\r
+    keyheadh = round(12*size)\r
+    keyholed = round(4*size)\r
+    keyholeoff = round(2*size)\r
+    # Ensure keyheadh and keyshafth have the same parity.\r
+    keyshafth = round((2*size - (int(keyheadh)&1)) / 2) * 2 + (int(keyheadh)&1)\r
+    keyshaftw = round(18.5*size)\r
+    keyhead = [round(x*size) for x in [12,11,8,10,9,8,11,12]]\r
+\r
+    squarepix = []\r
+\r
+    # Ellipse for the key head, minus an off-centre circular hole.\r
+    for y in range(int(keyheadh)):\r
+        dy = (y-(keyheadh-1)/2.0) / (keyheadh/2.0)\r
+        dyh = (y-(keyheadh-1)/2.0) / (keyholed/2.0)\r
+        for x in range(int(keyheadw)):\r
+            dx = (x-(keyheadw-1)/2.0) / (keyheadw/2.0)\r
+            dxh = (x-(keyheadw-1)/2.0-keyholeoff) / (keyholed/2.0)\r
+            if dy*dy+dx*dx <= 1 and dyh*dyh+dxh*dxh > 1:\r
+                pixel(x + keyshaftw, y, cy, canvas)\r
+\r
+    # Rectangle for the key shaft, extended at the bottom for the\r
+    # key head detail.\r
+    for x in range(int(keyshaftw)):\r
+        top = round((keyheadh - keyshafth) / 2)\r
+        bot = round((keyheadh + keyshafth) / 2)\r
+        xs = float(x) * (len(keyhead)-1) / round((len(keyhead)-1)*size)\r
+        xf = math.floor(xs)\r
+        xc = math.ceil(xs)\r
+        in_head = 0\r
+        if xc < len(keyhead):\r
+            in_head = 1\r
+            yf = keyhead[int(xf)]\r
+            yc = keyhead[int(xc)]\r
+            if xf == xc:\r
+                bot = yf\r
+            else:\r
+                bot = yf * (xc-xs) + yc * (xs-xf)\r
+        for y in range(int(top),int(bot)):\r
+            pixel(x, y, cy, canvas)\r
+            if in_head:\r
+                last = (x, y)\r
+        if x == 0:\r
+            squarepix.append((x, int(top), TL))\r
+        if x == 0:\r
+            squarepix.append(last + (BL,))\r
+        if last != None and not in_head:\r
+            squarepix.append(last + (BR,))\r
+            last = None\r
+\r
+    # And draw a border.\r
+    border(canvas, size, squarepix)\r
+\r
+    return canvas\r
+\r
+def linedist(x1,y1, x2,y2, x,y):\r
+    # Compute the distance from the point x,y to the line segment\r
+    # joining x1,y1 to x2,y2. Returns the distance vector, measured\r
+    # with x,y at the origin.\r
+\r
+    vectors = []\r
+\r
+    # Special case: if x1,y1 and x2,y2 are the same point, we\r
+    # don't attempt to extrapolate it into a line at all.\r
+    if x1 != x2 or y1 != y2:\r
+        # First, find the nearest point to x,y on the infinite\r
+        # projection of the line segment. So we construct a vector\r
+        # n perpendicular to that segment...\r
+        nx = y2-y1\r
+        ny = x1-x2\r
+        # ... compute the dot product of (x1,y1)-(x,y) with that\r
+        # vector...\r
+        nd = (x1-x)*nx + (y1-y)*ny\r
+        # ... multiply by the vector we first thought of...\r
+        ndx = nd * nx\r
+        ndy = nd * ny\r
+        # ... and divide twice by the length of n.\r
+        ndx = ndx / (nx*nx+ny*ny)\r
+        ndy = ndy / (nx*nx+ny*ny)\r
+        # That gives us a displacement vector from x,y to the\r
+        # nearest point. See if it's within the range of the line\r
+        # segment.\r
+        cx = x + ndx\r
+        cy = y + ndy\r
+        if cx >= min(x1,x2) and cx <= max(x1,x2) and \\r
+        cy >= min(y1,y2) and cy <= max(y1,y2):\r
+            vectors.append((ndx,ndy))\r
+\r
+    # Now we have up to three candidate result vectors: (ndx,ndy)\r
+    # as computed just above, and the two vectors to the ends of\r
+    # the line segment, (x1-x,y1-y) and (x2-x,y2-y). Pick the\r
+    # shortest.\r
+    vectors = vectors + [(x1-x,y1-y), (x2-x,y2-y)]\r
+    bestlen, best = None, None\r
+    for v in vectors:\r
+        vlen = v[0]*v[0]+v[1]*v[1]\r
+        if bestlen == None or bestlen > vlen:\r
+            bestlen = vlen\r
+            best = v\r
+    return best\r
+\r
+def spanner(size):\r
+    canvas = {}\r
+\r
+    # The spanner in the config box icon.\r
+\r
+    headcentre = 0.5 + round(4*size)\r
+    headradius = headcentre + 0.1\r
+    headhighlight = round(1.5*size)\r
+    holecentre = 0.5 + round(3*size)\r
+    holeradius = round(2*size)\r
+    holehighlight = round(1.5*size)\r
+    shaftend = 0.5 + round(25*size)\r
+    shaftwidth = round(2*size)\r
+    shafthighlight = round(1.5*size)\r
+    cmax = shaftend + shaftwidth\r
+\r
+    # Define three line segments, such that the shortest distance\r
+    # vectors from any point to each of these segments determines\r
+    # everything we need to know about where it is on the spanner\r
+    # shape.\r
+    segments = [\r
+    ((0,0), (holecentre, holecentre)),\r
+    ((headcentre, headcentre), (headcentre, headcentre)),\r
+    ((headcentre+headradius/math.sqrt(2), headcentre+headradius/math.sqrt(2)),\r
+    (cmax, cmax))\r
+    ]\r
+\r
+    for y in range(int(cmax)):\r
+        for x in range(int(cmax)):\r
+            vectors = [linedist(a,b,c,d,x,y) for ((a,b),(c,d)) in segments]\r
+            dists = [memoisedsqrt(vx*vx+vy*vy) for (vx,vy) in vectors]\r
+\r
+            # If the distance to the hole line is less than\r
+            # holeradius, we're not part of the spanner.\r
+            if dists[0] < holeradius:\r
+                continue\r
+            # If the distance to the head `line' is less than\r
+            # headradius, we are part of the spanner; likewise if\r
+            # the distance to the shaft line is less than\r
+            # shaftwidth _and_ the resulting shaft point isn't\r
+            # beyond the shaft end.\r
+            if dists[1] > headradius and \\r
+            (dists[2] > shaftwidth or x+vectors[2][0] >= shaftend):\r
+                continue\r
+\r
+            # We're part of the spanner. Now compute the highlight\r
+            # on this pixel. We do this by computing a `slope\r
+            # vector', which points from this pixel in the\r
+            # direction of its nearest edge. We store an array of\r
+            # slope vectors, in polar coordinates.\r
+            angles = [math.atan2(vy,vx) for (vx,vy) in vectors]\r
+            slopes = []\r
+            if dists[0] < holeradius + holehighlight:\r
+                slopes.append(((dists[0]-holeradius)/holehighlight,angles[0]))\r
+            if dists[1]/headradius < dists[2]/shaftwidth:\r
+                if dists[1] > headradius - headhighlight and dists[1] < headradius:\r
+                    slopes.append(((headradius-dists[1])/headhighlight,math.pi+angles[1]))\r
+            else:\r
+                if dists[2] > shaftwidth - shafthighlight and dists[2] < shaftwidth:\r
+                    slopes.append(((shaftwidth-dists[2])/shafthighlight,math.pi+angles[2]))\r
+            # Now we find the smallest distance in that array, if\r
+            # any, and that gives us a notional position on a\r
+            # sphere which we can use to compute the final\r
+            # highlight level.\r
+            bestdist = None\r
+            bestangle = 0\r
+            for dist, angle in slopes:\r
+                if bestdist == None or bestdist > dist:\r
+                    bestdist = dist\r
+                    bestangle = angle\r
+            if bestdist == None:\r
+                bestdist = 1.0\r
+            sx = (1.0-bestdist) * math.cos(bestangle)\r
+            sy = (1.0-bestdist) * math.sin(bestangle)\r
+            sz = math.sqrt(1.0 - sx*sx - sy*sy)\r
+            shade = sx-sy+sz / math.sqrt(3) # can range from -1 to +1\r
+            shade = 1.0 - (1-shade)/3\r
+\r
+            pixel(x, y, yellowpix(shade), canvas)\r
+\r
+    # And draw a border.\r
+    border(canvas, size, [])\r
+\r
+    return canvas\r
+\r
+def box(size, back):\r
+    canvas = {}\r
+\r
+    # The back side of the cardboard box in the installer icon.\r
+\r
+    boxwidth = round(15 * size)\r
+    boxheight = round(12 * size)\r
+    boxdepth = round(4 * size)\r
+    boxfrontflapheight = round(5 * size)\r
+    boxrightflapheight = round(3 * size)\r
+\r
+    # Three shades of basically acceptable brown, all achieved by\r
+    # halftoning between two of the Windows-16 colours. I'm quite\r
+    # pleased that was feasible at all!\r
+    dark = halftone(cr, cK)\r
+    med = halftone(cr, cy)\r
+    light = halftone(cr, cY)\r
+    # We define our halftoning parity in such a way that the black\r
+    # pixels along the RHS of the visible part of the box back\r
+    # match up with the one-pixel black outline around the\r
+    # right-hand side of the box. In other words, we want the pixel\r
+    # at (-1, boxwidth-1) to be black, and hence the one at (0,\r
+    # boxwidth) too.\r
+    parityadjust = int(boxwidth) % 2\r
+\r
+    # The entire back of the box.\r
+    if back:\r
+        for x in range(int(boxwidth + boxdepth)):\r
+            ytop = max(-x-1, -boxdepth-1)\r
+            ybot = min(boxheight, boxheight+boxwidth-1-x)\r
+            for y in range(int(ytop), int(ybot)):\r
+                pixel(x, y, dark[(x+y+parityadjust) % 2], canvas)\r
+\r
+    # Even when drawing the back of the box, we still draw the\r
+    # whole shape, because that means we get the right overall size\r
+    # (the flaps make the box front larger than the box back) and\r
+    # it'll all be overwritten anyway.\r
+\r
+    # The front face of the box.\r
+    for x in range(int(boxwidth)):\r
+        for y in range(int(boxheight)):\r
+            pixel(x, y, med[(x+y+parityadjust) % 2], canvas)\r
+    # The right face of the box.\r
+    for x in range(int(boxwidth), int(boxwidth+boxdepth)):\r
+        ybot = boxheight + boxwidth-x\r
+        ytop = ybot - boxheight\r
+        for y in range(int(ytop), int(ybot)):\r
+            pixel(x, y, dark[(x+y+parityadjust) % 2], canvas)\r
+    # The front flap of the box.\r
+    for y in range(int(boxfrontflapheight)):\r
+        xadj = int(round(-0.5*y))\r
+        for x in range(int(xadj), int(xadj+boxwidth)):\r
+            pixel(x, y, light[(x+y+parityadjust) % 2], canvas)\r
+    # The right flap of the box.\r
+    for x in range(int(boxwidth), int(boxwidth + boxdepth + boxrightflapheight + 1)):\r
+        ytop = max(boxwidth - 1 - x, x - boxwidth - 2*boxdepth - 1)\r
+        ybot = min(x - boxwidth - 1, boxwidth + 2*boxrightflapheight - 1 - x)\r
+        for y in range(int(ytop), int(ybot+1)):\r
+            pixel(x, y, med[(x+y+parityadjust) % 2], canvas)\r
+\r
+    # And draw a border.\r
+    border(canvas, size, [(0, int(boxheight)-1, BL)])\r
+\r
+    return canvas\r
+\r
+def boxback(size):\r
+    return box(size, 1)\r
+def boxfront(size):\r
+    return box(size, 0)\r
+\r
+# Functions to draw entire icons by composing the above components.\r
+\r
+def xybolt(c1, c2, size, boltoffx=0, boltoffy=0, aux={}):\r
+    # Two unspecified objects and a lightning bolt.\r
+\r
+    canvas = {}\r
+    w = h = round(32 * size)\r
+\r
+    bolt = lightning(size)\r
+\r
+    # Position c2 against the top right of the icon.\r
+    bb = bbox(c2)\r
+    assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h\r
+    overlay(c2, w-bb[2], 0-bb[1], canvas)\r
+    aux["c2pos"] = (w-bb[2], 0-bb[1])\r
+    # Position c1 against the bottom left of the icon.\r
+    bb = bbox(c1)\r
+    assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h\r
+    overlay(c1, 0-bb[0], h-bb[3], canvas)\r
+    aux["c1pos"] = (0-bb[0], h-bb[3])\r
+    # Place the lightning bolt artistically off-centre. (The\r
+    # rationale for this positioning is that it's centred on the\r
+    # midpoint between the centres of the two monitors in the PuTTY\r
+    # icon proper, but it's not really feasible to _base_ the\r
+    # calculation here on that.)\r
+    bb = bbox(bolt)\r
+    assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h\r
+    overlay(bolt, (w-bb[0]-bb[2])/2 + round(boltoffx*size), \\r
+    (h-bb[1]-bb[3])/2 + round((boltoffy-2)*size), canvas)\r
+\r
+    return canvas\r
+\r
+def putty_icon(size):\r
+    return xybolt(computer(size), computer(size), size)\r
+\r
+def puttycfg_icon(size):\r
+    w = h = round(32 * size)\r
+    s = spanner(size)\r
+    canvas = putty_icon(size)\r
+    # Centre the spanner.\r
+    bb = bbox(s)\r
+    overlay(s, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas)\r
+    return canvas\r
+\r
+def puttygen_icon(size):\r
+    return xybolt(computer(size), key(size), size, boltoffx=2)\r
+\r
+def pscp_icon(size):\r
+    return xybolt(document(size), computer(size), size)\r
+\r
+def puttyins_icon(size):\r
+    aret = {}\r
+    # The box back goes behind the lightning bolt.\r
+    canvas = xybolt(boxback(size), computer(size), size, boltoffx=-2, boltoffy=+1, aux=aret)\r
+    # But the box front goes over the top, so that the lightning\r
+    # bolt appears to come _out_ of the box. Here it's useful to\r
+    # know the exact coordinates where xybolt placed the box back,\r
+    # so we can overlay the box front exactly on top of it.\r
+    c1x, c1y = aret["c1pos"]\r
+    overlay(boxfront(size), c1x, c1y, canvas)\r
+    return canvas\r
+\r
+def pterm_icon(size):\r
+    # Just a really big computer.\r
+\r
+    canvas = {}\r
+    w = h = round(32 * size)\r
+\r
+    c = computer(size * 1.4)\r
+\r
+    # Centre c in the return canvas.\r
+    bb = bbox(c)\r
+    assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h\r
+    overlay(c, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas)\r
+\r
+    return canvas\r
+\r
+def ptermcfg_icon(size):\r
+    w = h = round(32 * size)\r
+    s = spanner(size)\r
+    canvas = pterm_icon(size)\r
+    # Centre the spanner.\r
+    bb = bbox(s)\r
+    overlay(s, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas)\r
+    return canvas\r
+\r
+def pageant_icon(size):\r
+    # A biggish computer, in a hat.\r
+\r
+    canvas = {}\r
+    w = h = round(32 * size)\r
+\r
+    c = computer(size * 1.2)\r
+    ht = hat(size)\r
+\r
+    cbb = bbox(c)\r
+    hbb = bbox(ht)\r
+\r
+    # Determine the relative y-coordinates of the computer and hat.\r
+    # We just centre the one on the other.\r
+    xrel = (cbb[0]+cbb[2]-hbb[0]-hbb[2])/2\r
+\r
+    # Determine the relative y-coordinates of the computer and hat.\r
+    # We do this by sitting the hat as low down on the computer as\r
+    # possible without any computer showing over the top. To do\r
+    # this we first have to find the minimum x coordinate at each\r
+    # y-coordinate of both components.\r
+    cty = topy(c)\r
+    hty = topy(ht)\r
+    yrelmin = None\r
+    for cx in cty.keys():\r
+        hx = cx - xrel\r
+        assert hty.has_key(hx)\r
+        yrel = cty[cx] - hty[hx]\r
+        if yrelmin == None:\r
+            yrelmin = yrel\r
+        else:\r
+            yrelmin = min(yrelmin, yrel)\r
+\r
+    # Overlay the hat on the computer.\r
+    overlay(ht, xrel, yrelmin, c)\r
+\r
+    # And centre the result in the main icon canvas.\r
+    bb = bbox(c)\r
+    assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h\r
+    overlay(c, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas)\r
+\r
+    return canvas\r
+\r
+# Test and output functions.\r
+\r
+import os\r
+import sys\r
+\r
+def testrun(func, fname):\r
+    canvases = []\r
+    for size in [0.5, 0.6, 1.0, 1.2, 1.5, 4.0]:\r
+        canvases.append(func(size))\r
+    wid = 0\r
+    ht = 0\r
+    for canvas in canvases:\r
+        minx, miny, maxx, maxy = bbox(canvas)\r
+        wid = max(wid, maxx-minx+4)\r
+        ht = ht + maxy-miny+4\r
+    block = []\r
+    for canvas in canvases:\r
+        minx, miny, maxx, maxy = bbox(canvas)\r
+        block.extend(render(canvas, minx-2, miny-2, minx-2+wid, maxy+2))\r
+    p = os.popen("convert -depth 8 -size %dx%d rgb:- %s" % (wid,ht,fname), "w")\r
+    assert len(block) == ht\r
+    for line in block:\r
+        assert len(line) == wid\r
+        for r, g, b, a in line:\r
+            # Composite on to orange.\r
+            r = int(round((r * a + 255 * (255-a)) / 255.0))\r
+            g = int(round((g * a + 128 * (255-a)) / 255.0))\r
+            b = int(round((b * a +   0 * (255-a)) / 255.0))\r
+            p.write("%c%c%c" % (r,g,b))\r
+    p.close()\r
+\r
+def drawicon(func, width, fname, orangebackground = 0):\r
+    canvas = func(width / 32.0)\r
+    finalise(canvas)\r
+    minx, miny, maxx, maxy = bbox(canvas)\r
+    assert minx >= 0 and miny >= 0 and maxx <= width and maxy <= width\r
+\r
+    block = render(canvas, 0, 0, width, width)\r
+    p = os.popen("convert -depth 8 -size %dx%d rgba:- %s" % (width,width,fname), "w")\r
+    assert len(block) == width\r
+    for line in block:\r
+        assert len(line) == width\r
+        for r, g, b, a in line:\r
+            if orangebackground:\r
+                # Composite on to orange.\r
+                r = int(round((r * a + 255 * (255-a)) / 255.0))\r
+                g = int(round((g * a + 128 * (255-a)) / 255.0))\r
+                b = int(round((b * a +   0 * (255-a)) / 255.0))\r
+                a = 255\r
+            p.write("%c%c%c%c" % (r,g,b,a))\r
+    p.close()\r
+\r
+args = sys.argv[1:]\r
+\r
+orangebackground = test = 0\r
+colours = 1 # 0=mono, 1=16col, 2=truecol\r
+doingargs = 1\r
+\r
+realargs = []\r
+for arg in args:\r
+    if doingargs and arg[0] == "-":\r
+        if arg == "-t":\r
+            test = 1\r
+        elif arg == "-it":\r
+            orangebackground = 1\r
+        elif arg == "-2":\r
+            colours = 0\r
+        elif arg == "-T":\r
+            colours = 2\r
+        elif arg == "--":\r
+            doingargs = 0\r
+        else:\r
+            sys.stderr.write("unrecognised option '%s'\n" % arg)\r
+            sys.exit(1)\r
+    else:\r
+        realargs.append(arg)\r
+\r
+if colours == 0:\r
+    # Monochrome.\r
+    cK=cr=cg=cb=cm=cc=cP=cw=cR=cG=cB=cM=cC=cD = 0\r
+    cY=cy=cW = 1\r
+    cT = -1\r
+    def greypix(value):\r
+        return [cK,cW][int(round(value))]\r
+    def yellowpix(value):\r
+        return [cK,cW][int(round(value))]\r
+    def bluepix(value):\r
+        return cK\r
+    def dark(value):\r
+        return [cT,cK][int(round(value))]\r
+    def blend(col1, col2):\r
+        if col1 == cT:\r
+            return col2\r
+        else:\r
+            return col1\r
+    pixvals = [\r
+    (0x00, 0x00, 0x00, 0xFF), # cK\r
+    (0xFF, 0xFF, 0xFF, 0xFF), # cW\r
+    (0x00, 0x00, 0x00, 0x00), # cT\r
+    ]\r
+    def outpix(colour):\r
+        return pixvals[colour]\r
+    def finalisepix(colour):\r
+        return colour\r
+    def halftone(col1, col2):\r
+        return (col1, col2)\r
+elif colours == 1:\r
+    # Windows 16-colour palette.\r
+    cK,cr,cg,cy,cb,cm,cc,cP,cw,cR,cG,cY,cB,cM,cC,cW = range(16)\r
+    cT = -1\r
+    cD = -2 # special translucent half-darkening value used internally\r
+    def greypix(value):\r
+        return [cK,cw,cw,cP,cW][int(round(4*value))]\r
+    def yellowpix(value):\r
+        return [cK,cy,cY][int(round(2*value))]\r
+    def bluepix(value):\r
+        return [cK,cb,cB][int(round(2*value))]\r
+    def dark(value):\r
+        return [cT,cD,cK][int(round(2*value))]\r
+    def blend(col1, col2):\r
+        if col1 == cT:\r
+            return col2\r
+        elif col1 == cD:\r
+            return [cK,cK,cK,cK,cK,cK,cK,cw,cK,cr,cg,cy,cb,cm,cc,cw,cD,cD][col2]\r
+        else:\r
+            return col1\r
+    pixvals = [\r
+    (0x00, 0x00, 0x00, 0xFF), # cK\r
+    (0x80, 0x00, 0x00, 0xFF), # cr\r
+    (0x00, 0x80, 0x00, 0xFF), # cg\r
+    (0x80, 0x80, 0x00, 0xFF), # cy\r
+    (0x00, 0x00, 0x80, 0xFF), # cb\r
+    (0x80, 0x00, 0x80, 0xFF), # cm\r
+    (0x00, 0x80, 0x80, 0xFF), # cc\r
+    (0xC0, 0xC0, 0xC0, 0xFF), # cP\r
+    (0x80, 0x80, 0x80, 0xFF), # cw\r
+    (0xFF, 0x00, 0x00, 0xFF), # cR\r
+    (0x00, 0xFF, 0x00, 0xFF), # cG\r
+    (0xFF, 0xFF, 0x00, 0xFF), # cY\r
+    (0x00, 0x00, 0xFF, 0xFF), # cB\r
+    (0xFF, 0x00, 0xFF, 0xFF), # cM\r
+    (0x00, 0xFF, 0xFF, 0xFF), # cC\r
+    (0xFF, 0xFF, 0xFF, 0xFF), # cW\r
+    (0x00, 0x00, 0x00, 0x80), # cD\r
+    (0x00, 0x00, 0x00, 0x00), # cT\r
+    ]\r
+    def outpix(colour):\r
+        return pixvals[colour]\r
+    def finalisepix(colour):\r
+        # cD is used internally, but can't be output. Convert to cK.\r
+        if colour == cD:\r
+            return cK\r
+        return colour\r
+    def halftone(col1, col2):\r
+        return (col1, col2)\r
+else:\r
+    # True colour.\r
+    cK = (0x00, 0x00, 0x00, 0xFF)\r
+    cr = (0x80, 0x00, 0x00, 0xFF)\r
+    cg = (0x00, 0x80, 0x00, 0xFF)\r
+    cy = (0x80, 0x80, 0x00, 0xFF)\r
+    cb = (0x00, 0x00, 0x80, 0xFF)\r
+    cm = (0x80, 0x00, 0x80, 0xFF)\r
+    cc = (0x00, 0x80, 0x80, 0xFF)\r
+    cP = (0xC0, 0xC0, 0xC0, 0xFF)\r
+    cw = (0x80, 0x80, 0x80, 0xFF)\r
+    cR = (0xFF, 0x00, 0x00, 0xFF)\r
+    cG = (0x00, 0xFF, 0x00, 0xFF)\r
+    cY = (0xFF, 0xFF, 0x00, 0xFF)\r
+    cB = (0x00, 0x00, 0xFF, 0xFF)\r
+    cM = (0xFF, 0x00, 0xFF, 0xFF)\r
+    cC = (0x00, 0xFF, 0xFF, 0xFF)\r
+    cW = (0xFF, 0xFF, 0xFF, 0xFF)\r
+    cD = (0x00, 0x00, 0x00, 0x80)\r
+    cT = (0x00, 0x00, 0x00, 0x00)\r
+    def greypix(value):\r
+        value = max(min(value, 1), 0)\r
+        return (int(round(0xFF*value)),) * 3 + (0xFF,)\r
+    def yellowpix(value):\r
+        value = max(min(value, 1), 0)\r
+        return (int(round(0xFF*value)),) * 2 + (0, 0xFF)\r
+    def bluepix(value):\r
+        value = max(min(value, 1), 0)\r
+        return (0, 0, int(round(0xFF*value)), 0xFF)\r
+    def dark(value):\r
+        value = max(min(value, 1), 0)\r
+        return (0, 0, 0, int(round(0xFF*value)))\r
+    def blend(col1, col2):\r
+        r1,g1,b1,a1 = col1\r
+        r2,g2,b2,a2 = col2\r
+        r = int(round((r1*a1 + r2*(0xFF-a1)) / 255.0))\r
+        g = int(round((g1*a1 + g2*(0xFF-a1)) / 255.0))\r
+        b = int(round((b1*a1 + b2*(0xFF-a1)) / 255.0))\r
+        a = int(round((255*a1 + a2*(0xFF-a1)) / 255.0))\r
+        return r, g, b, a\r
+    def outpix(colour):\r
+        return colour\r
+    if colours == 2:\r
+        # True colour with no alpha blending: we still have to\r
+        # finalise half-dark pixels to black.\r
+        def finalisepix(colour):\r
+            if colour[3] > 0:\r
+                return colour[:3] + (0xFF,)\r
+            return colour\r
+    else:\r
+        def finalisepix(colour):\r
+            return colour\r
+    def halftone(col1, col2):\r
+        r1,g1,b1,a1 = col1\r
+        r2,g2,b2,a2 = col2\r
+        colret = (int(r1+r2)/2, int(g1+g2)/2, int(b1+b2)/2, int(a1+a2)/2)\r
+        return (colret, colret)\r
+\r
+if test:\r
+    testrun(eval(realargs[0]), realargs[1])\r
+else:\r
+    drawicon(eval(realargs[0]), int(realargs[1]), realargs[2], orangebackground)\r
diff --git a/putty/IMPORT.C b/putty/IMPORT.C
new file mode 100644 (file)
index 0000000..1490c64
--- /dev/null
@@ -0,0 +1,1715 @@
+/*\r
+ * Code for PuTTY to import and export private key files in other\r
+ * SSH clients' formats.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "misc.h"\r
+\r
+int openssh_encrypted(const Filename *filename);\r
+struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,\r
+                                 const char **errmsg_p);\r
+int openssh_write(const Filename *filename, struct ssh2_userkey *key,\r
+                 char *passphrase);\r
+\r
+int sshcom_encrypted(const Filename *filename, char **comment);\r
+struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,\r
+                                const char **errmsg_p);\r
+int sshcom_write(const Filename *filename, struct ssh2_userkey *key,\r
+                char *passphrase);\r
+\r
+/*\r
+ * Given a key type, determine whether we know how to import it.\r
+ */\r
+int import_possible(int type)\r
+{\r
+    if (type == SSH_KEYTYPE_OPENSSH)\r
+       return 1;\r
+    if (type == SSH_KEYTYPE_SSHCOM)\r
+       return 1;\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Given a key type, determine what native key type\r
+ * (SSH_KEYTYPE_SSH1 or SSH_KEYTYPE_SSH2) it will come out as once\r
+ * we've imported it.\r
+ */\r
+int import_target_type(int type)\r
+{\r
+    /*\r
+     * There are no known foreign SSH-1 key formats.\r
+     */\r
+    return SSH_KEYTYPE_SSH2;\r
+}\r
+\r
+/*\r
+ * Determine whether a foreign key is encrypted.\r
+ */\r
+int import_encrypted(const Filename *filename, int type, char **comment)\r
+{\r
+    if (type == SSH_KEYTYPE_OPENSSH) {\r
+       /* OpenSSH doesn't do key comments */\r
+       *comment = dupstr(filename_to_str(filename));\r
+       return openssh_encrypted(filename);\r
+    }\r
+    if (type == SSH_KEYTYPE_SSHCOM) {\r
+       return sshcom_encrypted(filename, comment);\r
+    }\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Import an SSH-1 key.\r
+ */\r
+int import_ssh1(const Filename *filename, int type,\r
+               struct RSAKey *key, char *passphrase, const char **errmsg_p)\r
+{\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Import an SSH-2 key.\r
+ */\r
+struct ssh2_userkey *import_ssh2(const Filename *filename, int type,\r
+                                char *passphrase, const char **errmsg_p)\r
+{\r
+    if (type == SSH_KEYTYPE_OPENSSH)\r
+       return openssh_read(filename, passphrase, errmsg_p);\r
+    if (type == SSH_KEYTYPE_SSHCOM)\r
+       return sshcom_read(filename, passphrase, errmsg_p);\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Export an SSH-1 key.\r
+ */\r
+int export_ssh1(const Filename *filename, int type, struct RSAKey *key,\r
+               char *passphrase)\r
+{\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Export an SSH-2 key.\r
+ */\r
+int export_ssh2(const Filename *filename, int type,\r
+                struct ssh2_userkey *key, char *passphrase)\r
+{\r
+    if (type == SSH_KEYTYPE_OPENSSH)\r
+       return openssh_write(filename, key, passphrase);\r
+    if (type == SSH_KEYTYPE_SSHCOM)\r
+       return sshcom_write(filename, key, passphrase);\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Strip trailing CRs and LFs at the end of a line of text.\r
+ */\r
+void strip_crlf(char *str)\r
+{\r
+    char *p = str + strlen(str);\r
+\r
+    while (p > str && (p[-1] == '\r' || p[-1] == '\n'))\r
+       *--p = '\0';\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Helper routines. (The base64 ones are defined in sshpubk.c.)\r
+ */\r
+\r
+#define isbase64(c) (    ((c) >= 'A' && (c) <= 'Z') || \\r
+                         ((c) >= 'a' && (c) <= 'z') || \\r
+                         ((c) >= '0' && (c) <= '9') || \\r
+                         (c) == '+' || (c) == '/' || (c) == '=' \\r
+                         )\r
+\r
+/*\r
+ * Read an ASN.1/BER identifier and length pair.\r
+ * \r
+ * Flags are a combination of the #defines listed below.\r
+ * \r
+ * Returns -1 if unsuccessful; otherwise returns the number of\r
+ * bytes used out of the source data.\r
+ */\r
+\r
+/* ASN.1 tag classes. */\r
+#define ASN1_CLASS_UNIVERSAL        (0 << 6)\r
+#define ASN1_CLASS_APPLICATION      (1 << 6)\r
+#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)\r
+#define ASN1_CLASS_PRIVATE          (3 << 6)\r
+#define ASN1_CLASS_MASK             (3 << 6)\r
+\r
+/* Primitive versus constructed bit. */\r
+#define ASN1_CONSTRUCTED            (1 << 5)\r
+\r
+static int ber_read_id_len(void *source, int sourcelen,\r
+                          int *id, int *length, int *flags)\r
+{\r
+    unsigned char *p = (unsigned char *) source;\r
+\r
+    if (sourcelen == 0)\r
+       return -1;\r
+\r
+    *flags = (*p & 0xE0);\r
+    if ((*p & 0x1F) == 0x1F) {\r
+       *id = 0;\r
+       while (*p & 0x80) {\r
+           p++, sourcelen--;\r
+           if (sourcelen == 0)\r
+               return -1;\r
+           *id = (*id << 7) | (*p & 0x7F);\r
+       }\r
+       p++, sourcelen--;\r
+    } else {\r
+       *id = *p & 0x1F;\r
+       p++, sourcelen--;\r
+    }\r
+\r
+    if (sourcelen == 0)\r
+       return -1;\r
+\r
+    if (*p & 0x80) {\r
+       int n = *p & 0x7F;\r
+       p++, sourcelen--;\r
+       if (sourcelen < n)\r
+           return -1;\r
+       *length = 0;\r
+       while (n--)\r
+           *length = (*length << 8) | (*p++);\r
+       sourcelen -= n;\r
+    } else {\r
+       *length = *p;\r
+       p++, sourcelen--;\r
+    }\r
+\r
+    return p - (unsigned char *) source;\r
+}\r
+\r
+/*\r
+ * Write an ASN.1/BER identifier and length pair. Returns the\r
+ * number of bytes consumed. Assumes dest contains enough space.\r
+ * Will avoid writing anything if dest is NULL, but still return\r
+ * amount of space required.\r
+ */\r
+static int ber_write_id_len(void *dest, int id, int length, int flags)\r
+{\r
+    unsigned char *d = (unsigned char *)dest;\r
+    int len = 0;\r
+\r
+    if (id <= 30) {\r
+       /*\r
+        * Identifier is one byte.\r
+        */\r
+       len++;\r
+       if (d) *d++ = id | flags;\r
+    } else {\r
+       int n;\r
+       /*\r
+        * Identifier is multiple bytes: the first byte is 11111\r
+        * plus the flags, and subsequent bytes encode the value of\r
+        * the identifier, 7 bits at a time, with the top bit of\r
+        * each byte 1 except the last one which is 0.\r
+        */\r
+       len++;\r
+       if (d) *d++ = 0x1F | flags;\r
+       for (n = 1; (id >> (7*n)) > 0; n++)\r
+           continue;                  /* count the bytes */\r
+       while (n--) {\r
+           len++;\r
+           if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F);\r
+       }\r
+    }\r
+\r
+    if (length < 128) {\r
+       /*\r
+        * Length is one byte.\r
+        */\r
+       len++;\r
+       if (d) *d++ = length;\r
+    } else {\r
+       int n;\r
+       /*\r
+        * Length is multiple bytes. The first is 0x80 plus the\r
+        * number of subsequent bytes, and the subsequent bytes\r
+        * encode the actual length.\r
+        */\r
+       for (n = 1; (length >> (8*n)) > 0; n++)\r
+           continue;                  /* count the bytes */\r
+       len++;\r
+       if (d) *d++ = 0x80 | n;\r
+       while (n--) {\r
+           len++;\r
+           if (d) *d++ = (length >> (8*n)) & 0xFF;\r
+       }\r
+    }\r
+\r
+    return len;\r
+}\r
+\r
+static int put_string(void *target, void *data, int len)\r
+{\r
+    unsigned char *d = (unsigned char *)target;\r
+\r
+    PUT_32BIT(d, len);\r
+    memcpy(d+4, data, len);\r
+    return len+4;\r
+}\r
+\r
+static int put_mp(void *target, void *data, int len)\r
+{\r
+    unsigned char *d = (unsigned char *)target;\r
+    unsigned char *i = (unsigned char *)data;\r
+\r
+    if (*i & 0x80) {\r
+        PUT_32BIT(d, len+1);\r
+        d[4] = 0;\r
+        memcpy(d+5, data, len);\r
+        return len+5;\r
+    } else {\r
+        PUT_32BIT(d, len);\r
+        memcpy(d+4, data, len);\r
+        return len+4;\r
+    }\r
+}\r
+\r
+/* Simple structure to point to an mp-int within a blob. */\r
+struct mpint_pos { void *start; int bytes; };\r
+\r
+static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret)\r
+{\r
+    int bytes;\r
+    unsigned char *d = (unsigned char *) data;\r
+\r
+    if (len < 4)\r
+        goto error;\r
+    bytes = GET_32BIT(d);\r
+    if (len < 4+bytes)\r
+        goto error;\r
+\r
+    ret->start = d + 4;\r
+    ret->bytes = bytes;\r
+    return bytes+4;\r
+\r
+    error:\r
+    ret->start = NULL;\r
+    ret->bytes = -1;\r
+    return len;                        /* ensure further calls fail as well */\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Code to read and write OpenSSH private keys.\r
+ */\r
+\r
+enum { OSSH_DSA, OSSH_RSA };\r
+enum { OSSH_ENC_3DES, OSSH_ENC_AES };\r
+struct openssh_key {\r
+    int type;\r
+    int encrypted, encryption;\r
+    char iv[32];\r
+    unsigned char *keyblob;\r
+    int keyblob_len, keyblob_size;\r
+};\r
+\r
+static struct openssh_key *load_openssh_key(const Filename *filename,\r
+                                           const char **errmsg_p)\r
+{\r
+    struct openssh_key *ret;\r
+    FILE *fp;\r
+    char *line = NULL;\r
+    char *errmsg, *p;\r
+    int headers_done;\r
+    char base64_bit[4];\r
+    int base64_chars = 0;\r
+\r
+    ret = snew(struct openssh_key);\r
+    ret->keyblob = NULL;\r
+    ret->keyblob_len = ret->keyblob_size = 0;\r
+    ret->encrypted = 0;\r
+    memset(ret->iv, 0, sizeof(ret->iv));\r
+\r
+    fp = f_open(*filename, "r", FALSE);\r
+    if (!fp) {\r
+       errmsg = "unable to open key file";\r
+       goto error;\r
+    }\r
+\r
+    if (!(line = fgetline(fp))) {\r
+       errmsg = "unexpected end of file";\r
+       goto error;\r
+    }\r
+    strip_crlf(line);\r
+    if (0 != strncmp(line, "-----BEGIN ", 11) ||\r
+       0 != strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) {\r
+       errmsg = "file does not begin with OpenSSH key header";\r
+       goto error;\r
+    }\r
+    if (!strcmp(line, "-----BEGIN RSA PRIVATE KEY-----"))\r
+       ret->type = OSSH_RSA;\r
+    else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----"))\r
+       ret->type = OSSH_DSA;\r
+    else {\r
+       errmsg = "unrecognised key type";\r
+       goto error;\r
+    }\r
+    memset(line, 0, strlen(line));\r
+    sfree(line);\r
+    line = NULL;\r
+\r
+    headers_done = 0;\r
+    while (1) {\r
+       if (!(line = fgetline(fp))) {\r
+           errmsg = "unexpected end of file";\r
+           goto error;\r
+       }\r
+       strip_crlf(line);\r
+       if (0 == strncmp(line, "-----END ", 9) &&\r
+           0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----"))\r
+           break;                     /* done */\r
+       if ((p = strchr(line, ':')) != NULL) {\r
+           if (headers_done) {\r
+               errmsg = "header found in body of key data";\r
+               goto error;\r
+           }\r
+           *p++ = '\0';\r
+           while (*p && isspace((unsigned char)*p)) p++;\r
+           if (!strcmp(line, "Proc-Type")) {\r
+               if (p[0] != '4' || p[1] != ',') {\r
+                   errmsg = "Proc-Type is not 4 (only 4 is supported)";\r
+                   goto error;\r
+               }\r
+               p += 2;\r
+               if (!strcmp(p, "ENCRYPTED"))\r
+                   ret->encrypted = 1;\r
+           } else if (!strcmp(line, "DEK-Info")) {\r
+               int i, j, ivlen;\r
+\r
+               if (!strncmp(p, "DES-EDE3-CBC,", 13)) {\r
+                   ret->encryption = OSSH_ENC_3DES;\r
+                   ivlen = 8;\r
+               } else if (!strncmp(p, "AES-128-CBC,", 12)) {\r
+                   ret->encryption = OSSH_ENC_AES;\r
+                   ivlen = 16;\r
+               } else {\r
+                   errmsg = "unsupported cipher";\r
+                   goto error;\r
+               }\r
+               p = strchr(p, ',') + 1;/* always non-NULL, by above checks */\r
+               for (i = 0; i < ivlen; i++) {\r
+                   if (1 != sscanf(p, "%2x", &j)) {\r
+                       errmsg = "expected more iv data in DEK-Info";\r
+                       goto error;\r
+                   }\r
+                   ret->iv[i] = j;\r
+                   p += 2;\r
+               }\r
+               if (*p) {\r
+                   errmsg = "more iv data than expected in DEK-Info";\r
+                   goto error;\r
+               }\r
+           }\r
+       } else {\r
+           headers_done = 1;\r
+\r
+           p = line;\r
+           while (isbase64(*p)) {\r
+                base64_bit[base64_chars++] = *p;\r
+                if (base64_chars == 4) {\r
+                    unsigned char out[3];\r
+                    int len;\r
+\r
+                    base64_chars = 0;\r
+\r
+                    len = base64_decode_atom(base64_bit, out);\r
+\r
+                    if (len <= 0) {\r
+                        errmsg = "invalid base64 encoding";\r
+                        goto error;\r
+                    }\r
+\r
+                    if (ret->keyblob_len + len > ret->keyblob_size) {\r
+                        ret->keyblob_size = ret->keyblob_len + len + 256;\r
+                        ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,\r
+                                              unsigned char);\r
+                    }\r
+\r
+                    memcpy(ret->keyblob + ret->keyblob_len, out, len);\r
+                    ret->keyblob_len += len;\r
+\r
+                    memset(out, 0, sizeof(out));\r
+                }\r
+\r
+               p++;\r
+           }\r
+       }\r
+       memset(line, 0, strlen(line));\r
+       sfree(line);\r
+       line = NULL;\r
+    }\r
+\r
+    if (ret->keyblob_len == 0 || !ret->keyblob) {\r
+       errmsg = "key body not present";\r
+       goto error;\r
+    }\r
+\r
+    if (ret->encrypted && ret->keyblob_len % 8 != 0) {\r
+       errmsg = "encrypted key blob is not a multiple of cipher block size";\r
+       goto error;\r
+    }\r
+\r
+    memset(base64_bit, 0, sizeof(base64_bit));\r
+    if (errmsg_p) *errmsg_p = NULL;\r
+    return ret;\r
+\r
+    error:\r
+    if (line) {\r
+       memset(line, 0, strlen(line));\r
+       sfree(line);\r
+       line = NULL;\r
+    }\r
+    memset(base64_bit, 0, sizeof(base64_bit));\r
+    if (ret) {\r
+       if (ret->keyblob) {\r
+            memset(ret->keyblob, 0, ret->keyblob_size);\r
+            sfree(ret->keyblob);\r
+        }\r
+        memset(ret, 0, sizeof(*ret));\r
+       sfree(ret);\r
+    }\r
+    if (errmsg_p) *errmsg_p = errmsg;\r
+    return NULL;\r
+}\r
+\r
+int openssh_encrypted(const Filename *filename)\r
+{\r
+    struct openssh_key *key = load_openssh_key(filename, NULL);\r
+    int ret;\r
+\r
+    if (!key)\r
+       return 0;\r
+    ret = key->encrypted;\r
+    memset(key->keyblob, 0, key->keyblob_size);\r
+    sfree(key->keyblob);\r
+    memset(key, 0, sizeof(*key));\r
+    sfree(key);\r
+    return ret;\r
+}\r
+\r
+struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,\r
+                                 const char **errmsg_p)\r
+{\r
+    struct openssh_key *key = load_openssh_key(filename, errmsg_p);\r
+    struct ssh2_userkey *retkey;\r
+    unsigned char *p;\r
+    int ret, id, len, flags;\r
+    int i, num_integers;\r
+    struct ssh2_userkey *retval = NULL;\r
+    char *errmsg;\r
+    unsigned char *blob;\r
+    int blobsize = 0, blobptr, privptr;\r
+    char *modptr = NULL;\r
+    int modlen = 0;\r
+\r
+    blob = NULL;\r
+\r
+    if (!key)\r
+       return NULL;\r
+\r
+    if (key->encrypted) {\r
+       /*\r
+        * Derive encryption key from passphrase and iv/salt:\r
+        * \r
+        *  - let block A equal MD5(passphrase || iv)\r
+        *  - let block B equal MD5(A || passphrase || iv)\r
+        *  - block C would be MD5(B || passphrase || iv) and so on\r
+        *  - encryption key is the first N bytes of A || B\r
+        *\r
+        * (Note that only 8 bytes of the iv are used for key\r
+        * derivation, even when the key is encrypted with AES and\r
+        * hence there are 16 bytes available.)\r
+        */\r
+       struct MD5Context md5c;\r
+       unsigned char keybuf[32];\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Update(&md5c, (unsigned char *)key->iv, 8);\r
+       MD5Final(keybuf, &md5c);\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, keybuf, 16);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Update(&md5c, (unsigned char *)key->iv, 8);\r
+       MD5Final(keybuf+16, &md5c);\r
+\r
+       /*\r
+        * Now decrypt the key blob.\r
+        */\r
+       if (key->encryption == OSSH_ENC_3DES)\r
+           des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv,\r
+                                    key->keyblob, key->keyblob_len);\r
+       else {\r
+           void *ctx;\r
+           assert(key->encryption == OSSH_ENC_AES);\r
+           ctx = aes_make_context();\r
+           aes128_key(ctx, keybuf);\r
+           aes_iv(ctx, (unsigned char *)key->iv);\r
+           aes_ssh2_decrypt_blk(ctx, key->keyblob, key->keyblob_len);\r
+           aes_free_context(ctx);\r
+       }\r
+\r
+        memset(&md5c, 0, sizeof(md5c));\r
+        memset(keybuf, 0, sizeof(keybuf));\r
+    }\r
+\r
+    /*\r
+     * Now we have a decrypted key blob, which contains an ASN.1\r
+     * encoded private key. We must now untangle the ASN.1.\r
+     *\r
+     * We expect the whole key blob to be formatted as a SEQUENCE\r
+     * (0x30 followed by a length code indicating that the rest of\r
+     * the blob is part of the sequence). Within that SEQUENCE we\r
+     * expect to see a bunch of INTEGERs. What those integers mean\r
+     * depends on the key type:\r
+     *\r
+     *  - For RSA, we expect the integers to be 0, n, e, d, p, q,\r
+     *    dmp1, dmq1, iqmp in that order. (The last three are d mod\r
+     *    (p-1), d mod (q-1), inverse of q mod p respectively.)\r
+     *\r
+     *  - For DSA, we expect them to be 0, p, q, g, y, x in that\r
+     *    order.\r
+     */\r
+    \r
+    p = key->keyblob;\r
+\r
+    /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */\r
+    ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);\r
+    p += ret;\r
+    if (ret < 0 || id != 16) {\r
+       errmsg = "ASN.1 decoding failure";\r
+       retval = SSH2_WRONG_PASSPHRASE;\r
+       goto error;\r
+    }\r
+\r
+    /* Expect a load of INTEGERs. */\r
+    if (key->type == OSSH_RSA)\r
+       num_integers = 9;\r
+    else if (key->type == OSSH_DSA)\r
+       num_integers = 6;\r
+    else\r
+       num_integers = 0;              /* placate compiler warnings */\r
+\r
+    /*\r
+     * Space to create key blob in.\r
+     */\r
+    blobsize = 256+key->keyblob_len;\r
+    blob = snewn(blobsize, unsigned char);\r
+    PUT_32BIT(blob, 7);\r
+    if (key->type == OSSH_DSA)\r
+       memcpy(blob+4, "ssh-dss", 7);\r
+    else if (key->type == OSSH_RSA)\r
+       memcpy(blob+4, "ssh-rsa", 7);\r
+    blobptr = 4+7;\r
+    privptr = -1;\r
+\r
+    for (i = 0; i < num_integers; i++) {\r
+       ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,\r
+                             &id, &len, &flags);\r
+       p += ret;\r
+       if (ret < 0 || id != 2 ||\r
+           key->keyblob+key->keyblob_len-p < len) {\r
+           errmsg = "ASN.1 decoding failure";\r
+           retval = SSH2_WRONG_PASSPHRASE;\r
+           goto error;\r
+       }\r
+\r
+       if (i == 0) {\r
+           /*\r
+            * The first integer should be zero always (I think\r
+            * this is some sort of version indication).\r
+            */\r
+           if (len != 1 || p[0] != 0) {\r
+               errmsg = "version number mismatch";\r
+               goto error;\r
+           }\r
+       } else if (key->type == OSSH_RSA) {\r
+           /*\r
+            * Integers 1 and 2 go into the public blob but in the\r
+            * opposite order; integers 3, 4, 5 and 8 go into the\r
+            * private blob. The other two (6 and 7) are ignored.\r
+            */\r
+           if (i == 1) {\r
+               /* Save the details for after we deal with number 2. */\r
+               modptr = (char *)p;\r
+               modlen = len;\r
+           } else if (i != 6 && i != 7) {\r
+               PUT_32BIT(blob+blobptr, len);\r
+               memcpy(blob+blobptr+4, p, len);\r
+               blobptr += 4+len;\r
+               if (i == 2) {\r
+                   PUT_32BIT(blob+blobptr, modlen);\r
+                   memcpy(blob+blobptr+4, modptr, modlen);\r
+                   blobptr += 4+modlen;\r
+                   privptr = blobptr;\r
+               }\r
+           }\r
+       } else if (key->type == OSSH_DSA) {\r
+           /*\r
+            * Integers 1-4 go into the public blob; integer 5 goes\r
+            * into the private blob.\r
+            */\r
+           PUT_32BIT(blob+blobptr, len);\r
+           memcpy(blob+blobptr+4, p, len);\r
+           blobptr += 4+len;\r
+           if (i == 4)\r
+               privptr = blobptr;\r
+       }\r
+\r
+       /* Skip past the number. */\r
+       p += len;\r
+    }\r
+\r
+    /*\r
+     * Now put together the actual key. Simplest way to do this is\r
+     * to assemble our own key blobs and feed them to the createkey\r
+     * functions; this is a bit faffy but it does mean we get all\r
+     * the sanity checks for free.\r
+     */\r
+    assert(privptr > 0);              /* should have bombed by now if not */\r
+    retkey = snew(struct ssh2_userkey);\r
+    retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);\r
+    retkey->data = retkey->alg->createkey(blob, privptr,\r
+                                         blob+privptr, blobptr-privptr);\r
+    if (!retkey->data) {\r
+       sfree(retkey);\r
+       errmsg = "unable to create key data structure";\r
+       goto error;\r
+    }\r
+\r
+    retkey->comment = dupstr("imported-openssh-key");\r
+    errmsg = NULL;                     /* no error */\r
+    retval = retkey;\r
+\r
+    error:\r
+    if (blob) {\r
+        memset(blob, 0, blobsize);\r
+        sfree(blob);\r
+    }\r
+    memset(key->keyblob, 0, key->keyblob_size);\r
+    sfree(key->keyblob);\r
+    memset(key, 0, sizeof(*key));\r
+    sfree(key);\r
+    if (errmsg_p) *errmsg_p = errmsg;\r
+    return retval;\r
+}\r
+\r
+int openssh_write(const Filename *filename, struct ssh2_userkey *key,\r
+                 char *passphrase)\r
+{\r
+    unsigned char *pubblob, *privblob, *spareblob;\r
+    int publen, privlen, sparelen = 0;\r
+    unsigned char *outblob;\r
+    int outlen;\r
+    struct mpint_pos numbers[9];\r
+    int nnumbers, pos, len, seqlen, i;\r
+    char *header, *footer;\r
+    char zero[1];\r
+    unsigned char iv[8];\r
+    int ret = 0;\r
+    FILE *fp;\r
+\r
+    /*\r
+     * Fetch the key blobs.\r
+     */\r
+    pubblob = key->alg->public_blob(key->data, &publen);\r
+    privblob = key->alg->private_blob(key->data, &privlen);\r
+    spareblob = outblob = NULL;\r
+\r
+    /*\r
+     * Find the sequence of integers to be encoded into the OpenSSH\r
+     * key blob, and also decide on the header line.\r
+     */\r
+    if (key->alg == &ssh_rsa) {\r
+        int pos;\r
+        struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;\r
+        Bignum bd, bp, bq, bdmp1, bdmq1;\r
+\r
+        pos = 4 + GET_32BIT(pubblob);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);\r
+        pos = 0;\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);\r
+\r
+        assert(e.start && iqmp.start); /* can't go wrong */\r
+\r
+        /* We also need d mod (p-1) and d mod (q-1). */\r
+        bd = bignum_from_bytes(d.start, d.bytes);\r
+        bp = bignum_from_bytes(p.start, p.bytes);\r
+        bq = bignum_from_bytes(q.start, q.bytes);\r
+        decbn(bp);\r
+        decbn(bq);\r
+        bdmp1 = bigmod(bd, bp);\r
+        bdmq1 = bigmod(bd, bq);\r
+        freebn(bd);\r
+        freebn(bp);\r
+        freebn(bq);\r
+\r
+        dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;\r
+        dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;\r
+        sparelen = dmp1.bytes + dmq1.bytes;\r
+        spareblob = snewn(sparelen, unsigned char);\r
+        dmp1.start = spareblob;\r
+        dmq1.start = spareblob + dmp1.bytes;\r
+        for (i = 0; i < dmp1.bytes; i++)\r
+            spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);\r
+        for (i = 0; i < dmq1.bytes; i++)\r
+            spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);\r
+        freebn(bdmp1);\r
+        freebn(bdmq1);\r
+\r
+        numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';\r
+        numbers[1] = n;\r
+        numbers[2] = e;\r
+        numbers[3] = d;\r
+        numbers[4] = p;\r
+        numbers[5] = q;\r
+        numbers[6] = dmp1;\r
+        numbers[7] = dmq1;\r
+        numbers[8] = iqmp;\r
+\r
+        nnumbers = 9;\r
+        header = "-----BEGIN RSA PRIVATE KEY-----\n";\r
+        footer = "-----END RSA PRIVATE KEY-----\n";\r
+    } else if (key->alg == &ssh_dss) {\r
+        int pos;\r
+        struct mpint_pos p, q, g, y, x;\r
+\r
+        pos = 4 + GET_32BIT(pubblob);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);\r
+        pos = 0;\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);\r
+\r
+        assert(y.start && x.start); /* can't go wrong */\r
+\r
+        numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; \r
+        numbers[1] = p;\r
+        numbers[2] = q;\r
+        numbers[3] = g;\r
+        numbers[4] = y;\r
+        numbers[5] = x;\r
+\r
+        nnumbers = 6;\r
+        header = "-----BEGIN DSA PRIVATE KEY-----\n";\r
+        footer = "-----END DSA PRIVATE KEY-----\n";\r
+    } else {\r
+        assert(0);                     /* zoinks! */\r
+       exit(1); /* XXX: GCC doesn't understand assert() on some systems. */\r
+    }\r
+\r
+    /*\r
+     * Now count up the total size of the ASN.1 encoded integers,\r
+     * so as to determine the length of the containing SEQUENCE.\r
+     */\r
+    len = 0;\r
+    for (i = 0; i < nnumbers; i++) {\r
+       len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);\r
+       len += numbers[i].bytes;\r
+    }\r
+    seqlen = len;\r
+    /* Now add on the SEQUENCE header. */\r
+    len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);\r
+    /* Round up to the cipher block size, ensuring we have at least one\r
+     * byte of padding (see below). */\r
+    outlen = len;\r
+    if (passphrase)\r
+       outlen = (outlen+8) &~ 7;\r
+\r
+    /*\r
+     * Now we know how big outblob needs to be. Allocate it.\r
+     */\r
+    outblob = snewn(outlen, unsigned char);\r
+\r
+    /*\r
+     * And write the data into it.\r
+     */\r
+    pos = 0;\r
+    pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);\r
+    for (i = 0; i < nnumbers; i++) {\r
+       pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);\r
+       memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);\r
+       pos += numbers[i].bytes;\r
+    }\r
+\r
+    /*\r
+     * Padding on OpenSSH keys is deterministic. The number of\r
+     * padding bytes is always more than zero, and always at most\r
+     * the cipher block length. The value of each padding byte is\r
+     * equal to the number of padding bytes. So a plaintext that's\r
+     * an exact multiple of the block size will be padded with 08\r
+     * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a\r
+     * plaintext one byte less than a multiple of the block size\r
+     * will be padded with just 01.\r
+     * \r
+     * This enables the OpenSSL key decryption function to strip\r
+     * off the padding algorithmically and return the unpadded\r
+     * plaintext to the next layer: it looks at the final byte, and\r
+     * then expects to find that many bytes at the end of the data\r
+     * with the same value. Those are all removed and the rest is\r
+     * returned.\r
+     */\r
+    assert(pos == len);\r
+    while (pos < outlen) {\r
+        outblob[pos++] = outlen - len;\r
+    }\r
+\r
+    /*\r
+     * Encrypt the key.\r
+     *\r
+     * For the moment, we still encrypt our OpenSSH keys using\r
+     * old-style 3DES.\r
+     */\r
+    if (passphrase) {\r
+       /*\r
+        * Invent an iv. Then derive encryption key from passphrase\r
+        * and iv/salt:\r
+        * \r
+        *  - let block A equal MD5(passphrase || iv)\r
+        *  - let block B equal MD5(A || passphrase || iv)\r
+        *  - block C would be MD5(B || passphrase || iv) and so on\r
+        *  - encryption key is the first N bytes of A || B\r
+        */\r
+       struct MD5Context md5c;\r
+       unsigned char keybuf[32];\r
+\r
+       for (i = 0; i < 8; i++) iv[i] = random_byte();\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Update(&md5c, iv, 8);\r
+       MD5Final(keybuf, &md5c);\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, keybuf, 16);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Update(&md5c, iv, 8);\r
+       MD5Final(keybuf+16, &md5c);\r
+\r
+       /*\r
+        * Now encrypt the key blob.\r
+        */\r
+       des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen);\r
+\r
+        memset(&md5c, 0, sizeof(md5c));\r
+        memset(keybuf, 0, sizeof(keybuf));\r
+    }\r
+\r
+    /*\r
+     * And save it. We'll use Unix line endings just in case it's\r
+     * subsequently transferred in binary mode.\r
+     */\r
+    fp = f_open(*filename, "wb", TRUE);      /* ensure Unix line endings */\r
+    if (!fp)\r
+       goto error;\r
+    fputs(header, fp);\r
+    if (passphrase) {\r
+       fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,");\r
+       for (i = 0; i < 8; i++)\r
+           fprintf(fp, "%02X", iv[i]);\r
+       fprintf(fp, "\n\n");\r
+    }\r
+    base64_encode(fp, outblob, outlen, 64);\r
+    fputs(footer, fp);\r
+    fclose(fp);\r
+    ret = 1;\r
+\r
+    error:\r
+    if (outblob) {\r
+        memset(outblob, 0, outlen);\r
+        sfree(outblob);\r
+    }\r
+    if (spareblob) {\r
+        memset(spareblob, 0, sparelen);\r
+        sfree(spareblob);\r
+    }\r
+    if (privblob) {\r
+        memset(privblob, 0, privlen);\r
+        sfree(privblob);\r
+    }\r
+    if (pubblob) {\r
+        memset(pubblob, 0, publen);\r
+        sfree(pubblob);\r
+    }\r
+    return ret;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Code to read ssh.com private keys.\r
+ */\r
+\r
+/*\r
+ * The format of the base64 blob is largely SSH-2-packet-formatted,\r
+ * except that mpints are a bit different: they're more like the\r
+ * old SSH-1 mpint. You have a 32-bit bit count N, followed by\r
+ * (N+7)/8 bytes of data.\r
+ * \r
+ * So. The blob contains:\r
+ * \r
+ *  - uint32 0x3f6ff9eb       (magic number)\r
+ *  - uint32 size             (total blob size)\r
+ *  - string key-type         (see below)\r
+ *  - string cipher-type      (tells you if key is encrypted)\r
+ *  - string encrypted-blob\r
+ * \r
+ * (The first size field includes the size field itself and the\r
+ * magic number before it. All other size fields are ordinary SSH-2\r
+ * strings, so the size field indicates how much data is to\r
+ * _follow_.)\r
+ * \r
+ * The encrypted blob, once decrypted, contains a single string\r
+ * which in turn contains the payload. (This allows padding to be\r
+ * added after that string while still making it clear where the\r
+ * real payload ends. Also it probably makes for a reasonable\r
+ * decryption check.)\r
+ * \r
+ * The payload blob, for an RSA key, contains:\r
+ *  - mpint e\r
+ *  - mpint d\r
+ *  - mpint n  (yes, the public and private stuff is intermixed)\r
+ *  - mpint u  (presumably inverse of p mod q)\r
+ *  - mpint p  (p is the smaller prime)\r
+ *  - mpint q  (q is the larger)\r
+ * \r
+ * For a DSA key, the payload blob contains:\r
+ *  - uint32 0\r
+ *  - mpint p\r
+ *  - mpint g\r
+ *  - mpint q\r
+ *  - mpint y\r
+ *  - mpint x\r
+ * \r
+ * Alternatively, if the parameters are `predefined', that\r
+ * (0,p,g,q) sequence can be replaced by a uint32 1 and a string\r
+ * containing some predefined parameter specification. *shudder*,\r
+ * but I doubt we'll encounter this in real life.\r
+ * \r
+ * The key type strings are ghastly. The RSA key I looked at had a\r
+ * type string of\r
+ * \r
+ *   `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}'\r
+ * \r
+ * and the DSA key wasn't much better:\r
+ * \r
+ *   `dl-modp{sign{dsa-nist-sha1},dh{plain}}'\r
+ * \r
+ * It isn't clear that these will always be the same. I think it\r
+ * might be wise just to look at the `if-modn{sign{rsa' and\r
+ * `dl-modp{sign{dsa' prefixes.\r
+ * \r
+ * Finally, the encryption. The cipher-type string appears to be\r
+ * either `none' or `3des-cbc'. Looks as if this is SSH-2-style\r
+ * 3des-cbc (i.e. outer cbc rather than inner). The key is created\r
+ * from the passphrase by means of yet another hashing faff:\r
+ * \r
+ *  - first 16 bytes are MD5(passphrase)\r
+ *  - next 16 bytes are MD5(passphrase || first 16 bytes)\r
+ *  - if there were more, they'd be MD5(passphrase || first 32),\r
+ *    and so on.\r
+ */\r
+\r
+#define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb\r
+\r
+struct sshcom_key {\r
+    char comment[256];                 /* allowing any length is overkill */\r
+    unsigned char *keyblob;\r
+    int keyblob_len, keyblob_size;\r
+};\r
+\r
+static struct sshcom_key *load_sshcom_key(const Filename *filename,\r
+                                         const char **errmsg_p)\r
+{\r
+    struct sshcom_key *ret;\r
+    FILE *fp;\r
+    char *line = NULL;\r
+    int hdrstart, len;\r
+    char *errmsg, *p;\r
+    int headers_done;\r
+    char base64_bit[4];\r
+    int base64_chars = 0;\r
+\r
+    ret = snew(struct sshcom_key);\r
+    ret->comment[0] = '\0';\r
+    ret->keyblob = NULL;\r
+    ret->keyblob_len = ret->keyblob_size = 0;\r
+\r
+    fp = f_open(*filename, "r", FALSE);\r
+    if (!fp) {\r
+       errmsg = "unable to open key file";\r
+       goto error;\r
+    }\r
+    if (!(line = fgetline(fp))) {\r
+       errmsg = "unexpected end of file";\r
+       goto error;\r
+    }\r
+    strip_crlf(line);\r
+    if (0 != strcmp(line, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----")) {\r
+       errmsg = "file does not begin with ssh.com key header";\r
+       goto error;\r
+    }\r
+    memset(line, 0, strlen(line));\r
+    sfree(line);\r
+    line = NULL;\r
+\r
+    headers_done = 0;\r
+    while (1) {\r
+       if (!(line = fgetline(fp))) {\r
+           errmsg = "unexpected end of file";\r
+           goto error;\r
+       }\r
+       strip_crlf(line);\r
+        if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----"))\r
+            break;                     /* done */\r
+       if ((p = strchr(line, ':')) != NULL) {\r
+           if (headers_done) {\r
+               errmsg = "header found in body of key data";\r
+               goto error;\r
+           }\r
+           *p++ = '\0';\r
+           while (*p && isspace((unsigned char)*p)) p++;\r
+           hdrstart = p - line;\r
+\r
+            /*\r
+             * Header lines can end in a trailing backslash for\r
+             * continuation.\r
+             */\r
+           len = hdrstart + strlen(line+hdrstart);\r
+           assert(!line[len]);\r
+            while (line[len-1] == '\\') {\r
+               char *line2;\r
+               int line2len;\r
+\r
+               line2 = fgetline(fp);\r
+               if (!line2) {\r
+                    errmsg = "unexpected end of file";\r
+                    goto error;\r
+                }\r
+               strip_crlf(line2);\r
+\r
+               line2len = strlen(line2);\r
+               line = sresize(line, len + line2len + 1, char);\r
+               strcpy(line + len - 1, line2);\r
+               len += line2len - 1;\r
+               assert(!line[len]);\r
+\r
+               memset(line2, 0, strlen(line2));\r
+               sfree(line2);\r
+               line2 = NULL;\r
+            }\r
+           p = line + hdrstart;\r
+           strip_crlf(p);\r
+            if (!strcmp(line, "Comment")) {\r
+                /* Strip quotes in comment if present. */\r
+                if (p[0] == '"' && p[strlen(p)-1] == '"') {\r
+                    p++;\r
+                    p[strlen(p)-1] = '\0';\r
+                }\r
+                strncpy(ret->comment, p, sizeof(ret->comment));\r
+                ret->comment[sizeof(ret->comment)-1] = '\0';\r
+            }\r
+       } else {\r
+           headers_done = 1;\r
+\r
+           p = line;\r
+           while (isbase64(*p)) {\r
+                base64_bit[base64_chars++] = *p;\r
+                if (base64_chars == 4) {\r
+                    unsigned char out[3];\r
+\r
+                    base64_chars = 0;\r
+\r
+                    len = base64_decode_atom(base64_bit, out);\r
+\r
+                    if (len <= 0) {\r
+                        errmsg = "invalid base64 encoding";\r
+                        goto error;\r
+                    }\r
+\r
+                    if (ret->keyblob_len + len > ret->keyblob_size) {\r
+                        ret->keyblob_size = ret->keyblob_len + len + 256;\r
+                        ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,\r
+                                              unsigned char);\r
+                    }\r
+\r
+                    memcpy(ret->keyblob + ret->keyblob_len, out, len);\r
+                    ret->keyblob_len += len;\r
+                }\r
+\r
+               p++;\r
+           }\r
+       }\r
+       memset(line, 0, strlen(line));\r
+       sfree(line);\r
+       line = NULL;\r
+    }\r
+\r
+    if (ret->keyblob_len == 0 || !ret->keyblob) {\r
+       errmsg = "key body not present";\r
+       goto error;\r
+    }\r
+\r
+    if (errmsg_p) *errmsg_p = NULL;\r
+    return ret;\r
+\r
+    error:\r
+    if (line) {\r
+       memset(line, 0, strlen(line));\r
+       sfree(line);\r
+       line = NULL;\r
+    }\r
+    if (ret) {\r
+       if (ret->keyblob) {\r
+            memset(ret->keyblob, 0, ret->keyblob_size);\r
+            sfree(ret->keyblob);\r
+        }\r
+        memset(ret, 0, sizeof(*ret));\r
+       sfree(ret);\r
+    }\r
+    if (errmsg_p) *errmsg_p = errmsg;\r
+    return NULL;\r
+}\r
+\r
+int sshcom_encrypted(const Filename *filename, char **comment)\r
+{\r
+    struct sshcom_key *key = load_sshcom_key(filename, NULL);\r
+    int pos, len, answer;\r
+\r
+    *comment = NULL;\r
+    if (!key)\r
+        return 0;\r
+\r
+    /*\r
+     * Check magic number.\r
+     */\r
+    if (GET_32BIT(key->keyblob) != 0x3f6ff9eb)\r
+        return 0;                      /* key is invalid */\r
+\r
+    /*\r
+     * Find the cipher-type string.\r
+     */\r
+    answer = 0;\r
+    pos = 8;\r
+    if (key->keyblob_len < pos+4)\r
+        goto done;                     /* key is far too short */\r
+    pos += 4 + GET_32BIT(key->keyblob + pos);   /* skip key type */\r
+    if (key->keyblob_len < pos+4)\r
+        goto done;                     /* key is far too short */\r
+    len = GET_32BIT(key->keyblob + pos);   /* find cipher-type length */\r
+    if (key->keyblob_len < pos+4+len)\r
+        goto done;                     /* cipher type string is incomplete */\r
+    if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4))\r
+        answer = 1;\r
+\r
+    done:\r
+    *comment = dupstr(key->comment);\r
+    memset(key->keyblob, 0, key->keyblob_size);\r
+    sfree(key->keyblob);\r
+    memset(key, 0, sizeof(*key));\r
+    sfree(key);\r
+    return answer;\r
+}\r
+\r
+static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)\r
+{\r
+    int bits;\r
+    int bytes;\r
+    unsigned char *d = (unsigned char *) data;\r
+\r
+    if (len < 4)\r
+        goto error;\r
+    bits = GET_32BIT(d);\r
+\r
+    bytes = (bits + 7) / 8;\r
+    if (len < 4+bytes)\r
+        goto error;\r
+\r
+    ret->start = d + 4;\r
+    ret->bytes = bytes;\r
+    return bytes+4;\r
+\r
+    error:\r
+    ret->start = NULL;\r
+    ret->bytes = -1;\r
+    return len;                        /* ensure further calls fail as well */\r
+}\r
+\r
+static int sshcom_put_mpint(void *target, void *data, int len)\r
+{\r
+    unsigned char *d = (unsigned char *)target;\r
+    unsigned char *i = (unsigned char *)data;\r
+    int bits = len * 8 - 1;\r
+\r
+    while (bits > 0) {\r
+       if (*i & (1 << (bits & 7)))\r
+           break;\r
+       if (!(bits-- & 7))\r
+           i++, len--;\r
+    }\r
+\r
+    PUT_32BIT(d, bits+1);\r
+    memcpy(d+4, i, len);\r
+    return len+4;\r
+}\r
+\r
+struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,\r
+                                const char **errmsg_p)\r
+{\r
+    struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);\r
+    char *errmsg;\r
+    int pos, len;\r
+    const char prefix_rsa[] = "if-modn{sign{rsa";\r
+    const char prefix_dsa[] = "dl-modp{sign{dsa";\r
+    enum { RSA, DSA } type;\r
+    int encrypted;\r
+    char *ciphertext;\r
+    int cipherlen;\r
+    struct ssh2_userkey *ret = NULL, *retkey;\r
+    const struct ssh_signkey *alg;\r
+    unsigned char *blob = NULL;\r
+    int blobsize = 0, publen, privlen;\r
+\r
+    if (!key)\r
+        return NULL;\r
+\r
+    /*\r
+     * Check magic number.\r
+     */\r
+    if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) {\r
+        errmsg = "key does not begin with magic number";\r
+        goto error;\r
+    }\r
+\r
+    /*\r
+     * Determine the key type.\r
+     */\r
+    pos = 8;\r
+    if (key->keyblob_len < pos+4 ||\r
+        (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {\r
+        errmsg = "key blob does not contain a key type string";\r
+        goto error;\r
+    }\r
+    if (len > sizeof(prefix_rsa) - 1 &&\r
+        !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) {\r
+        type = RSA;\r
+    } else if (len > sizeof(prefix_dsa) - 1 &&\r
+        !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) {\r
+        type = DSA;\r
+    } else {\r
+        errmsg = "key is of unknown type";\r
+        goto error;\r
+    }\r
+    pos += 4+len;\r
+\r
+    /*\r
+     * Determine the cipher type.\r
+     */\r
+    if (key->keyblob_len < pos+4 ||\r
+        (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {\r
+        errmsg = "key blob does not contain a cipher type string";\r
+        goto error;\r
+    }\r
+    if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4))\r
+        encrypted = 0;\r
+    else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8))\r
+        encrypted = 1;\r
+    else {\r
+        errmsg = "key encryption is of unknown type";\r
+        goto error;\r
+    }\r
+    pos += 4+len;\r
+\r
+    /*\r
+     * Get hold of the encrypted part of the key.\r
+     */\r
+    if (key->keyblob_len < pos+4 ||\r
+        (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {\r
+        errmsg = "key blob does not contain actual key data";\r
+        goto error;\r
+    }\r
+    ciphertext = (char *)key->keyblob + pos + 4;\r
+    cipherlen = len;\r
+    if (cipherlen == 0) {\r
+        errmsg = "length of key data is zero";\r
+        goto error;\r
+    }\r
+\r
+    /*\r
+     * Decrypt it if necessary.\r
+     */\r
+    if (encrypted) {\r
+       /*\r
+        * Derive encryption key from passphrase and iv/salt:\r
+        * \r
+        *  - let block A equal MD5(passphrase)\r
+        *  - let block B equal MD5(passphrase || A)\r
+        *  - block C would be MD5(passphrase || A || B) and so on\r
+        *  - encryption key is the first N bytes of A || B\r
+        */\r
+       struct MD5Context md5c;\r
+       unsigned char keybuf[32], iv[8];\r
+\r
+        if (cipherlen % 8 != 0) {\r
+            errmsg = "encrypted part of key is not a multiple of cipher block"\r
+                " size";\r
+            goto error;\r
+        }\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Final(keybuf, &md5c);\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Update(&md5c, keybuf, 16);\r
+       MD5Final(keybuf+16, &md5c);\r
+\r
+       /*\r
+        * Now decrypt the key blob.\r
+        */\r
+        memset(iv, 0, sizeof(iv));\r
+       des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,\r
+                                cipherlen);\r
+\r
+        memset(&md5c, 0, sizeof(md5c));\r
+        memset(keybuf, 0, sizeof(keybuf));\r
+\r
+        /*\r
+         * Hereafter we return WRONG_PASSPHRASE for any parsing\r
+         * error. (But only if we've just tried to decrypt it!\r
+         * Returning WRONG_PASSPHRASE for an unencrypted key is\r
+         * automatic doom.)\r
+         */\r
+        if (encrypted)\r
+            ret = SSH2_WRONG_PASSPHRASE;\r
+    }\r
+\r
+    /*\r
+     * Strip away the containing string to get to the real meat.\r
+     */\r
+    len = GET_32BIT(ciphertext);\r
+    if (len < 0 || len > cipherlen-4) {\r
+        errmsg = "containing string was ill-formed";\r
+        goto error;\r
+    }\r
+    ciphertext += 4;\r
+    cipherlen = len;\r
+\r
+    /*\r
+     * Now we break down into RSA versus DSA. In either case we'll\r
+     * construct public and private blobs in our own format, and\r
+     * end up feeding them to alg->createkey().\r
+     */\r
+    blobsize = cipherlen + 256;\r
+    blob = snewn(blobsize, unsigned char);\r
+    privlen = 0;\r
+    if (type == RSA) {\r
+        struct mpint_pos n, e, d, u, p, q;\r
+        int pos = 0;\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);\r
+        if (!q.start) {\r
+            errmsg = "key data did not contain six integers";\r
+            goto error;\r
+        }\r
+\r
+        alg = &ssh_rsa;\r
+        pos = 0;\r
+        pos += put_string(blob+pos, "ssh-rsa", 7);\r
+        pos += put_mp(blob+pos, e.start, e.bytes);\r
+        pos += put_mp(blob+pos, n.start, n.bytes);\r
+        publen = pos;\r
+        pos += put_string(blob+pos, d.start, d.bytes);\r
+        pos += put_mp(blob+pos, q.start, q.bytes);\r
+        pos += put_mp(blob+pos, p.start, p.bytes);\r
+        pos += put_mp(blob+pos, u.start, u.bytes);\r
+        privlen = pos - publen;\r
+    } else if (type == DSA) {\r
+        struct mpint_pos p, q, g, x, y;\r
+        int pos = 4;\r
+        if (GET_32BIT(ciphertext) != 0) {\r
+            errmsg = "predefined DSA parameters not supported";\r
+            goto error;\r
+        }\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y);\r
+        pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x);\r
+        if (!x.start) {\r
+            errmsg = "key data did not contain five integers";\r
+            goto error;\r
+        }\r
+\r
+        alg = &ssh_dss;\r
+        pos = 0;\r
+        pos += put_string(blob+pos, "ssh-dss", 7);\r
+        pos += put_mp(blob+pos, p.start, p.bytes);\r
+        pos += put_mp(blob+pos, q.start, q.bytes);\r
+        pos += put_mp(blob+pos, g.start, g.bytes);\r
+        pos += put_mp(blob+pos, y.start, y.bytes);\r
+        publen = pos;\r
+        pos += put_mp(blob+pos, x.start, x.bytes);\r
+        privlen = pos - publen;\r
+    } else\r
+       return NULL;\r
+\r
+    assert(privlen > 0);              /* should have bombed by now if not */\r
+\r
+    retkey = snew(struct ssh2_userkey);\r
+    retkey->alg = alg;\r
+    retkey->data = alg->createkey(blob, publen, blob+publen, privlen);\r
+    if (!retkey->data) {\r
+       sfree(retkey);\r
+       errmsg = "unable to create key data structure";\r
+       goto error;\r
+    }\r
+    retkey->comment = dupstr(key->comment);\r
+\r
+    errmsg = NULL; /* no error */\r
+    ret = retkey;\r
+\r
+    error:\r
+    if (blob) {\r
+        memset(blob, 0, blobsize);\r
+        sfree(blob);\r
+    }\r
+    memset(key->keyblob, 0, key->keyblob_size);\r
+    sfree(key->keyblob);\r
+    memset(key, 0, sizeof(*key));\r
+    sfree(key);\r
+    if (errmsg_p) *errmsg_p = errmsg;\r
+    return ret;\r
+}\r
+\r
+int sshcom_write(const Filename *filename, struct ssh2_userkey *key,\r
+                char *passphrase)\r
+{\r
+    unsigned char *pubblob, *privblob;\r
+    int publen, privlen;\r
+    unsigned char *outblob;\r
+    int outlen;\r
+    struct mpint_pos numbers[6];\r
+    int nnumbers, initial_zero, pos, lenpos, i;\r
+    char *type;\r
+    char *ciphertext;\r
+    int cipherlen;\r
+    int ret = 0;\r
+    FILE *fp;\r
+\r
+    /*\r
+     * Fetch the key blobs.\r
+     */\r
+    pubblob = key->alg->public_blob(key->data, &publen);\r
+    privblob = key->alg->private_blob(key->data, &privlen);\r
+    outblob = NULL;\r
+\r
+    /*\r
+     * Find the sequence of integers to be encoded into the OpenSSH\r
+     * key blob, and also decide on the header line.\r
+     */\r
+    if (key->alg == &ssh_rsa) {\r
+        int pos;\r
+        struct mpint_pos n, e, d, p, q, iqmp;\r
+\r
+        pos = 4 + GET_32BIT(pubblob);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);\r
+        pos = 0;\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);\r
+\r
+        assert(e.start && iqmp.start); /* can't go wrong */\r
+\r
+        numbers[0] = e;\r
+        numbers[1] = d;\r
+        numbers[2] = n;\r
+        numbers[3] = iqmp;\r
+        numbers[4] = q;\r
+        numbers[5] = p;\r
+\r
+        nnumbers = 6;\r
+       initial_zero = 0;\r
+       type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";\r
+    } else if (key->alg == &ssh_dss) {\r
+        int pos;\r
+        struct mpint_pos p, q, g, y, x;\r
+\r
+        pos = 4 + GET_32BIT(pubblob);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);\r
+        pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);\r
+        pos = 0;\r
+        pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);\r
+\r
+        assert(y.start && x.start); /* can't go wrong */\r
+\r
+        numbers[0] = p;\r
+        numbers[1] = g;\r
+        numbers[2] = q;\r
+        numbers[3] = y;\r
+        numbers[4] = x;\r
+\r
+        nnumbers = 5;\r
+       initial_zero = 1;\r
+       type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";\r
+    } else {\r
+        assert(0);                     /* zoinks! */\r
+       exit(1); /* XXX: GCC doesn't understand assert() on some systems. */\r
+    }\r
+\r
+    /*\r
+     * Total size of key blob will be somewhere under 512 plus\r
+     * combined length of integers. We'll calculate the more\r
+     * precise size as we construct the blob.\r
+     */\r
+    outlen = 512;\r
+    for (i = 0; i < nnumbers; i++)\r
+       outlen += 4 + numbers[i].bytes;\r
+    outblob = snewn(outlen, unsigned char);\r
+\r
+    /*\r
+     * Create the unencrypted key blob.\r
+     */\r
+    pos = 0;\r
+    PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4;\r
+    pos += 4;                         /* length field, fill in later */\r
+    pos += put_string(outblob+pos, type, strlen(type));\r
+    {\r
+       char *ciphertype = passphrase ? "3des-cbc" : "none";\r
+       pos += put_string(outblob+pos, ciphertype, strlen(ciphertype));\r
+    }\r
+    lenpos = pos;                     /* remember this position */\r
+    pos += 4;                         /* encrypted-blob size */\r
+    pos += 4;                         /* encrypted-payload size */\r
+    if (initial_zero) {\r
+       PUT_32BIT(outblob+pos, 0);\r
+       pos += 4;\r
+    }\r
+    for (i = 0; i < nnumbers; i++)\r
+       pos += sshcom_put_mpint(outblob+pos,\r
+                               numbers[i].start, numbers[i].bytes);\r
+    /* Now wrap up the encrypted payload. */\r
+    PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8));\r
+    /* Pad encrypted blob to a multiple of cipher block size. */\r
+    if (passphrase) {\r
+       int padding = -(pos - (lenpos+4)) & 7;\r
+       while (padding--)\r
+           outblob[pos++] = random_byte();\r
+    }\r
+    ciphertext = (char *)outblob+lenpos+4;\r
+    cipherlen = pos - (lenpos+4);\r
+    assert(!passphrase || cipherlen % 8 == 0);\r
+    /* Wrap up the encrypted blob string. */\r
+    PUT_32BIT(outblob+lenpos, cipherlen);\r
+    /* And finally fill in the total length field. */\r
+    PUT_32BIT(outblob+4, pos);\r
+\r
+    assert(pos < outlen);\r
+\r
+    /*\r
+     * Encrypt the key.\r
+     */\r
+    if (passphrase) {\r
+       /*\r
+        * Derive encryption key from passphrase and iv/salt:\r
+        * \r
+        *  - let block A equal MD5(passphrase)\r
+        *  - let block B equal MD5(passphrase || A)\r
+        *  - block C would be MD5(passphrase || A || B) and so on\r
+        *  - encryption key is the first N bytes of A || B\r
+        */\r
+       struct MD5Context md5c;\r
+       unsigned char keybuf[32], iv[8];\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Final(keybuf, &md5c);\r
+\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Update(&md5c, keybuf, 16);\r
+       MD5Final(keybuf+16, &md5c);\r
+\r
+       /*\r
+        * Now decrypt the key blob.\r
+        */\r
+        memset(iv, 0, sizeof(iv));\r
+       des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,\r
+                                cipherlen);\r
+\r
+        memset(&md5c, 0, sizeof(md5c));\r
+        memset(keybuf, 0, sizeof(keybuf));\r
+    }\r
+\r
+    /*\r
+     * And save it. We'll use Unix line endings just in case it's\r
+     * subsequently transferred in binary mode.\r
+     */\r
+    fp = f_open(*filename, "wb", TRUE);      /* ensure Unix line endings */\r
+    if (!fp)\r
+       goto error;\r
+    fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);\r
+    fprintf(fp, "Comment: \"");\r
+    /*\r
+     * Comment header is broken with backslash-newline if it goes\r
+     * over 70 chars. Although it's surrounded by quotes, it\r
+     * _doesn't_ escape backslashes or quotes within the string.\r
+     * Don't ask me, I didn't design it.\r
+     */\r
+    {\r
+       int slen = 60;                 /* starts at 60 due to "Comment: " */\r
+       char *c = key->comment;\r
+       while ((int)strlen(c) > slen) {\r
+           fprintf(fp, "%.*s\\\n", slen, c);\r
+           c += slen;\r
+           slen = 70;                 /* allow 70 chars on subsequent lines */\r
+       }\r
+       fprintf(fp, "%s\"\n", c);\r
+    }\r
+    base64_encode(fp, outblob, pos, 70);\r
+    fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);\r
+    fclose(fp);\r
+    ret = 1;\r
+\r
+    error:\r
+    if (outblob) {\r
+        memset(outblob, 0, outlen);\r
+        sfree(outblob);\r
+    }\r
+    if (privblob) {\r
+        memset(privblob, 0, privlen);\r
+        sfree(privblob);\r
+    }\r
+    if (pubblob) {\r
+        memset(pubblob, 0, publen);\r
+        sfree(pubblob);\r
+    }\r
+    return ret;\r
+}\r
diff --git a/putty/INT64.C b/putty/INT64.C
new file mode 100644 (file)
index 0000000..6f24bce
--- /dev/null
@@ -0,0 +1,175 @@
+/*\r
+ * Handling of the int64 and uint64 types. Done in 32-bit integers,\r
+ * for (pre-C99) portability. Hopefully once C99 becomes widespread\r
+ * we can kiss this lot goodbye...\r
+ */\r
+\r
+#include <assert.h>\r
+#include <string.h>\r
+\r
+#include "int64.h"\r
+\r
+uint64 uint64_div10(uint64 x, int *remainder)\r
+{\r
+    uint64 y;\r
+    unsigned int rem, r2;\r
+    y.hi = x.hi / 10;\r
+    y.lo = x.lo / 10;\r
+    rem = x.lo % 10;\r
+    /*\r
+     * Now we have to add in the remainder left over from x.hi.\r
+     */\r
+    r2 = x.hi % 10;\r
+    y.lo += r2 * 429496729;\r
+    rem += r2 * 6;\r
+    y.lo += rem / 10;\r
+    rem %= 10;\r
+\r
+    if (remainder)\r
+       *remainder = rem;\r
+    return y;\r
+}\r
+\r
+void uint64_decimal(uint64 x, char *buffer)\r
+{\r
+    char buf[20];\r
+    int start = 20;\r
+    int d;\r
+\r
+    do {\r
+       x = uint64_div10(x, &d);\r
+       assert(start > 0);\r
+       buf[--start] = d + '0';\r
+    } while (x.hi || x.lo);\r
+\r
+    memcpy(buffer, buf + start, sizeof(buf) - start);\r
+    buffer[sizeof(buf) - start] = '\0';\r
+}\r
+\r
+uint64 uint64_make(unsigned long hi, unsigned long lo)\r
+{\r
+    uint64 y;\r
+    y.hi = hi & 0xFFFFFFFFU;\r
+    y.lo = lo & 0xFFFFFFFFU;\r
+    return y;\r
+}\r
+\r
+uint64 uint64_add(uint64 x, uint64 y)\r
+{\r
+    x.lo = (x.lo + y.lo) & 0xFFFFFFFFU;\r
+    x.hi += y.hi + (x.lo < y.lo ? 1 : 0);\r
+    return x;\r
+}\r
+\r
+uint64 uint64_add32(uint64 x, unsigned long y)\r
+{\r
+    uint64 yy;\r
+    yy.hi = 0;\r
+    yy.lo = y;\r
+    return uint64_add(x, yy);\r
+}\r
+\r
+int uint64_compare(uint64 x, uint64 y)\r
+{\r
+    if (x.hi != y.hi)\r
+       return x.hi < y.hi ? -1 : +1;\r
+    if (x.lo != y.lo)\r
+       return x.lo < y.lo ? -1 : +1;\r
+    return 0;\r
+}\r
+\r
+uint64 uint64_subtract(uint64 x, uint64 y)\r
+{\r
+    x.lo = (x.lo - y.lo) & 0xFFFFFFFFU;\r
+    x.hi = (x.hi - y.hi - (x.lo > (y.lo ^ 0xFFFFFFFFU) ? 1 : 0)) & 0xFFFFFFFFU;\r
+    return x;\r
+}\r
+\r
+double uint64_to_double(uint64 x)\r
+{\r
+    return (4294967296.0 * x.hi) + (double)x.lo;\r
+}\r
+\r
+uint64 uint64_shift_right(uint64 x, int shift)\r
+{\r
+    if (shift < 32) {\r
+       x.lo >>= shift;\r
+       x.lo |= (x.hi << (32-shift)) & 0xFFFFFFFFU;\r
+       x.hi >>= shift;\r
+    } else {\r
+       x.lo = x.hi >> (shift-32);\r
+       x.hi = 0;\r
+    }\r
+    return x;\r
+}\r
+\r
+uint64 uint64_shift_left(uint64 x, int shift)\r
+{\r
+    if (shift < 32) {\r
+       x.hi = (x.hi << shift) & 0xFFFFFFFFU;\r
+       x.hi |= (x.lo >> (32-shift));\r
+       x.lo = (x.lo << shift) & 0xFFFFFFFFU;\r
+    } else {\r
+       x.hi = (x.lo << (shift-32)) & 0xFFFFFFFFU;\r
+       x.lo = 0;\r
+    }\r
+    return x;\r
+}\r
+\r
+uint64 uint64_from_decimal(char *str)\r
+{\r
+    uint64 ret;\r
+    ret.hi = ret.lo = 0;\r
+    while (*str >= '0' && *str <= '9') {\r
+       ret = uint64_add(uint64_shift_left(ret, 3),\r
+                        uint64_shift_left(ret, 1));\r
+       ret = uint64_add32(ret, *str - '0');\r
+       str++;\r
+    }\r
+    return ret;\r
+}\r
+\r
+#ifdef TESTMODE\r
+\r
+#include <stdio.h>\r
+\r
+int main(void)\r
+{\r
+    uint64 x, y, z;\r
+    char buf[80];\r
+\r
+    x = uint64_make(0x3456789AUL, 0xDEF01234UL);\r
+    printf("%08lx.%08lx\n", x.hi, x.lo);\r
+    uint64_decimal(x, buf);\r
+    printf("%s\n", buf);\r
+\r
+    y = uint64_add32(x, 0xFFFFFFFFU);\r
+    printf("%08lx.%08lx\n", y.hi, y.lo);\r
+    uint64_decimal(y, buf);\r
+    printf("%s\n", buf);\r
+\r
+    z = uint64_subtract(y, x);\r
+    printf("%08lx.%08lx\n", z.hi, z.lo);\r
+    uint64_decimal(z, buf);\r
+    printf("%s\n", buf);\r
+\r
+    z = uint64_subtract(x, y);\r
+    printf("%08lx.%08lx\n", z.hi, z.lo);\r
+    uint64_decimal(z, buf);\r
+    printf("%s\n", buf);\r
+\r
+    y = uint64_shift_right(x, 4);\r
+    printf("%08lx.%08lx\n", y.hi, y.lo);\r
+\r
+    y = uint64_shift_right(x, 36);\r
+    printf("%08lx.%08lx\n", y.hi, y.lo);\r
+\r
+    y = uint64_shift_left(x, 4);\r
+    printf("%08lx.%08lx\n", x.hi, x.lo);\r
+\r
+    y = uint64_shift_left(x, 36);\r
+    printf("%08lx.%08lx\n", x.hi, x.lo);\r
+\r
+    return 0;\r
+}\r
+#endif\r
diff --git a/putty/INT64.H b/putty/INT64.H
new file mode 100644 (file)
index 0000000..63df3a9
--- /dev/null
@@ -0,0 +1,24 @@
+/*\r
+ * Header for int64.c.\r
+ */\r
+\r
+#ifndef PUTTY_INT64_H\r
+#define PUTTY_INT64_H\r
+\r
+typedef struct {\r
+    unsigned long hi, lo;\r
+} uint64;\r
+\r
+uint64 uint64_div10(uint64 x, int *remainder);\r
+void uint64_decimal(uint64 x, char *buffer);\r
+uint64 uint64_make(unsigned long hi, unsigned long lo);\r
+uint64 uint64_add(uint64 x, uint64 y);\r
+uint64 uint64_add32(uint64 x, unsigned long y);\r
+int uint64_compare(uint64 x, uint64 y);\r
+uint64 uint64_subtract(uint64 x, uint64 y);\r
+double uint64_to_double(uint64 x);\r
+uint64 uint64_shift_right(uint64 x, int shift);\r
+uint64 uint64_shift_left(uint64 x, int shift);\r
+uint64 uint64_from_decimal(char *str);\r
+\r
+#endif\r
diff --git a/putty/LDISC.C b/putty/LDISC.C
new file mode 100644 (file)
index 0000000..119a02a
--- /dev/null
@@ -0,0 +1,336 @@
+/*\r
+ * ldisc.c: PuTTY line discipline. Sits between the input coming\r
+ * from keypresses in the window, and the output channel leading to\r
+ * the back end. Implements echo and/or local line editing,\r
+ * depending on what's currently configured.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+#include "terminal.h"\r
+#include "ldisc.h"\r
+\r
+#define ECHOING (ldisc->cfg->localecho == FORCE_ON || \\r
+                 (ldisc->cfg->localecho == AUTO && \\r
+                      (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \\r
+                          term_ldisc(ldisc->term, LD_ECHO))))\r
+#define EDITING (ldisc->cfg->localedit == FORCE_ON || \\r
+                 (ldisc->cfg->localedit == AUTO && \\r
+                      (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \\r
+                          term_ldisc(ldisc->term, LD_EDIT))))\r
+\r
+static void c_write(Ldisc ldisc, char *buf, int len)\r
+{\r
+    from_backend(ldisc->frontend, 0, buf, len);\r
+}\r
+\r
+static int plen(Ldisc ldisc, unsigned char c)\r
+{\r
+    if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term)))\r
+       return 1;\r
+    else if (c < 128)\r
+       return 2;                      /* ^x for some x */\r
+    else if (in_utf(ldisc->term) && c >= 0xC0)\r
+       return 1;                      /* UTF-8 introducer character\r
+                                       * (FIXME: combining / wide chars) */\r
+    else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0)\r
+       return 0;                      /* UTF-8 followup character */\r
+    else\r
+       return 4;                      /* <XY> hex representation */\r
+}\r
+\r
+static void pwrite(Ldisc ldisc, unsigned char c)\r
+{\r
+    if ((c >= 32 && c <= 126) ||\r
+       (!in_utf(ldisc->term) && c >= 0xA0) ||\r
+       (in_utf(ldisc->term) && c >= 0x80)) {\r
+       c_write(ldisc, (char *)&c, 1);\r
+    } else if (c < 128) {\r
+       char cc[2];\r
+       cc[1] = (c == 127 ? '?' : c + 0x40);\r
+       cc[0] = '^';\r
+       c_write(ldisc, cc, 2);\r
+    } else {\r
+       char cc[5];\r
+       sprintf(cc, "<%02X>", c);\r
+       c_write(ldisc, cc, 4);\r
+    }\r
+}\r
+\r
+static int char_start(Ldisc ldisc, unsigned char c)\r
+{\r
+    if (in_utf(ldisc->term))\r
+       return (c < 0x80 || c >= 0xC0);\r
+    else\r
+       return 1;\r
+}\r
+\r
+static void bsb(Ldisc ldisc, int n)\r
+{\r
+    while (n--)\r
+       c_write(ldisc, "\010 \010", 3);\r
+}\r
+\r
+#define CTRL(x) (x^'@')\r
+#define KCTRL(x) ((x^'@') | 0x100)\r
+\r
+void *ldisc_create(Config *mycfg, Terminal *term,\r
+                  Backend *back, void *backhandle,\r
+                  void *frontend)\r
+{\r
+    Ldisc ldisc = snew(struct ldisc_tag);\r
+\r
+    ldisc->buf = NULL;\r
+    ldisc->buflen = 0;\r
+    ldisc->bufsiz = 0;\r
+    ldisc->quotenext = 0;\r
+\r
+    ldisc->cfg = mycfg;\r
+    ldisc->back = back;\r
+    ldisc->backhandle = backhandle;\r
+    ldisc->term = term;\r
+    ldisc->frontend = frontend;\r
+\r
+    /* Link ourselves into the backend and the terminal */\r
+    if (term)\r
+       term->ldisc = ldisc;\r
+    if (back)\r
+       back->provide_ldisc(backhandle, ldisc);\r
+\r
+    return ldisc;\r
+}\r
+\r
+void ldisc_free(void *handle)\r
+{\r
+    Ldisc ldisc = (Ldisc) handle;\r
+\r
+    if (ldisc->term)\r
+       ldisc->term->ldisc = NULL;\r
+    if (ldisc->back)\r
+       ldisc->back->provide_ldisc(ldisc->backhandle, NULL);\r
+    if (ldisc->buf)\r
+       sfree(ldisc->buf);\r
+    sfree(ldisc);\r
+}\r
+\r
+void ldisc_send(void *handle, char *buf, int len, int interactive)\r
+{\r
+    Ldisc ldisc = (Ldisc) handle;\r
+    int keyflag = 0;\r
+    /*\r
+     * Called with len=0 when the options change. We must inform\r
+     * the front end in case it needs to know.\r
+     */\r
+    if (len == 0) {\r
+       ldisc_update(ldisc->frontend, ECHOING, EDITING);\r
+       return;\r
+    }\r
+    /*\r
+     * Notify the front end that something was pressed, in case\r
+     * it's depending on finding out (e.g. keypress termination for\r
+     * Close On Exit). \r
+     */\r
+    frontend_keypress(ldisc->frontend);\r
+\r
+    /*\r
+     * Less than zero means null terminated special string.\r
+     */\r
+    if (len < 0) {\r
+       len = strlen(buf);\r
+       keyflag = KCTRL('@');\r
+    }\r
+    /*\r
+     * Either perform local editing, or just send characters.\r
+     */\r
+    if (EDITING) {\r
+       while (len--) {\r
+           int c;\r
+           c = (unsigned char)(*buf++) + keyflag;\r
+           if (!interactive && c == '\r')\r
+               c += KCTRL('@');\r
+           switch (ldisc->quotenext ? ' ' : c) {\r
+               /*\r
+                * ^h/^?: delete, and output BSBs, to return to\r
+                * last character boundary (in UTF-8 mode this may\r
+                * be more than one byte)\r
+                * ^w: delete, and output BSBs, to return to last\r
+                * space/nonspace boundary\r
+                * ^u: delete, and output BSBs, to return to BOL\r
+                * ^c: Do a ^u then send a telnet IP\r
+                * ^z: Do a ^u then send a telnet SUSP\r
+                * ^\: Do a ^u then send a telnet ABORT\r
+                * ^r: echo "^R\n" and redraw line\r
+                * ^v: quote next char\r
+                * ^d: if at BOL, end of file and close connection,\r
+                * else send line and reset to BOL\r
+                * ^m: send line-plus-\r\n and reset to BOL\r
+                */\r
+             case KCTRL('H'):\r
+             case KCTRL('?'):         /* backspace/delete */\r
+               if (ldisc->buflen > 0) {\r
+                   do {\r
+                       if (ECHOING)\r
+                           bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                       ldisc->buflen--;\r
+                   } while (!char_start(ldisc, ldisc->buf[ldisc->buflen]));\r
+               }\r
+               break;\r
+             case CTRL('W'):          /* delete word */\r
+               while (ldisc->buflen > 0) {\r
+                   if (ECHOING)\r
+                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                   ldisc->buflen--;\r
+                   if (ldisc->buflen > 0 &&\r
+                       isspace((unsigned char)ldisc->buf[ldisc->buflen-1]) &&\r
+                       !isspace((unsigned char)ldisc->buf[ldisc->buflen]))\r
+                       break;\r
+               }\r
+               break;\r
+             case CTRL('U'):          /* delete line */\r
+             case CTRL('C'):          /* Send IP */\r
+             case CTRL('\\'):         /* Quit */\r
+             case CTRL('Z'):          /* Suspend */\r
+               while (ldisc->buflen > 0) {\r
+                   if (ECHOING)\r
+                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                   ldisc->buflen--;\r
+               }\r
+               ldisc->back->special(ldisc->backhandle, TS_EL);\r
+                /*\r
+                 * We don't send IP, SUSP or ABORT if the user has\r
+                 * configured telnet specials off! This breaks\r
+                 * talkers otherwise.\r
+                 */\r
+                if (!ldisc->cfg->telnet_keyboard)\r
+                    goto default_case;\r
+               if (c == CTRL('C'))\r
+                   ldisc->back->special(ldisc->backhandle, TS_IP);\r
+               if (c == CTRL('Z'))\r
+                   ldisc->back->special(ldisc->backhandle, TS_SUSP);\r
+               if (c == CTRL('\\'))\r
+                   ldisc->back->special(ldisc->backhandle, TS_ABORT);\r
+               break;\r
+             case CTRL('R'):          /* redraw line */\r
+               if (ECHOING) {\r
+                   int i;\r
+                   c_write(ldisc, "^R\r\n", 4);\r
+                   for (i = 0; i < ldisc->buflen; i++)\r
+                       pwrite(ldisc, ldisc->buf[i]);\r
+               }\r
+               break;\r
+             case CTRL('V'):          /* quote next char */\r
+               ldisc->quotenext = TRUE;\r
+               break;\r
+             case CTRL('D'):          /* logout or send */\r
+               if (ldisc->buflen == 0) {\r
+                   ldisc->back->special(ldisc->backhandle, TS_EOF);\r
+               } else {\r
+                   ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);\r
+                   ldisc->buflen = 0;\r
+               }\r
+               break;\r
+               /*\r
+                * This particularly hideous bit of code from RDB\r
+                * allows ordinary ^M^J to do the same thing as\r
+                * magic-^M when in Raw protocol. The line `case\r
+                * KCTRL('M'):' is _inside_ the if block. Thus:\r
+                * \r
+                *  - receiving regular ^M goes straight to the\r
+                *    default clause and inserts as a literal ^M.\r
+                *  - receiving regular ^J _not_ directly after a\r
+                *    literal ^M (or not in Raw protocol) fails the\r
+                *    if condition, leaps to the bottom of the if,\r
+                *    and falls through into the default clause\r
+                *    again.\r
+                *  - receiving regular ^J just after a literal ^M\r
+                *    in Raw protocol passes the if condition,\r
+                *    deletes the literal ^M, and falls through\r
+                *    into the magic-^M code\r
+                *  - receiving a magic-^M empties the line buffer,\r
+                *    signals end-of-line in one of the various\r
+                *    entertaining ways, and _doesn't_ fall out of\r
+                *    the bottom of the if and through to the\r
+                *    default clause because of the break.\r
+                */\r
+             case CTRL('J'):\r
+               if (ldisc->cfg->protocol == PROT_RAW &&\r
+                   ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {\r
+                   if (ECHOING)\r
+                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                   ldisc->buflen--;\r
+                   /* FALLTHROUGH */\r
+             case KCTRL('M'):         /* send with newline */\r
+                   if (ldisc->buflen > 0)\r
+                       ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);\r
+                   if (ldisc->cfg->protocol == PROT_RAW)\r
+                       ldisc->back->send(ldisc->backhandle, "\r\n", 2);\r
+                   else if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)\r
+                       ldisc->back->special(ldisc->backhandle, TS_EOL);\r
+                   else\r
+                       ldisc->back->send(ldisc->backhandle, "\r", 1);\r
+                   if (ECHOING)\r
+                       c_write(ldisc, "\r\n", 2);\r
+                   ldisc->buflen = 0;\r
+                   break;\r
+               }\r
+               /* FALLTHROUGH */\r
+             default:                 /* get to this label from ^V handler */\r
+                default_case:\r
+               if (ldisc->buflen >= ldisc->bufsiz) {\r
+                   ldisc->bufsiz = ldisc->buflen + 256;\r
+                   ldisc->buf = sresize(ldisc->buf, ldisc->bufsiz, char);\r
+               }\r
+               ldisc->buf[ldisc->buflen++] = c;\r
+               if (ECHOING)\r
+                   pwrite(ldisc, (unsigned char) c);\r
+               ldisc->quotenext = FALSE;\r
+               break;\r
+           }\r
+       }\r
+    } else {\r
+       if (ldisc->buflen != 0) {\r
+           ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);\r
+           while (ldisc->buflen > 0) {\r
+               bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+               ldisc->buflen--;\r
+           }\r
+       }\r
+       if (len > 0) {\r
+           if (ECHOING)\r
+               c_write(ldisc, buf, len);\r
+           if (keyflag && ldisc->cfg->protocol == PROT_TELNET && len == 1) {\r
+               switch (buf[0]) {\r
+                 case CTRL('M'):\r
+                   if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)\r
+                       ldisc->back->special(ldisc->backhandle, TS_EOL);\r
+                   else\r
+                       ldisc->back->send(ldisc->backhandle, "\r", 1);\r
+                   break;\r
+                 case CTRL('?'):\r
+                 case CTRL('H'):\r
+                   if (ldisc->cfg->telnet_keyboard) {\r
+                       ldisc->back->special(ldisc->backhandle, TS_EC);\r
+                       break;\r
+                   }\r
+                 case CTRL('C'):\r
+                   if (ldisc->cfg->telnet_keyboard) {\r
+                       ldisc->back->special(ldisc->backhandle, TS_IP);\r
+                       break;\r
+                   }\r
+                 case CTRL('Z'):\r
+                   if (ldisc->cfg->telnet_keyboard) {\r
+                       ldisc->back->special(ldisc->backhandle, TS_SUSP);\r
+                       break;\r
+                   }\r
+\r
+                 default:\r
+                   ldisc->back->send(ldisc->backhandle, buf, len);\r
+                   break;\r
+               }\r
+           } else\r
+               ldisc->back->send(ldisc->backhandle, buf, len);\r
+       }\r
+    }\r
+}\r
diff --git a/putty/LDISC.H b/putty/LDISC.H
new file mode 100644 (file)
index 0000000..ef84f6d
--- /dev/null
@@ -0,0 +1,22 @@
+/*\r
+ * ldisc.h: defines the Ldisc data structure used by ldisc.c and\r
+ * ldiscucs.c. (Unfortunately it was necessary to split the ldisc\r
+ * module in two, to avoid unnecessarily linking in the Unicode\r
+ * stuff in tools that don't require it.)\r
+ */\r
+\r
+#ifndef PUTTY_LDISC_H\r
+#define PUTTY_LDISC_H\r
+\r
+typedef struct ldisc_tag {\r
+    Terminal *term;\r
+    Backend *back;\r
+    Config *cfg;\r
+    void *backhandle;\r
+    void *frontend;\r
+\r
+    char *buf;\r
+    int buflen, bufsiz, quotenext;\r
+} *Ldisc;\r
+\r
+#endif /* PUTTY_LDISC_H */\r
diff --git a/putty/LDISCUCS.C b/putty/LDISCUCS.C
new file mode 100644 (file)
index 0000000..eda383b
--- /dev/null
@@ -0,0 +1,100 @@
+/*\r
+ * ldisc.c: PuTTY line discipline. Sits between the input coming\r
+ * from keypresses in the window, and the output channel leading to\r
+ * the back end. Implements echo and/or local line editing,\r
+ * depending on what's currently configured.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+#include "terminal.h"\r
+#include "ldisc.h"\r
+\r
+void lpage_send(void *handle,\r
+               int codepage, char *buf, int len, int interactive)\r
+{\r
+    Ldisc ldisc = (Ldisc)handle;\r
+    wchar_t *widebuffer = 0;\r
+    int widesize = 0;\r
+    int wclen;\r
+\r
+    if (codepage < 0) {\r
+       ldisc_send(ldisc, buf, len, interactive);\r
+       return;\r
+    }\r
+\r
+    widesize = len * 2;\r
+    widebuffer = snewn(widesize, wchar_t);\r
+\r
+    wclen = mb_to_wc(codepage, 0, buf, len, widebuffer, widesize);\r
+    luni_send(ldisc, widebuffer, wclen, interactive);\r
+\r
+    sfree(widebuffer);\r
+}\r
+\r
+void luni_send(void *handle, wchar_t * widebuf, int len, int interactive)\r
+{\r
+    Ldisc ldisc = (Ldisc)handle;\r
+    int ratio = (in_utf(ldisc->term))?3:1;\r
+    char *linebuffer;\r
+    int linesize;\r
+    int i;\r
+    char *p;\r
+\r
+    linesize = len * ratio * 2;\r
+    linebuffer = snewn(linesize, char);\r
+\r
+    if (in_utf(ldisc->term)) {\r
+       /* UTF is a simple algorithm */\r
+       for (p = linebuffer, i = 0; i < len; i++) {\r
+           unsigned long ch = widebuf[i];\r
+\r
+           if ((ch & 0xF800) == 0xD800) {\r
+#ifdef PLATFORM_IS_UTF16\r
+               if (i+1 < len) {\r
+                   unsigned long ch2 = widebuf[i+1];\r
+                   if ((ch & 0xFC00) == 0xD800 &&\r
+                       (ch2 & 0xFC00) == 0xDC00) {\r
+                       ch = 0x10000 + ((ch & 0x3FF) << 10) + (ch2 & 0x3FF);\r
+                       i++;\r
+                   }\r
+               } else\r
+#endif\r
+               {\r
+                   /* Unrecognised UTF-16 sequence */\r
+                   ch = '.';\r
+               }\r
+           }\r
+\r
+           if (ch < 0x80) {\r
+               *p++ = (char) (ch);\r
+           } else if (ch < 0x800) {\r
+               *p++ = (char) (0xC0 | (ch >> 6));\r
+               *p++ = (char) (0x80 | (ch & 0x3F));\r
+           } else if (ch < 0x10000) {\r
+               *p++ = (char) (0xE0 | (ch >> 12));\r
+               *p++ = (char) (0x80 | ((ch >> 6) & 0x3F));\r
+               *p++ = (char) (0x80 | (ch & 0x3F));\r
+           } else {\r
+               *p++ = (char) (0xF0 | (ch >> 18));\r
+               *p++ = (char) (0x80 | ((ch >> 12) & 0x3F));\r
+               *p++ = (char) (0x80 | ((ch >> 6) & 0x3F));\r
+               *p++ = (char) (0x80 | (ch & 0x3F));\r
+           }\r
+       }\r
+    } else {\r
+       int rv;\r
+       rv = wc_to_mb(ldisc->term->ucsdata->line_codepage, 0, widebuf, len,\r
+                     linebuffer, linesize, NULL, NULL, ldisc->term->ucsdata);\r
+       if (rv >= 0)\r
+           p = linebuffer + rv;\r
+       else\r
+           p = linebuffer;\r
+    }\r
+    if (p > linebuffer)\r
+       ldisc_send(ldisc, linebuffer, p - linebuffer, interactive);\r
+\r
+    sfree(linebuffer);\r
+}\r
diff --git a/putty/LICENCE b/putty/LICENCE
new file mode 100644 (file)
index 0000000..d404fee
--- /dev/null
@@ -0,0 +1,25 @@
+PuTTY is copyright 1997-2011 Simon Tatham.\r
+\r
+Portions copyright Robert de Bath, Joris van Rantwijk, Delian\r
+Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,\r
+Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus\r
+Kuhn, Colin Watson, and CORE SDI S.A.\r
+\r
+Permission is hereby granted, free of charge, to any person\r
+obtaining a copy of this software and associated documentation files\r
+(the "Software"), to deal in the Software without restriction,\r
+including without limitation the rights to use, copy, modify, merge,\r
+publish, distribute, sublicense, and/or sell copies of the Software,\r
+and to permit persons to whom the Software is furnished to do so,\r
+subject to the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be\r
+included in all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+NONINFRINGEMENT.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE\r
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
diff --git a/putty/LOGGING.C b/putty/LOGGING.C
new file mode 100644 (file)
index 0000000..8fc5819
--- /dev/null
@@ -0,0 +1,430 @@
+/*\r
+ * Session logging.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+#include <time.h>\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+\r
+/* log session to file stuff ... */\r
+struct LogContext {\r
+    FILE *lgfp;\r
+    enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state;\r
+    bufchain queue;\r
+    Filename currlogfilename;\r
+    void *frontend;\r
+    Config cfg;\r
+};\r
+\r
+static void xlatlognam(Filename *d, Filename s, char *hostname, struct tm *tm);\r
+\r
+/*\r
+ * Internal wrapper function which must be called for _all_ output\r
+ * to the log file. It takes care of opening the log file if it\r
+ * isn't open, buffering data if it's in the process of being\r
+ * opened asynchronously, etc.\r
+ */\r
+static void logwrite(struct LogContext *ctx, void *data, int len)\r
+{\r
+    /*\r
+     * In state L_CLOSED, we call logfopen, which will set the state\r
+     * to one of L_OPENING, L_OPEN or L_ERROR. Hence we process all of\r
+     * those three _after_ processing L_CLOSED.\r
+     */\r
+    if (ctx->state == L_CLOSED)\r
+       logfopen(ctx);\r
+\r
+    if (ctx->state == L_OPENING) {\r
+       bufchain_add(&ctx->queue, data, len);\r
+    } else if (ctx->state == L_OPEN) {\r
+       assert(ctx->lgfp);\r
+       if (fwrite(data, 1, len, ctx->lgfp) < (size_t)len) {\r
+           logfclose(ctx);\r
+           ctx->state = L_ERROR;\r
+           /* Log state is L_ERROR so this won't cause a loop */\r
+           logevent(ctx->frontend,\r
+                    "Disabled writing session log due to error while writing");\r
+       }\r
+    }                                 /* else L_ERROR, so ignore the write */\r
+}\r
+\r
+/*\r
+ * Convenience wrapper on logwrite() which printf-formats the\r
+ * string.\r
+ */\r
+static void logprintf(struct LogContext *ctx, const char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *data;\r
+\r
+    va_start(ap, fmt);\r
+    data = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+\r
+    logwrite(ctx, data, strlen(data));\r
+    sfree(data);\r
+}\r
+\r
+/*\r
+ * Flush any open log file.\r
+ */\r
+void logflush(void *handle) {\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if (ctx->cfg.logtype > 0)\r
+       if (ctx->state == L_OPEN)\r
+           fflush(ctx->lgfp);\r
+}\r
+\r
+static void logfopen_callback(void *handle, int mode)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    char buf[256], *event;\r
+    struct tm tm;\r
+    const char *fmode;\r
+\r
+    if (mode == 0) {\r
+       ctx->state = L_ERROR;          /* disable logging */\r
+    } else {\r
+       fmode = (mode == 1 ? "ab" : "wb");\r
+       ctx->lgfp = f_open(ctx->currlogfilename, fmode, FALSE);\r
+       if (ctx->lgfp)\r
+           ctx->state = L_OPEN;\r
+       else\r
+           ctx->state = L_ERROR;\r
+    }\r
+\r
+    if (ctx->state == L_OPEN) {\r
+       /* Write header line into log file. */\r
+       tm = ltime();\r
+       strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);\r
+       logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s"\r
+                 " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf);\r
+    }\r
+\r
+    event = dupprintf("%s session log (%s mode) to file: %s",\r
+                     ctx->state == L_ERROR ?\r
+                     (mode == 0 ? "Disabled writing" : "Error writing") :\r
+                     (mode == 1 ? "Appending" : "Writing new"),\r
+                     (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :\r
+                      ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :\r
+                      ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" :\r
+                      ctx->cfg.logtype == LGTYP_SSHRAW ? "SSH raw data" :\r
+                      "unknown"),\r
+                     filename_to_str(&ctx->currlogfilename));\r
+    logevent(ctx->frontend, event);\r
+    sfree(event);\r
+\r
+    /*\r
+     * Having either succeeded or failed in opening the log file,\r
+     * we should write any queued data out.\r
+     */\r
+    assert(ctx->state != L_OPENING);   /* make _sure_ it won't be requeued */\r
+    while (bufchain_size(&ctx->queue)) {\r
+       void *data;\r
+       int len;\r
+       bufchain_prefix(&ctx->queue, &data, &len);\r
+       logwrite(ctx, data, len);\r
+       bufchain_consume(&ctx->queue, len);\r
+    }\r
+}\r
+\r
+/*\r
+ * Open the log file. Takes care of detecting an already-existing\r
+ * file and asking the user whether they want to append, overwrite\r
+ * or cancel logging.\r
+ */\r
+void logfopen(void *handle)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    struct tm tm;\r
+    int mode;\r
+\r
+    /* Prevent repeat calls */\r
+    if (ctx->state != L_CLOSED)\r
+       return;\r
+\r
+    if (!ctx->cfg.logtype)\r
+       return;\r
+\r
+    tm = ltime();\r
+\r
+    /* substitute special codes in file name */\r
+    xlatlognam(&ctx->currlogfilename, ctx->cfg.logfilename,ctx->cfg.host, &tm);\r
+\r
+    ctx->lgfp = f_open(ctx->currlogfilename, "r", FALSE);  /* file already present? */\r
+    if (ctx->lgfp) {\r
+       fclose(ctx->lgfp);\r
+       if (ctx->cfg.logxfovr != LGXF_ASK) {\r
+           mode = ((ctx->cfg.logxfovr == LGXF_OVR) ? 2 : 1);\r
+       } else\r
+           mode = askappend(ctx->frontend, ctx->currlogfilename,\r
+                            logfopen_callback, ctx);\r
+    } else\r
+       mode = 2;                      /* create == overwrite */\r
+\r
+    if (mode < 0)\r
+       ctx->state = L_OPENING;\r
+    else\r
+       logfopen_callback(ctx, mode);  /* open the file */\r
+}\r
+\r
+void logfclose(void *handle)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if (ctx->lgfp) {\r
+       fclose(ctx->lgfp);\r
+       ctx->lgfp = NULL;\r
+    }\r
+    ctx->state = L_CLOSED;\r
+}\r
+\r
+/*\r
+ * Log session traffic.\r
+ */\r
+void logtraffic(void *handle, unsigned char c, int logmode)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if (ctx->cfg.logtype > 0) {\r
+       if (ctx->cfg.logtype == logmode)\r
+           logwrite(ctx, &c, 1);\r
+    }\r
+}\r
+\r
+/*\r
+ * Log an Event Log entry. Used in SSH packet logging mode; this is\r
+ * also as convenient a place as any to put the output of Event Log\r
+ * entries to stderr when a command-line tool is in verbose mode.\r
+ * (In particular, this is a better place to put it than in the\r
+ * front ends, because it only has to be done once for all\r
+ * platforms. Platforms which don't have a meaningful stderr can\r
+ * just avoid defining FLAG_STDERR.\r
+ */\r
+void log_eventlog(void *handle, const char *event)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) {\r
+       fprintf(stderr, "%s\n", event);\r
+       fflush(stderr);\r
+    }\r
+    /* If we don't have a context yet (eg winnet.c init) then skip entirely */\r
+    if (!ctx)\r
+       return;\r
+    if (ctx->cfg.logtype != LGTYP_PACKETS &&\r
+       ctx->cfg.logtype != LGTYP_SSHRAW)\r
+       return;\r
+    logprintf(ctx, "Event Log: %s\r\n", event);\r
+    logflush(ctx);\r
+}\r
+\r
+/*\r
+ * Log an SSH packet.\r
+ * If n_blanks != 0, blank or omit some parts.\r
+ * Set of blanking areas must be in increasing order.\r
+ */\r
+void log_packet(void *handle, int direction, int type,\r
+               char *texttype, const void *data, int len,\r
+               int n_blanks, const struct logblank_t *blanks,\r
+               const unsigned long *seq)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    char dumpdata[80], smalldata[5];\r
+    int p = 0, b = 0, omitted = 0;\r
+    int output_pos = 0; /* NZ if pending output in dumpdata */\r
+\r
+    if (!(ctx->cfg.logtype == LGTYP_SSHRAW ||\r
+          (ctx->cfg.logtype == LGTYP_PACKETS && texttype)))\r
+       return;\r
+\r
+    /* Packet header. */\r
+    if (texttype) {\r
+       if (seq) {\r
+           logprintf(ctx, "%s packet #0x%lx, type %d / 0x%02x (%s)\r\n",\r
+                     direction == PKT_INCOMING ? "Incoming" : "Outgoing",\r
+                     *seq, type, type, texttype);\r
+       } else {\r
+           logprintf(ctx, "%s packet type %d / 0x%02x (%s)\r\n",\r
+                     direction == PKT_INCOMING ? "Incoming" : "Outgoing",\r
+                     type, type, texttype);\r
+       }\r
+    } else {\r
+        logprintf(ctx, "%s raw data\r\n",\r
+                  direction == PKT_INCOMING ? "Incoming" : "Outgoing");\r
+    }\r
+\r
+    /*\r
+     * Output a hex/ASCII dump of the packet body, blanking/omitting\r
+     * parts as specified.\r
+     */\r
+    while (p < len) {\r
+       int blktype;\r
+\r
+       /* Move to a current entry in the blanking array. */\r
+       while ((b < n_blanks) &&\r
+              (p >= blanks[b].offset + blanks[b].len))\r
+           b++;\r
+       /* Work out what type of blanking to apply to\r
+        * this byte. */\r
+       blktype = PKTLOG_EMIT; /* default */\r
+       if ((b < n_blanks) &&\r
+           (p >= blanks[b].offset) &&\r
+           (p < blanks[b].offset + blanks[b].len))\r
+           blktype = blanks[b].type;\r
+\r
+       /* If we're about to stop omitting, it's time to say how\r
+        * much we omitted. */\r
+       if ((blktype != PKTLOG_OMIT) && omitted) {\r
+           logprintf(ctx, "  (%d byte%s omitted)\r\n",\r
+                     omitted, (omitted==1?"":"s"));\r
+           omitted = 0;\r
+       }\r
+\r
+       /* (Re-)initialise dumpdata as necessary\r
+        * (start of row, or if we've just stopped omitting) */\r
+       if (!output_pos && !omitted)\r
+           sprintf(dumpdata, "  %08x%*s\r\n", p-(p%16), 1+3*16+2+16, "");\r
+\r
+       /* Deal with the current byte. */\r
+       if (blktype == PKTLOG_OMIT) {\r
+           omitted++;\r
+       } else {\r
+           int c;\r
+           if (blktype == PKTLOG_BLANK) {\r
+               c = 'X';\r
+               sprintf(smalldata, "XX");\r
+           } else {  /* PKTLOG_EMIT */\r
+               c = ((unsigned char *)data)[p];\r
+               sprintf(smalldata, "%02x", c);\r
+           }\r
+           dumpdata[10+2+3*(p%16)] = smalldata[0];\r
+           dumpdata[10+2+3*(p%16)+1] = smalldata[1];\r
+           dumpdata[10+1+3*16+2+(p%16)] = (isprint(c) ? c : '.');\r
+           output_pos = (p%16) + 1;\r
+       }\r
+\r
+       p++;\r
+\r
+       /* Flush row if necessary */\r
+       if (((p % 16) == 0) || (p == len) || omitted) {\r
+           if (output_pos) {\r
+               strcpy(dumpdata + 10+1+3*16+2+output_pos, "\r\n");\r
+               logwrite(ctx, dumpdata, strlen(dumpdata));\r
+               output_pos = 0;\r
+           }\r
+       }\r
+\r
+    }\r
+\r
+    /* Tidy up */\r
+    if (omitted)\r
+       logprintf(ctx, "  (%d byte%s omitted)\r\n",\r
+                 omitted, (omitted==1?"":"s"));\r
+    logflush(ctx);\r
+}\r
+\r
+void *log_init(void *frontend, Config *cfg)\r
+{\r
+    struct LogContext *ctx = snew(struct LogContext);\r
+    ctx->lgfp = NULL;\r
+    ctx->state = L_CLOSED;\r
+    ctx->frontend = frontend;\r
+    ctx->cfg = *cfg;                  /* STRUCTURE COPY */\r
+    bufchain_init(&ctx->queue);\r
+    return ctx;\r
+}\r
+\r
+void log_free(void *handle)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+\r
+    logfclose(ctx);\r
+    bufchain_clear(&ctx->queue);\r
+    sfree(ctx);\r
+}\r
+\r
+void log_reconfig(void *handle, Config *cfg)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    int reset_logging;\r
+\r
+    if (!filename_equal(ctx->cfg.logfilename, cfg->logfilename) ||\r
+       ctx->cfg.logtype != cfg->logtype)\r
+       reset_logging = TRUE;\r
+    else\r
+       reset_logging = FALSE;\r
+\r
+    if (reset_logging)\r
+       logfclose(ctx);\r
+\r
+    ctx->cfg = *cfg;                  /* STRUCTURE COPY */\r
+\r
+    if (reset_logging)\r
+       logfopen(ctx);\r
+}\r
+\r
+/*\r
+ * translate format codes into time/date strings\r
+ * and insert them into log file name\r
+ *\r
+ * "&Y":YYYY   "&m":MM   "&d":DD   "&T":hhmmss   "&h":<hostname>   "&&":&\r
+ */\r
+static void xlatlognam(Filename *dest, Filename src,\r
+                      char *hostname, struct tm *tm) {\r
+    char buf[10], *bufp;\r
+    int size;\r
+    char buffer[FILENAME_MAX];\r
+    int len = sizeof(buffer)-1;\r
+    char *d;\r
+    const char *s;\r
+\r
+    d = buffer;\r
+    s = filename_to_str(&src);\r
+\r
+    while (*s) {\r
+       /* Let (bufp, len) be the string to append. */\r
+       bufp = buf;                    /* don't usually override this */\r
+       if (*s == '&') {\r
+           char c;\r
+           s++;\r
+           size = 0;\r
+           if (*s) switch (c = *s++, tolower((unsigned char)c)) {\r
+             case 'y':\r
+               size = strftime(buf, sizeof(buf), "%Y", tm);\r
+               break;\r
+             case 'm':\r
+               size = strftime(buf, sizeof(buf), "%m", tm);\r
+               break;\r
+             case 'd':\r
+               size = strftime(buf, sizeof(buf), "%d", tm);\r
+               break;\r
+             case 't':\r
+               size = strftime(buf, sizeof(buf), "%H%M%S", tm);\r
+               break;\r
+             case 'h':\r
+               bufp = hostname;\r
+               size = strlen(bufp);\r
+               break;\r
+             default:\r
+               buf[0] = '&';\r
+               size = 1;\r
+               if (c != '&')\r
+                   buf[size++] = c;\r
+           }\r
+       } else {\r
+           buf[0] = *s++;\r
+           size = 1;\r
+       }\r
+       if (size > len)\r
+           size = len;\r
+       memcpy(d, bufp, size);\r
+       d += size;\r
+       len -= size;\r
+    }\r
+    *d = '\0';\r
+\r
+    *dest = filename_from_str(buffer);\r
+}\r
diff --git a/putty/MACOSX/INFO.PLI b/putty/MACOSX/INFO.PLI
new file mode 100644 (file)
index 0000000..3d53c0d
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\r
+<plist version="1.0">\r
+<dict>\r
+       <key>CFBundleIconFile</key>\r
+       <string>PuTTY.icns</string>\r
+</dict>\r
+</plist>\r
diff --git a/putty/MACOSX/MAKEFILE b/putty/MACOSX/MAKEFILE
new file mode 100644 (file)
index 0000000..8620f12
--- /dev/null
@@ -0,0 +1,805 @@
+# Makefile for putty under Mac OS X.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=-DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=-DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=-DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=-DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=-DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=-DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=-DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=-DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=-DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=-DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=-DMSVC4 (Windows only)\r
+#  - RCFL=-DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON.\r
+#\r
+#  - RCFL=-DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=-DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=-DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=-DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=-DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+CC = $(TOOLPATH)gcc\r
+\r
+CFLAGS = -O2 -Wall -Werror -g -I.././ -I../charset/ -I../windows/ -I../unix/ \\r
+               -I../macosx/\r
+MLDFLAGS = -framework Cocoa\r
+ULDFLAGS =\r
+\r
+CFLAGS += -DMACOSX\r
+\r
+all: PuTTY plink pscp psftp puttygen\r
+PuTTY.app:\r
+       mkdir -p $@\r
+PuTTY.app/Contents: PuTTY.app\r
+       mkdir -p $@\r
+PuTTY.app/Contents/MacOS: PuTTY.app/Contents\r
+       mkdir -p $@\r
+PuTTY.app/Contents/Resources: PuTTY.app/Contents\r
+       mkdir -p $@\r
+PuTTY.app/Contents/Resources/PuTTY.icns: PuTTY.app/Contents/Resources putty.icns\r
+       cp putty.icns $@\r
+PuTTY.app/Contents/Info.plist: PuTTY.app/Contents/Resources info.plist\r
+       cp info.plist $@\r
+PuTTY: PuTTY.app/Contents/MacOS/PuTTY \\r
+               PuTTY.app/Contents/Resources/PuTTY.icns \\r
+               PuTTY.app/Contents/Info.plist $(PuTTY_extra)\r
+\r
+PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all_s.o config.o \\r
+               cproxy.o dialog.o fromucs.o ldisc.o ldiscucs.o localenc.o \\r
+               logging.o macenc.o mimeenc.o minibidi.o misc.o osxctrls.o \\r
+               osxdlg.o osxmain.o osxsel.o osxwin.o pgssapi.o pinger.o \\r
+               portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o \\r
+               settings.o slookup.o ssh.o sshaes.o ssharcf.o sshblowf.o \\r
+               sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \\r
+               sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \\r
+               sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o testback.o \\r
+               time.o timing.o toucs.o tree234.o utf8.o ux_x11.o uxagentc.o \\r
+               uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxprint.o \\r
+               uxproxy.o uxpty.o uxsel.o uxser.o uxsignal.o uxstore.o \\r
+               uxucs.o version.o wcwidth.o wildcard.o x11fwd.o xenc.o\r
+       $(CC) $(MLDFLAGS) -o $@ be_all_s.o config.o cproxy.o dialog.o \\r
+               fromucs.o ldisc.o ldiscucs.o localenc.o logging.o macenc.o \\r
+               mimeenc.o minibidi.o misc.o osxctrls.o osxdlg.o osxmain.o \\r
+               osxsel.o osxwin.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \\r
+               rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \\r
+               ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o telnet.o terminal.o testback.o time.o timing.o \\r
+               toucs.o tree234.o utf8.o ux_x11.o uxagentc.o uxcfg.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o uxpty.o \\r
+               uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \\r
+               wcwidth.o wildcard.o x11fwd.o xenc.o \r
+\r
+plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \\r
+               uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \\r
+               uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \\r
+               wildcard.o x11fwd.o\r
+       $(CC) $(ULDFLAGS) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o \\r
+               logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \\r
+               rlogin.o settings.o ssh.o sshaes.o ssharcf.o sshblowf.o \\r
+               sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \\r
+               sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \\r
+               sshsh512.o sshsha.o sshzlib.o telnet.o time.o timing.o \\r
+               tree234.o ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o \\r
+               uxnet.o uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o \\r
+               uxsignal.o uxstore.o version.o wildcard.o x11fwd.o \r
+\r
+pscp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) $(ULDFLAGS) -o $@ be_none.o cmdline.o cproxy.o int64.o \\r
+               logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o pscp.o \\r
+               settings.o sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o \\r
+               sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \\r
+               sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \\r
+               sshsh512.o sshsha.o sshzlib.o time.o timing.o tree234.o \\r
+               uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o \\r
+               uxproxy.o uxsel.o uxsftp.o uxstore.o version.o wildcard.o \\r
+               x11fwd.o \r
+\r
+psftp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) $(ULDFLAGS) -o $@ be_none.o cmdline.o cproxy.o int64.o \\r
+               logging.o misc.o pgssapi.o pinger.o portfwd.o proxy.o \\r
+               psftp.o settings.o sftp.o ssh.o sshaes.o ssharcf.o \\r
+               sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \\r
+               sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o \\r
+               sshsh256.o sshsh512.o sshsha.o sshzlib.o time.o timing.o \\r
+               tree234.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \\r
+               uxnoise.o uxproxy.o uxsel.o uxsftp.o uxstore.o version.o \\r
+               wildcard.o x11fwd.o \r
+\r
+puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \\r
+               sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \\r
+               sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \\r
+               tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \\r
+               version.o\r
+       $(CC) $(ULDFLAGS) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o \\r
+               sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \\r
+               sshsha.o time.o tree234.o uxcons.o uxgen.o uxmisc.o \\r
+               uxnoise.o uxstore.o version.o \r
+\r
+be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+be_nos_s.o: ../be_nos_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkfont.h ../putty.h \\r
+               ../storage.h ../dialog.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkfont.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+int64.o: ../int64.c ../int64.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+minibidi.o: ../minibidi.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+nogss.o: ../nogss.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \\r
+               ../tree234.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) -x objective-c $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \\r
+               ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) -x objective-c $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) -x objective-c $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) -x objective-c $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) -x objective-c $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \\r
+               ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../sshgssc.h ../sshgss.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../pgssapi.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../puttyps.h \\r
+               ../network.h ../pgssapi.h ../sshgss.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \\r
+               ../misc.h ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+time.o: ../time.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+tree234.o: ../tree234.c ../puttymem.h ../tree234.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../puttyps.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \\r
+               ../puttymem.h ../puttyps.h ../network.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxsignal.o: ../unix/uxsignal.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \\r
+               ../misc.h ../puttyps.h ../network.h ../tree234.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+version.o: ../version.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \\r
+               ../puttyps.h ../network.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \\r
+               ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \\r
+               ../puttymem.h ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \\r
+               ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \\r
+               ../sshgssc.h ../misc.h ../puttyps.h ../network.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winhandl.o: ../windows/winhandl.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winhelp.o: ../windows/winhelp.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winnojmp.o: ../windows/winnojmp.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \\r
+               ../proxy.h ../puttyps.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winser.o: ../windows/winser.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \\r
+               ../puttyps.h ../network.h ../tree234.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+xpmptcfg.o: ../unix/xpmptcfg.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+xpmpterm.o: ../unix/xpmpterm.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+xpmpucfg.o: ../unix/xpmpucfg.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+xpmputty.o: ../unix/xpmputty.c\r
+       $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $<\r
+\r
+\r
+clean:\r
+       rm -f *.o *.dmg plink pscp psftp puttygen\r
+       rm -rf *.app\r
+\r
+FORCE:\r
diff --git a/putty/MACOSX/OSX.H b/putty/MACOSX/OSX.H
new file mode 100644 (file)
index 0000000..fcc152f
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef PUTTY_OSX_H\r
+#define PUTTY_OSX_H\r
+\r
+/*\r
+ * Cocoa defines `FontSpec' itself, so we must change its name.\r
+ * (Arrgh.)\r
+ */\r
+#define FontSpec FontSpec_OSX_Proof\r
+\r
+/*\r
+ * Define the various compatibility symbols to make uxpty.c compile\r
+ * correctly on OS X.\r
+ */\r
+#define BSD_PTYS\r
+#define OMIT_UTMP\r
+#define HAVE_NO_SETRESUID\r
+#define NOT_X_WINDOWS\r
+\r
+/*\r
+ * OS X is largely just Unix, so we can include most of this\r
+ * unchanged.\r
+ */\r
+#include "unix.h"\r
+\r
+/*\r
+ * Functions exported by osxsel.m. (Both of these functions are\r
+ * expected to be called in the _main_ thread: the select subthread\r
+ * is an implementation detail of osxsel.m and ideally should not\r
+ * be visible at all outside it.)\r
+ */\r
+void osxsel_init(void);                       /* call this to kick things off */\r
+void osxsel_process_results(void);     /* call this on receipt of a netevent */\r
+\r
+#endif\r
diff --git a/putty/MACOSX/OSXCLASS.H b/putty/MACOSX/OSXCLASS.H
new file mode 100644 (file)
index 0000000..e005586
--- /dev/null
@@ -0,0 +1,107 @@
+/*\r
+ * Header file for the Objective-C parts of Mac OS X PuTTY. This\r
+ * file contains the class definitions, which would cause compile\r
+ * failures in the pure C modules if they appeared in osx.h.\r
+ */\r
+\r
+#ifndef PUTTY_OSXCLASS_H\r
+#define PUTTY_OSXCLASS_H\r
+\r
+#include "putty.h"\r
+\r
+/*\r
+ * The application controller class, defined in osxmain.m.\r
+ */\r
+@interface AppController : NSObject\r
+{\r
+    NSTimer *timer;\r
+}\r
+- (void)newSessionConfig:(id)sender;\r
+- (void)newTerminal:(id)sender;\r
+- (void)newSessionWithConfig:(id)cfg;\r
+- (void)setTimer:(long)next;\r
+@end\r
+extern AppController *controller;\r
+\r
+/*\r
+ * The SessionWindow class, defined in osxwin.m.\r
+ */\r
+\r
+struct alert_queue {\r
+    struct alert_queue *next;\r
+    NSAlert *alert;\r
+    void (*callback)(void *, int);\r
+    void *ctx;\r
+};\r
+\r
+@class SessionWindow;\r
+@class TerminalView;\r
+\r
+@interface SessionWindow : NSWindow\r
+{\r
+    Terminal *term;\r
+    TerminalView *termview;\r
+    struct unicode_data ucsdata;\r
+    void *logctx;\r
+    Config cfg;\r
+    void *ldisc;\r
+    Backend *back;\r
+    void *backhandle;\r
+    int exited;\r
+    /*\r
+     * The following two members relate to the currently active\r
+     * alert sheet, if any. They are NULL if there isn't one.\r
+     */\r
+    void (*alert_callback)(void *, int);\r
+    void *alert_ctx;\r
+    /* This queues future alerts that need to be shown. */\r
+    struct alert_queue *alert_qhead, *alert_qtail;\r
+}\r
+- (id)initWithConfig:(Config)cfg;\r
+- (void)drawStartFinish:(BOOL)start;\r
+- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b;\r
+- (Config *)cfg;\r
+- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y\r
+    attr:(unsigned long)attr lattr:(int)lattr;\r
+- (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr;\r
+- (int)fromBackendUntrusted:(const char *)data len:(int)len;\r
+- (void)startAlert:(NSAlert *)alert\r
+    withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx;\r
+- (void)endSession:(int)clean;\r
+- (void)notifyRemoteExit;\r
+- (Terminal *)term;\r
+@end\r
+\r
+/*\r
+ * The ConfigWindow class, defined in osxdlg.m.\r
+ */\r
+\r
+@class ConfigWindow;\r
+\r
+@interface ConfigWindow : NSWindow\r
+{\r
+    NSOutlineView *treeview;\r
+    struct controlbox *ctrlbox;\r
+    void *dv;\r
+    Config cfg;\r
+}\r
+- (id)initWithConfig:(Config)cfg;\r
+@end\r
+\r
+/*\r
+ * Functions exported by osxctrls.m. (They have to go in this\r
+ * header file and not osx.h, because some of them have Cocoa class\r
+ * types in their prototypes.)\r
+ */\r
+#define HSPACING 12                   /* needed in osxdlg.m and osxctrls.m */\r
+#define VSPACING 8\r
+\r
+void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action);\r
+void fe_dlg_free(void *dv);\r
+void create_ctrls(void *dv, NSView *parent, struct controlset *s,\r
+                 int *minw, int *minh);\r
+int place_ctrls(void *dv, struct controlset *s, int leftx, int topy,\r
+               int width);            /* returns height used */\r
+void select_panel(void *dv, struct controlbox *b, const char *name);\r
+\r
+#endif /* PUTTY_OSXCLASS_H */\r
diff --git a/putty/MACOSX/OSXCTRLS.M b/putty/MACOSX/OSXCTRLS.M
new file mode 100644 (file)
index 0000000..5e0699e
--- /dev/null
@@ -0,0 +1,1815 @@
+/*\r
+ * osxctrls.m: OS X implementation of the dialog.h interface.\r
+ */\r
+\r
+#import <Cocoa/Cocoa.h>\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "osxclass.h"\r
+#include "tree234.h"\r
+\r
+/*\r
+ * Still to be implemented:\r
+ * \r
+ *  - file selectors (NSOpenPanel / NSSavePanel)\r
+ * \r
+ *  - font selectors\r
+ *  - colour selectors\r
+ *     * both of these have a conceptual oddity in Cocoa that\r
+ *      you're only supposed to have one per application. But I\r
+ *      currently expect to be able to have multiple PuTTY config\r
+ *      boxes on screen at once; what happens if you trigger the\r
+ *      font selector in each one at the same time?\r
+ *     * if it comes to that, the _font_ selector can probably be\r
+ *      managed by other means: nobody is forcing me to implement\r
+ *      a font selector using a `Change...' button. The portable\r
+ *      dialog interface gives me the flexibility to do this how I\r
+ *      want.\r
+ *     * The colour selector interface, in its present form, is\r
+ *      more interesting and _if_ a radical change of plan is\r
+ *      required then it may stretch across the interface into the\r
+ *      portable side.\r
+ *     * Before I do anything rash I should start by looking at the\r
+ *      Mac Classic port and see how it's done there, on the basis\r
+ *      that Apple seem reasonably unlikely to have invented this\r
+ *      crazy restriction specifically for OS X.\r
+ * \r
+ *  - focus management\r
+ *     * I tried using makeFirstResponder to give keyboard focus,\r
+ *      but it appeared not to work. Try again, and work out how\r
+ *      it should be done.\r
+ *     * also look into tab order. Currently pressing Tab suggests\r
+ *      that only edit boxes and list boxes can get the keyboard\r
+ *      focus, and that buttons (in all their forms) are unable to\r
+ *      be driven by the keyboard. Find out for sure.\r
+ * \r
+ *  - dlg_error_msg\r
+ *     * this may run into the usual aggro with modal dialog boxes.\r
+ */\r
+\r
+/*\r
+ * For Cocoa control layout, I need a two-stage process. In stage\r
+ * one, I allocate all the controls and measure their natural\r
+ * sizes, which allows me to compute the _minimum_ width and height\r
+ * of a given section of dialog. Then, in stage two, I lay out the\r
+ * dialog box as a whole, decide how much each section of the box\r
+ * needs to receive, and assign it its final size.\r
+ */\r
+\r
+/*\r
+ * As yet unsolved issues [FIXME]:\r
+ * \r
+ *  - Sometimes the height returned from create_ctrls and the\r
+ *    height returned from place_ctrls differ. Find out why. It may\r
+ *    be harmless (e.g. results of NSTextView being odd), but I\r
+ *    want to know.\r
+ * \r
+ *  - NSTextViews are indented a bit. It'd be nice to put their\r
+ *    left margin at the same place as everything else's.\r
+ * \r
+ *  - I don't yet know whether we even _can_ support tab order or\r
+ *    keyboard shortcuts. If we can't, then fair enough, we can't.\r
+ *    But if we can, we should.\r
+ * \r
+ *  - I would _really_ like to know of a better way to correct\r
+ *    NSButton's stupid size estimates than by subclassing it and\r
+ *    overriding sizeToFit with hard-wired sensible values!\r
+ * \r
+ *  - Speaking of stupid size estimates, the amount by which I'm\r
+ *    adjusting a titled NSBox (currently equal to the point size\r
+ *    of its title font) looks as if it isn't _quite_ enough.\r
+ *    Figure out what the real amount should be and use it.\r
+ * \r
+ *  - I don't understand why there's always a scrollbar displayed\r
+ *    in each list box. I thought I told it to autohide scrollers?\r
+ * \r
+ *  - Why do I have to fudge list box heights by adding one? (Might\r
+ *    it be to do with the missing header view?)\r
+ */\r
+\r
+/*\r
+ * Subclass of NSButton which corrects the fact that the normal\r
+ * one's sizeToFit method persistently returns 32 as its height,\r
+ * which is simply a lie. I have yet to work out a better\r
+ * alternative than hard-coding the real heights.\r
+ */\r
+@interface MyButton : NSButton\r
+{\r
+    int minht;\r
+}\r
+@end\r
+@implementation MyButton\r
+- (id)initWithFrame:(NSRect)r\r
+{\r
+    self = [super initWithFrame:r];\r
+    minht = 25;\r
+    return self;\r
+}\r
+- (void)setButtonType:(NSButtonType)t\r
+{\r
+    if (t == NSRadioButton || t == NSSwitchButton)\r
+       minht = 18;\r
+    else\r
+       minht = 25;\r
+    [super setButtonType:t];\r
+}\r
+- (void)sizeToFit\r
+{\r
+    NSRect r;\r
+    [super sizeToFit];\r
+    r = [self frame];\r
+    r.size.height = minht;\r
+    [self setFrame:r];\r
+}\r
+@end\r
+\r
+/*\r
+ * Class used as the data source for NSTableViews.\r
+ */\r
+@interface MyTableSource : NSObject\r
+{\r
+    tree234 *tree;\r
+}\r
+- (id)init;\r
+- (void)add:(const char *)str withId:(int)id;\r
+- (int)getid:(int)index;\r
+- (void)swap:(int)index1 with:(int)index2;\r
+- (void)removestr:(int)index;\r
+- (void)clear;\r
+@end\r
+@implementation MyTableSource\r
+- (id)init\r
+{\r
+    self = [super init];\r
+    tree = newtree234(NULL);\r
+    return self;\r
+}\r
+- (void)dealloc\r
+{\r
+    char *p;\r
+    while ((p = delpos234(tree, 0)) != NULL)\r
+       sfree(p);\r
+    freetree234(tree);\r
+    [super dealloc];\r
+}\r
+- (void)add:(const char *)str withId:(int)id\r
+{\r
+    addpos234(tree, dupprintf("%d\t%s", id, str), count234(tree));\r
+}\r
+- (int)getid:(int)index\r
+{\r
+    char *p = index234(tree, index);\r
+    return atoi(p);\r
+}\r
+- (void)removestr:(int)index\r
+{\r
+    char *p = delpos234(tree, index);\r
+    sfree(p);\r
+}\r
+- (void)swap:(int)index1 with:(int)index2\r
+{\r
+    char *p1, *p2;\r
+\r
+    if (index1 > index2) {\r
+       int t = index1; index1 = index2; index2 = t;\r
+    }\r
+\r
+    /* delete later one first so it doesn't affect index of earlier one */\r
+    p2 = delpos234(tree, index2);\r
+    p1 = delpos234(tree, index1);\r
+\r
+    /* now insert earlier one before later one for the inverse reason */\r
+    addpos234(tree, p2, index1);\r
+    addpos234(tree, p1, index2);\r
+}\r
+- (void)clear\r
+{\r
+    char *p;\r
+    while ((p = delpos234(tree, 0)) != NULL)\r
+       sfree(p);\r
+}\r
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView\r
+{\r
+    return count234(tree);\r
+}\r
+- (id)tableView:(NSTableView *)aTableView\r
+    objectValueForTableColumn:(NSTableColumn *)aTableColumn\r
+    row:(int)rowIndex\r
+{\r
+    int j = [[aTableColumn identifier] intValue];\r
+    char *p = index234(tree, rowIndex);\r
+\r
+    while (j >= 0) {\r
+       p += strcspn(p, "\t");\r
+       if (*p) p++;\r
+       j--;\r
+    }\r
+\r
+    return [NSString stringWithCString:p length:strcspn(p, "\t")];\r
+}\r
+@end\r
+\r
+/*\r
+ * Object to receive messages from various control classes.\r
+ */\r
+@class Receiver;\r
+\r
+struct fe_dlg {\r
+    NSWindow *window;\r
+    NSObject *target;\r
+    SEL action;\r
+    tree234 *byctrl;\r
+    tree234 *bywidget;\r
+    tree234 *boxes;\r
+    void *data;                               /* passed to portable side */\r
+    Receiver *rec;\r
+};\r
+\r
+@interface Receiver : NSObject\r
+{\r
+    struct fe_dlg *d;\r
+}\r
+- (id)initWithStruct:(struct fe_dlg *)aStruct;\r
+@end\r
+\r
+struct fe_ctrl {\r
+    union control *ctrl;\r
+    NSButton *button, *button2;\r
+    NSTextField *label, *editbox;\r
+    NSComboBox *combobox;\r
+    NSButton **radiobuttons;\r
+    NSTextView *textview;\r
+    NSPopUpButton *popupbutton;\r
+    NSTableView *tableview;\r
+    NSScrollView *scrollview;\r
+    int nradiobuttons;\r
+    void *privdata;\r
+    int privdata_needs_free;\r
+};\r
+\r
+static int fe_ctrl_cmp_by_ctrl(void *av, void *bv)\r
+{\r
+    struct fe_ctrl *a = (struct fe_ctrl *)av;\r
+    struct fe_ctrl *b = (struct fe_ctrl *)bv;\r
+\r
+    if (a->ctrl < b->ctrl)\r
+       return -1;\r
+    if (a->ctrl > b->ctrl)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int fe_ctrl_find_by_ctrl(void *av, void *bv)\r
+{\r
+    union control *a = (union control *)av;\r
+    struct fe_ctrl *b = (struct fe_ctrl *)bv;\r
+\r
+    if (a < b->ctrl)\r
+       return -1;\r
+    if (a > b->ctrl)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+struct fe_box {\r
+    struct controlset *s;\r
+    id box;\r
+};\r
+\r
+static int fe_boxcmp(void *av, void *bv)\r
+{\r
+    struct fe_box *a = (struct fe_box *)av;\r
+    struct fe_box *b = (struct fe_box *)bv;\r
+\r
+    if (a->s < b->s)\r
+       return -1;\r
+    if (a->s > b->s)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int fe_boxfind(void *av, void *bv)\r
+{\r
+    struct controlset *a = (struct controlset *)av;\r
+    struct fe_box *b = (struct fe_box *)bv;\r
+\r
+    if (a < b->s)\r
+       return -1;\r
+    if (a > b->s)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+struct fe_backwards {                 /* map Cocoa widgets back to fe_ctrls */\r
+    id widget;\r
+    struct fe_ctrl *c;\r
+};\r
+\r
+static int fe_backwards_cmp_by_widget(void *av, void *bv)\r
+{\r
+    struct fe_backwards *a = (struct fe_backwards *)av;\r
+    struct fe_backwards *b = (struct fe_backwards *)bv;\r
+\r
+    if (a->widget < b->widget)\r
+       return -1;\r
+    if (a->widget > b->widget)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int fe_backwards_find_by_widget(void *av, void *bv)\r
+{\r
+    id a = (id)av;\r
+    struct fe_backwards *b = (struct fe_backwards *)bv;\r
+\r
+    if (a < b->widget)\r
+       return -1;\r
+    if (a > b->widget)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static struct fe_ctrl *fe_ctrl_new(union control *ctrl)\r
+{\r
+    struct fe_ctrl *c;\r
+\r
+    c = snew(struct fe_ctrl);\r
+    c->ctrl = ctrl;\r
+\r
+    c->button = c->button2 = nil;\r
+    c->label = nil;\r
+    c->editbox = nil;\r
+    c->combobox = nil;\r
+    c->textview = nil;\r
+    c->popupbutton = nil;\r
+    c->tableview = nil;\r
+    c->scrollview = nil;\r
+    c->radiobuttons = NULL;\r
+    c->nradiobuttons = 0;\r
+    c->privdata = NULL;\r
+    c->privdata_needs_free = FALSE;\r
+\r
+    return c;\r
+}\r
+\r
+static void fe_ctrl_free(struct fe_ctrl *c)\r
+{\r
+    if (c->privdata_needs_free)\r
+       sfree(c->privdata);\r
+    sfree(c->radiobuttons);\r
+    sfree(c);\r
+}\r
+\r
+static struct fe_ctrl *fe_ctrl_byctrl(struct fe_dlg *d, union control *ctrl)\r
+{\r
+    return find234(d->byctrl, ctrl, fe_ctrl_find_by_ctrl);\r
+}\r
+\r
+static void add_box(struct fe_dlg *d, struct controlset *s, id box)\r
+{\r
+    struct fe_box *b = snew(struct fe_box);\r
+    b->box = box;\r
+    b->s = s;\r
+    add234(d->boxes, b);\r
+}\r
+\r
+static id find_box(struct fe_dlg *d, struct controlset *s)\r
+{\r
+    struct fe_box *b = find234(d->boxes, s, fe_boxfind);\r
+    return b ? b->box : NULL;\r
+}\r
+\r
+static void add_widget(struct fe_dlg *d, struct fe_ctrl *c, id widget)\r
+{\r
+    struct fe_backwards *b = snew(struct fe_backwards);\r
+    b->widget = widget;\r
+    b->c = c;\r
+    add234(d->bywidget, b);\r
+}\r
+\r
+static struct fe_ctrl *find_widget(struct fe_dlg *d, id widget)\r
+{\r
+    struct fe_backwards *b = find234(d->bywidget, widget,\r
+                                    fe_backwards_find_by_widget);\r
+    return b ? b->c : NULL;\r
+}\r
+\r
+void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action)\r
+{\r
+    struct fe_dlg *d;\r
+\r
+    d = snew(struct fe_dlg);\r
+    d->window = window;\r
+    d->target = target;\r
+    d->action = action;\r
+    d->byctrl = newtree234(fe_ctrl_cmp_by_ctrl);\r
+    d->bywidget = newtree234(fe_backwards_cmp_by_widget);\r
+    d->boxes = newtree234(fe_boxcmp);\r
+    d->data = data;\r
+    d->rec = [[Receiver alloc] initWithStruct:d];\r
+\r
+    return d;\r
+}\r
+\r
+void fe_dlg_free(void *dv)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c;\r
+    struct fe_box *b;\r
+\r
+    while ( (c = delpos234(d->byctrl, 0)) != NULL )\r
+       fe_ctrl_free(c);\r
+    freetree234(d->byctrl);\r
+\r
+    while ( (c = delpos234(d->bywidget, 0)) != NULL )\r
+       sfree(c);\r
+    freetree234(d->bywidget);\r
+\r
+    while ( (b = delpos234(d->boxes, 0)) != NULL )\r
+       sfree(b);\r
+    freetree234(d->boxes);\r
+\r
+    [d->rec release];\r
+\r
+    sfree(d);\r
+}\r
+\r
+@implementation Receiver\r
+- (id)initWithStruct:(struct fe_dlg *)aStruct\r
+{\r
+    self = [super init];\r
+    d = aStruct;\r
+    return self;\r
+}\r
+- (void)buttonPushed:(id)sender\r
+{\r
+    struct fe_ctrl *c = find_widget(d, sender);\r
+\r
+    assert(c && c->ctrl->generic.type == CTRL_BUTTON);\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);\r
+}\r
+- (void)checkboxChanged:(id)sender\r
+{\r
+    struct fe_ctrl *c = find_widget(d, sender);\r
+\r
+    assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)radioChanged:(id)sender\r
+{\r
+    struct fe_ctrl *c = find_widget(d, sender);\r
+    int j;\r
+\r
+    assert(c && c->radiobuttons);\r
+    for (j = 0; j < c->nradiobuttons; j++)\r
+       if (sender != c->radiobuttons[j])\r
+           [c->radiobuttons[j] setState:NSOffState];\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)popupMenuSelected:(id)sender\r
+{\r
+    struct fe_ctrl *c = find_widget(d, sender);\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)controlTextDidChange:(NSNotification *)notification\r
+{\r
+    id widget = [notification object];\r
+    struct fe_ctrl *c = find_widget(d, widget);\r
+    assert(c && c->ctrl->generic.type == CTRL_EDITBOX);\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)controlTextDidEndEditing:(NSNotification *)notification\r
+{\r
+    id widget = [notification object];\r
+    struct fe_ctrl *c = find_widget(d, widget);\r
+    assert(c && c->ctrl->generic.type == CTRL_EDITBOX);\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_REFRESH);\r
+}\r
+- (void)tableViewSelectionDidChange:(NSNotification *)notification\r
+{\r
+    id widget = [notification object];\r
+    struct fe_ctrl *c = find_widget(d, widget);\r
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX);\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_SELCHANGE);\r
+}\r
+- (BOOL)tableView:(NSTableView *)aTableView\r
+    shouldEditTableColumn:(NSTableColumn *)aTableColumn\r
+    row:(int)rowIndex\r
+{\r
+    return NO;                        /* no editing permitted */\r
+}\r
+- (void)listDoubleClicked:(id)sender\r
+{\r
+    struct fe_ctrl *c = find_widget(d, sender);\r
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX);\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);\r
+}\r
+- (void)dragListButton:(id)sender\r
+{\r
+    struct fe_ctrl *c = find_widget(d, sender);\r
+    int direction, row, nrows;\r
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&\r
+          c->ctrl->listbox.draglist);\r
+\r
+    if (sender == c->button)\r
+       direction = -1;                /* up */\r
+    else\r
+       direction = +1;                /* down */\r
+\r
+    row = [c->tableview selectedRow];\r
+    nrows = [c->tableview numberOfRows];\r
+\r
+    if (row + direction < 0 || row + direction >= nrows) {\r
+       NSBeep();\r
+       return;\r
+    }\r
+\r
+    [[c->tableview dataSource] swap:row with:row+direction];\r
+    [c->tableview reloadData];\r
+    [c->tableview selectRow:row+direction byExtendingSelection:NO];\r
+\r
+    c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+@end\r
+\r
+void create_ctrls(void *dv, NSView *parent, struct controlset *s,\r
+                 int *minw, int *minh)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    int ccw[100];                     /* cumulative column widths */\r
+    int cypos[100];\r
+    int ncols;\r
+    int wmin = 0, hmin = 0;\r
+    int i, j, cw, ch;\r
+    NSRect rect;\r
+    NSFont *textviewfont = nil;\r
+    int boxh = 0, boxw = 0;\r
+\r
+    if (!s->boxname && s->boxtitle) {\r
+        /* This controlset is a panel title. */\r
+\r
+       NSTextField *tf;\r
+\r
+       tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+       [tf setEditable:NO];\r
+       [tf setSelectable:NO];\r
+       [tf setBordered:NO];\r
+       [tf setDrawsBackground:NO];\r
+       [tf setStringValue:[NSString stringWithCString:s->boxtitle]];\r
+       [tf sizeToFit];\r
+       rect = [tf frame];\r
+       [parent addSubview:tf];\r
+\r
+       /*\r
+        * I'm going to store this NSTextField in the boxes tree,\r
+        * because I really can't face having a special tree234\r
+        * mapping controlsets to panel titles.\r
+        */\r
+       add_box(d, s, tf);\r
+\r
+       *minw = rect.size.width;\r
+       *minh = rect.size.height;\r
+\r
+       return;\r
+    }\r
+\r
+    if (*s->boxname) {\r
+       /*\r
+        * Create an NSBox to contain this subset of controls.\r
+        */\r
+       NSBox *box;\r
+       NSRect tmprect;\r
+\r
+       box = [[NSBox alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+       if (s->boxtitle)\r
+           [box setTitle:[NSString stringWithCString:s->boxtitle]];\r
+       else\r
+           [box setTitlePosition:NSNoTitle];\r
+       add_box(d, s, box);\r
+       tmprect = [box frame];\r
+       [box setContentViewMargins:NSMakeSize(20,20)];\r
+       [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];\r
+       rect = [box frame];\r
+       [box setFrame:tmprect];\r
+       boxh = (int)(rect.size.height - 100);\r
+       boxw = (int)(rect.size.width - 100);\r
+       [parent addSubview:box];\r
+\r
+       if (s->boxtitle)\r
+           boxh += [[box titleFont] pointSize];\r
+\r
+       /*\r
+        * All subsequent controls will be placed within this box.\r
+        */\r
+       parent = box;\r
+    }\r
+\r
+    ncols = 1;\r
+    ccw[0] = 0;\r
+    ccw[1] = 100;\r
+    cypos[0] = 0;\r
+\r
+    /*\r
+     * Now iterate through the controls themselves, create them,\r
+     * and add their width and height to the overall width/height\r
+     * calculation.\r
+     */\r
+    for (i = 0; i < s->ncontrols; i++) {\r
+       union control *ctrl = s->ctrls[i];\r
+       struct fe_ctrl *c;\r
+       int colstart = COLUMN_START(ctrl->generic.column);\r
+       int colspan = COLUMN_SPAN(ctrl->generic.column);\r
+       int colend = colstart + colspan;\r
+       int ytop, wthis;\r
+\r
+        switch (ctrl->generic.type) {\r
+          case CTRL_COLUMNS:\r
+           for (j = 1; j < ncols; j++)\r
+               if (cypos[0] < cypos[j])\r
+                   cypos[0] = cypos[j];\r
+\r
+           assert(ctrl->columns.ncols < lenof(ccw));\r
+\r
+           ccw[0] = 0;\r
+           for (j = 0; j < ctrl->columns.ncols; j++) {\r
+               ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?\r
+                                    ctrl->columns.percentages[j] : 100);\r
+               cypos[j] = cypos[0];\r
+           }\r
+\r
+           ncols = ctrl->columns.ncols;\r
+\r
+            continue;                  /* no actual control created */\r
+          case CTRL_TABDELAY:\r
+           /*\r
+            * I'm currently uncertain that we can implement tab\r
+            * order in OS X.\r
+            */\r
+            continue;                  /* no actual control created */\r
+       }\r
+\r
+       c = fe_ctrl_new(ctrl);\r
+       add234(d->byctrl, c);\r
+\r
+       cw = ch = 0;\r
+\r
+        switch (ctrl->generic.type) {\r
+          case CTRL_BUTTON:\r
+          case CTRL_CHECKBOX:\r
+           {\r
+               NSButton *b;\r
+\r
+               b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];\r
+               [b setBezelStyle:NSRoundedBezelStyle];\r
+               if (ctrl->generic.type == CTRL_CHECKBOX)\r
+                   [b setButtonType:NSSwitchButton];\r
+               [b setTitle:[NSString stringWithCString:ctrl->generic.label]];\r
+               if (ctrl->button.isdefault)\r
+                   [b setKeyEquivalent:@"\r"];\r
+               else if (ctrl->button.iscancel)\r
+                   [b setKeyEquivalent:@"\033"];\r
+               [b sizeToFit];\r
+               rect = [b frame];\r
+\r
+               [parent addSubview:b];\r
+\r
+               [b setTarget:d->rec];\r
+               if (ctrl->generic.type == CTRL_CHECKBOX)\r
+                   [b setAction:@selector(checkboxChanged:)];\r
+               else\r
+                   [b setAction:@selector(buttonPushed:)];\r
+               add_widget(d, c, b);\r
+\r
+               c->button = b;\r
+\r
+               cw = rect.size.width;\r
+               ch = rect.size.height;\r
+           }\r
+           break;\r
+         case CTRL_EDITBOX:\r
+           {\r
+               int editp = ctrl->editbox.percentwidth;\r
+               int labelp = editp == 100 ? 100 : 100 - editp;\r
+               NSTextField *tf;\r
+               NSComboBox *cb;\r
+\r
+               tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+               [tf setEditable:NO];\r
+               [tf setSelectable:NO];\r
+               [tf setBordered:NO];\r
+               [tf setDrawsBackground:NO];\r
+               [tf setStringValue:[NSString\r
+                                   stringWithCString:ctrl->generic.label]];\r
+               [tf sizeToFit];\r
+               rect = [tf frame];\r
+               [parent addSubview:tf];\r
+               c->label = tf;\r
+\r
+               cw = rect.size.width * 100 / labelp;\r
+               ch = rect.size.height;\r
+\r
+               if (ctrl->editbox.has_list) {\r
+                   cb = [[NSComboBox alloc]\r
+                         initWithFrame:NSMakeRect(0,0,1,1)];\r
+                   [cb setStringValue:@"x"];\r
+                   [cb sizeToFit];\r
+                   rect = [cb frame];\r
+                   [parent addSubview:cb];\r
+                   c->combobox = cb;\r
+               } else {\r
+                   if (ctrl->editbox.password)\r
+                       tf = [NSSecureTextField alloc];\r
+                   else\r
+                       tf = [NSTextField alloc];\r
+\r
+                   tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];\r
+                   [tf setEditable:YES];\r
+                   [tf setSelectable:YES];\r
+                   [tf setBordered:YES];\r
+                   [tf setStringValue:@"x"];\r
+                   [tf sizeToFit];\r
+                   rect = [tf frame];\r
+                   [parent addSubview:tf];\r
+                   c->editbox = tf;\r
+\r
+                   [tf setDelegate:d->rec];\r
+                   add_widget(d, c, tf);\r
+               }\r
+\r
+               if (editp == 100) {\r
+                   /* the edit box and its label are vertically separated */\r
+                   ch += VSPACING + rect.size.height;\r
+               } else {\r
+                   /* the edit box and its label are horizontally separated */\r
+                   if (ch < rect.size.height)\r
+                       ch = rect.size.height;\r
+               }\r
+\r
+               if (cw < rect.size.width * 100 / editp)\r
+                   cw = rect.size.width * 100 / editp;\r
+           }\r
+           break;\r
+         case CTRL_TEXT:\r
+           {\r
+               NSTextView *tv;\r
+               int testwid;\r
+\r
+               if (!textviewfont) {\r
+                   NSTextField *tf;\r
+                   tf = [[NSTextField alloc] init];\r
+                   textviewfont = [tf font];\r
+                   [tf release];\r
+               }\r
+\r
+               testwid = (ccw[colend] - ccw[colstart]) * 3;\r
+\r
+               tv = [[NSTextView alloc]\r
+                     initWithFrame:NSMakeRect(0,0,testwid,1)];\r
+               [tv setEditable:NO];\r
+               [tv setSelectable:NO];\r
+               //[tv setBordered:NO];\r
+               [tv setDrawsBackground:NO];\r
+               [tv setFont:textviewfont];\r
+               [tv setString:\r
+                [NSString stringWithCString:ctrl->generic.label]];\r
+               rect = [tv frame];\r
+               [tv sizeToFit];\r
+               [parent addSubview:tv];\r
+               c->textview = tv;\r
+\r
+               cw = rect.size.width;\r
+               ch = rect.size.height;\r
+           }\r
+           break;\r
+         case CTRL_RADIO:\r
+           {\r
+               NSTextField *tf;\r
+               int j;\r
+\r
+               if (ctrl->generic.label) {\r
+                   tf = [[NSTextField alloc]\r
+                         initWithFrame:NSMakeRect(0,0,1,1)];\r
+                   [tf setEditable:NO];\r
+                   [tf setSelectable:NO];\r
+                   [tf setBordered:NO];\r
+                   [tf setDrawsBackground:NO];\r
+                   [tf setStringValue:\r
+                    [NSString stringWithCString:ctrl->generic.label]];\r
+                   [tf sizeToFit];\r
+                   rect = [tf frame];\r
+                   [parent addSubview:tf];\r
+                   c->label = tf;\r
+\r
+                   cw = rect.size.width;\r
+                   ch = rect.size.height;\r
+               } else {\r
+                   cw = 0;\r
+                   ch = -VSPACING;    /* compensate for next advance */\r
+               }\r
+\r
+               c->nradiobuttons = ctrl->radio.nbuttons;\r
+               c->radiobuttons = snewn(ctrl->radio.nbuttons, NSButton *);\r
+\r
+               for (j = 0; j < ctrl->radio.nbuttons; j++) {\r
+                   NSButton *b;\r
+                   int ncols;\r
+\r
+                   b = [[MyButton alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+                   [b setBezelStyle:NSRoundedBezelStyle];\r
+                   [b setButtonType:NSRadioButton];\r
+                   [b setTitle:[NSString\r
+                                stringWithCString:ctrl->radio.buttons[j]]];\r
+                   [b sizeToFit];\r
+                   rect = [b frame];\r
+                   [parent addSubview:b];\r
+\r
+                   c->radiobuttons[j] = b;\r
+\r
+                   [b setTarget:d->rec];\r
+                   [b setAction:@selector(radioChanged:)];\r
+                   add_widget(d, c, b);\r
+\r
+                   /*\r
+                    * Add to the height every time we place a\r
+                    * button in column 0.\r
+                    */\r
+                   if (j % ctrl->radio.ncolumns == 0) {\r
+                       ch += rect.size.height + VSPACING;\r
+                   }\r
+\r
+                   /*\r
+                    * Add to the width by working out how many\r
+                    * columns this button spans.\r
+                    */\r
+                   if (j == ctrl->radio.nbuttons - 1)\r
+                       ncols = (ctrl->radio.ncolumns -\r
+                                (j % ctrl->radio.ncolumns));\r
+                   else\r
+                       ncols = 1;\r
+\r
+                   if (cw < rect.size.width * ctrl->radio.ncolumns / ncols)\r
+                       cw = rect.size.width * ctrl->radio.ncolumns / ncols;\r
+               }\r
+           }\r
+           break;\r
+         case CTRL_FILESELECT:\r
+         case CTRL_FONTSELECT:\r
+           {\r
+               NSTextField *tf;\r
+               NSButton *b;\r
+               int kh;\r
+\r
+               tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+               [tf setEditable:NO];\r
+               [tf setSelectable:NO];\r
+               [tf setBordered:NO];\r
+               [tf setDrawsBackground:NO];\r
+               [tf setStringValue:[NSString\r
+                                   stringWithCString:ctrl->generic.label]];\r
+               [tf sizeToFit];\r
+               rect = [tf frame];\r
+               [parent addSubview:tf];\r
+               c->label = tf;\r
+\r
+               cw = rect.size.width;\r
+               ch = rect.size.height;\r
+\r
+               tf = [NSTextField alloc];\r
+               tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];\r
+               if (ctrl->generic.type == CTRL_FILESELECT) {\r
+                   [tf setEditable:YES];\r
+                   [tf setSelectable:YES];\r
+                   [tf setBordered:YES];\r
+               } else {\r
+                   [tf setEditable:NO];\r
+                   [tf setSelectable:NO];\r
+                   [tf setBordered:NO];\r
+                   [tf setDrawsBackground:NO];\r
+               }\r
+               [tf setStringValue:@"x"];\r
+               [tf sizeToFit];\r
+               rect = [tf frame];\r
+               [parent addSubview:tf];\r
+               c->editbox = tf;\r
+\r
+               kh = rect.size.height;\r
+               if (cw < rect.size.width * 4 / 3)\r
+                   cw = rect.size.width * 4 / 3;\r
+\r
+               b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];\r
+               [b setBezelStyle:NSRoundedBezelStyle];\r
+               if (ctrl->generic.type == CTRL_FILESELECT)\r
+                   [b setTitle:@"Browse..."];\r
+               else\r
+                   [b setTitle:@"Change..."];\r
+               // [b setKeyEquivalent:somethingorother];\r
+               // [b setTarget:somethingorother];\r
+               // [b setAction:somethingorother];\r
+               [b sizeToFit];\r
+               rect = [b frame];\r
+               [parent addSubview:b];\r
+\r
+               c->button = b;\r
+\r
+               if (kh < rect.size.height)\r
+                   kh = rect.size.height;\r
+               ch += VSPACING + kh;\r
+               if (cw < rect.size.width * 4)\r
+                   cw = rect.size.width * 4;\r
+           }\r
+           break;\r
+         case CTRL_LISTBOX:\r
+           {\r
+               int listp = ctrl->listbox.percentwidth;\r
+               int labelp = listp == 100 ? 100 : 100 - listp;\r
+               NSTextField *tf;\r
+               NSPopUpButton *pb;\r
+               NSTableView *tv;\r
+               NSScrollView *sv;\r
+\r
+               if (ctrl->generic.label) {\r
+                   tf = [[NSTextField alloc]\r
+                         initWithFrame:NSMakeRect(0,0,1,1)];\r
+                   [tf setEditable:NO];\r
+                   [tf setSelectable:NO];\r
+                   [tf setBordered:NO];\r
+                   [tf setDrawsBackground:NO];\r
+                   [tf setStringValue:\r
+                    [NSString stringWithCString:ctrl->generic.label]];\r
+                   [tf sizeToFit];\r
+                   rect = [tf frame];\r
+                   [parent addSubview:tf];\r
+                   c->label = tf;\r
+\r
+                   cw = rect.size.width;\r
+                   ch = rect.size.height;\r
+               } else {\r
+                   cw = 0;\r
+                   ch = -VSPACING;    /* compensate for next advance */\r
+               }\r
+\r
+               if (ctrl->listbox.height == 0) {\r
+                   pb = [[NSPopUpButton alloc]\r
+                         initWithFrame:NSMakeRect(0,0,1,1)];\r
+                   [pb sizeToFit];\r
+                   rect = [pb frame];\r
+                   [parent addSubview:pb];\r
+                   c->popupbutton = pb;\r
+\r
+                   [pb setTarget:d->rec];\r
+                   [pb setAction:@selector(popupMenuSelected:)];\r
+                   add_widget(d, c, pb);\r
+               } else {\r
+                   assert(listp == 100);\r
+                   if (ctrl->listbox.draglist) {\r
+                       int bi;\r
+\r
+                       listp = 75;\r
+\r
+                       for (bi = 0; bi < 2; bi++) {\r
+                           NSButton *b;\r
+                           b = [[MyButton alloc]\r
+                                initWithFrame:NSMakeRect(0, 0, 1, 1)];\r
+                           [b setBezelStyle:NSRoundedBezelStyle];\r
+                           if (bi == 0)\r
+                               [b setTitle:@"Up"];\r
+                           else\r
+                               [b setTitle:@"Down"];\r
+                           [b sizeToFit];\r
+                           rect = [b frame];\r
+                           [parent addSubview:b];\r
+\r
+                           if (bi == 0)\r
+                               c->button = b;\r
+                           else\r
+                               c->button2 = b;\r
+\r
+                           [b setTarget:d->rec];\r
+                           [b setAction:@selector(dragListButton:)];\r
+                           add_widget(d, c, b);\r
+\r
+                           if (cw < rect.size.width * 4)\r
+                               cw = rect.size.width * 4;\r
+                       }\r
+                   }\r
+\r
+                   sv = [[NSScrollView alloc] initWithFrame:\r
+                         NSMakeRect(20,20,10,10)];\r
+                   [sv setBorderType:NSLineBorder];\r
+                   tv = [[NSTableView alloc] initWithFrame:[sv frame]];\r
+                   [[tv headerView] setFrame:NSMakeRect(0,0,0,0)];\r
+                   [sv setDocumentView:tv];\r
+                   [parent addSubview:sv];\r
+                   [sv setHasVerticalScroller:YES];\r
+                   [sv setAutohidesScrollers:YES];\r
+                   [tv setAllowsColumnReordering:NO];\r
+                   [tv setAllowsColumnResizing:NO];\r
+                   [tv setAllowsMultipleSelection:ctrl->listbox.multisel];\r
+                   [tv setAllowsEmptySelection:YES];\r
+                   [tv setAllowsColumnSelection:YES];\r
+                   [tv setDataSource:[[MyTableSource alloc] init]];\r
+                   rect = [tv frame];\r
+                   /*\r
+                    * For some reason this consistently comes out\r
+                    * one short. Add one.\r
+                    */\r
+                   rect.size.height = (ctrl->listbox.height+1)*[tv rowHeight];\r
+                   [sv setFrame:rect];\r
+                   c->tableview = tv;\r
+                   c->scrollview = sv;\r
+\r
+                   [tv setDelegate:d->rec];\r
+                   [tv setTarget:d->rec];\r
+                   [tv setDoubleAction:@selector(listDoubleClicked:)];\r
+                   add_widget(d, c, tv);\r
+               }\r
+\r
+               if (c->tableview) {\r
+                   int ncols, *percentages;\r
+                   int hundred = 100;\r
+\r
+                   if (ctrl->listbox.ncols) {\r
+                       ncols = ctrl->listbox.ncols;\r
+                       percentages = ctrl->listbox.percentages;\r
+                   } else {\r
+                       ncols = 1;\r
+                       percentages = &hundred;\r
+                   }\r
+\r
+                   for (j = 0; j < ncols; j++) {\r
+                       NSTableColumn *col;\r
+\r
+                       col = [[NSTableColumn alloc] initWithIdentifier:\r
+                              [NSNumber numberWithInt:j]];\r
+                       [c->tableview addTableColumn:col];\r
+                   }\r
+               }\r
+\r
+               if (labelp == 100) {\r
+                   /* the list and its label are vertically separated */\r
+                   ch += VSPACING + rect.size.height;\r
+               } else {\r
+                   /* the list and its label are horizontally separated */\r
+                   if (ch < rect.size.height)\r
+                       ch = rect.size.height;\r
+               }\r
+\r
+               if (cw < rect.size.width * 100 / listp)\r
+                   cw = rect.size.width * 100 / listp;\r
+           }\r
+           break;\r
+       }\r
+\r
+       /*\r
+        * Update the width and height data for the control we've\r
+        * just created.\r
+        */\r
+       ytop = 0;\r
+\r
+       for (j = colstart; j < colend; j++) {\r
+           if (ytop < cypos[j])\r
+               ytop = cypos[j];\r
+       }\r
+\r
+       for (j = colstart; j < colend; j++)\r
+           cypos[j] = ytop + ch + VSPACING;\r
+\r
+       if (hmin < ytop + ch)\r
+           hmin = ytop + ch;\r
+\r
+       wthis = (cw + HSPACING) * 100 / (ccw[colend] - ccw[colstart]);\r
+       wthis -= HSPACING;\r
+\r
+       if (wmin < wthis)\r
+           wmin = wthis;\r
+    }\r
+\r
+    if (*s->boxname) {\r
+       /*\r
+        * Add a bit to the width and height for the box.\r
+        */\r
+       wmin += boxw;\r
+       hmin += boxh;\r
+    }\r
+\r
+    //printf("For controlset %s/%s, returning w=%d h=%d\n",\r
+    //       s->pathname, s->boxname, wmin, hmin);\r
+    *minw = wmin;\r
+    *minh = hmin;\r
+}\r
+\r
+int place_ctrls(void *dv, struct controlset *s, int leftx, int topy,\r
+               int width)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    int ccw[100];                     /* cumulative column widths */\r
+    int cypos[100];\r
+    int ncols;\r
+    int i, j, ret;\r
+    int boxh = 0, boxw = 0;\r
+\r
+    if (!s->boxname && s->boxtitle) {\r
+        /* Size and place the panel title. */\r
+\r
+       NSTextField *tf = find_box(d, s);\r
+       NSRect rect;\r
+\r
+       rect = [tf frame];\r
+       [tf setFrame:NSMakeRect(leftx, topy-rect.size.height,\r
+                               width, rect.size.height)];\r
+       return rect.size.height;\r
+    }\r
+\r
+    if (*s->boxname) {\r
+       NSRect rect, tmprect;\r
+       NSBox *box = find_box(d, s);\r
+\r
+       assert(box != NULL);\r
+       tmprect = [box frame];\r
+       [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];\r
+       rect = [box frame];\r
+       [box setFrame:tmprect];\r
+       boxw = rect.size.width - 100;\r
+       boxh = rect.size.height - 100;\r
+       if (s->boxtitle)\r
+           boxh += [[box titleFont] pointSize];\r
+       topy -= boxh;\r
+       width -= boxw;\r
+    }\r
+\r
+    ncols = 1;\r
+    ccw[0] = 0;\r
+    ccw[1] = 100;\r
+    cypos[0] = topy;\r
+    ret = 0;\r
+\r
+    /*\r
+     * Now iterate through the controls themselves, placing them\r
+     * appropriately.\r
+     */\r
+    for (i = 0; i < s->ncontrols; i++) {\r
+       union control *ctrl = s->ctrls[i];\r
+       struct fe_ctrl *c;\r
+       int colstart = COLUMN_START(ctrl->generic.column);\r
+       int colspan = COLUMN_SPAN(ctrl->generic.column);\r
+       int colend = colstart + colspan;\r
+       int xthis, ythis, wthis, ch;\r
+       NSRect rect;\r
+\r
+        switch (ctrl->generic.type) {\r
+          case CTRL_COLUMNS:\r
+           for (j = 1; j < ncols; j++)\r
+               if (cypos[0] > cypos[j])\r
+                   cypos[0] = cypos[j];\r
+\r
+           assert(ctrl->columns.ncols < lenof(ccw));\r
+\r
+           ccw[0] = 0;\r
+           for (j = 0; j < ctrl->columns.ncols; j++) {\r
+               ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?\r
+                                    ctrl->columns.percentages[j] : 100);\r
+               cypos[j] = cypos[0];\r
+           }\r
+\r
+           ncols = ctrl->columns.ncols;\r
+\r
+            continue;                  /* no actual control created */\r
+          case CTRL_TABDELAY:\r
+            continue;                  /* nothing to do here, move along */\r
+       }\r
+\r
+       c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+       ch = 0;\r
+       ythis = topy;\r
+\r
+       for (j = colstart; j < colend; j++) {\r
+           if (ythis > cypos[j])\r
+               ythis = cypos[j];\r
+       }\r
+\r
+       xthis = (width + HSPACING) * ccw[colstart] / 100;\r
+       wthis = (width + HSPACING) * ccw[colend] / 100 - HSPACING - xthis;\r
+       xthis += leftx;\r
+\r
+        switch (ctrl->generic.type) {\r
+          case CTRL_BUTTON:\r
+         case CTRL_CHECKBOX:\r
+           rect = [c->button frame];\r
+           [c->button setFrame:NSMakeRect(xthis,ythis-rect.size.height,wthis,\r
+                                          rect.size.height)];\r
+           ch = rect.size.height;\r
+           break;\r
+         case CTRL_EDITBOX:\r
+           {\r
+               int editp = ctrl->editbox.percentwidth;\r
+               int labelp = editp == 100 ? 100 : 100 - editp;\r
+               int lheight, theight, rheight, ynext, editw;\r
+               NSControl *edit = (c->editbox ? c->editbox : c->combobox);\r
+\r
+               rect = [c->label frame];\r
+               lheight = rect.size.height;\r
+               rect = [edit frame];\r
+               theight = rect.size.height;\r
+\r
+               if (editp == 100)\r
+                   rheight = lheight;\r
+               else\r
+                   rheight = (lheight < theight ? theight : lheight);\r
+\r
+               [c->label setFrame:\r
+                NSMakeRect(xthis, ythis-(rheight+lheight)/2,\r
+                           (wthis + HSPACING) * labelp / 100 - HSPACING,\r
+                           lheight)];\r
+               if (editp == 100) {\r
+                   ynext = ythis - rheight - VSPACING;\r
+                   rheight = theight;\r
+               } else {\r
+                   ynext = ythis;\r
+               }\r
+\r
+               editw = (wthis + HSPACING) * editp / 100 - HSPACING;\r
+\r
+               [edit setFrame:\r
+                NSMakeRect(xthis+wthis-editw, ynext-(rheight+theight)/2,\r
+                           editw, theight)];\r
+\r
+               ch = (ythis - ynext) + theight;\r
+           }\r
+           break;\r
+          case CTRL_TEXT:\r
+           [c->textview setFrame:NSMakeRect(xthis, 0, wthis, 1)];\r
+           [c->textview sizeToFit];\r
+           rect = [c->textview frame];\r
+           [c->textview setFrame:NSMakeRect(xthis, ythis-rect.size.height,\r
+                                            wthis, rect.size.height)];\r
+           ch = rect.size.height;\r
+           break;\r
+         case CTRL_RADIO:\r
+           {\r
+               int j, ynext;\r
+\r
+               if (c->label) {\r
+                   rect = [c->label frame];\r
+                   [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,\r
+                                                 wthis,rect.size.height)];\r
+                   ynext = ythis - rect.size.height - VSPACING;\r
+               } else\r
+                   ynext = ythis;\r
+\r
+               for (j = 0; j < ctrl->radio.nbuttons; j++) {\r
+                   int col = j % ctrl->radio.ncolumns;\r
+                   int ncols;\r
+                   int lx,rx;\r
+\r
+                   if (j == ctrl->radio.nbuttons - 1)\r
+                       ncols = ctrl->radio.ncolumns - col;\r
+                   else\r
+                       ncols = 1;\r
+\r
+                   lx = (wthis + HSPACING) * col / ctrl->radio.ncolumns;\r
+                   rx = ((wthis + HSPACING) *\r
+                         (col+ncols) / ctrl->radio.ncolumns) - HSPACING;\r
+\r
+                   /*\r
+                    * Set the frame size.\r
+                    */\r
+                   rect = [c->radiobuttons[j] frame];\r
+                   [c->radiobuttons[j] setFrame:\r
+                    NSMakeRect(lx+xthis, ynext-rect.size.height,\r
+                               rx-lx, rect.size.height)];\r
+\r
+                   /*\r
+                    * Advance to next line if we're in the last\r
+                    * column.\r
+                    */\r
+                   if (col + ncols == ctrl->radio.ncolumns)\r
+                       ynext -= rect.size.height + VSPACING;\r
+               }\r
+               ch = (ythis - ynext) - VSPACING;\r
+           }\r
+           break;\r
+         case CTRL_FILESELECT:\r
+         case CTRL_FONTSELECT:\r
+           {\r
+               int ynext, eh, bh, th, mx;\r
+\r
+               rect = [c->label frame];\r
+               [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,\r
+                                             wthis,rect.size.height)];\r
+               ynext = ythis - rect.size.height - VSPACING;\r
+\r
+               rect = [c->editbox frame];\r
+               eh = rect.size.height;\r
+               rect = [c->button frame];\r
+               bh = rect.size.height;\r
+               th = (eh > bh ? eh : bh);\r
+\r
+               mx = (wthis + HSPACING) * 3 / 4 - HSPACING;\r
+\r
+               [c->editbox setFrame:\r
+                NSMakeRect(xthis, ynext-(th+eh)/2, mx, eh)];\r
+               [c->button setFrame:\r
+                NSMakeRect(xthis+mx+HSPACING, ynext-(th+bh)/2,\r
+                           wthis-mx-HSPACING, bh)];\r
+\r
+               ch = (ythis - ynext) + th + VSPACING;\r
+           }\r
+           break;\r
+         case CTRL_LISTBOX:\r
+           {\r
+               int listp = ctrl->listbox.percentwidth;\r
+               int labelp = listp == 100 ? 100 : 100 - listp;\r
+               int lheight, theight, rheight, ynext, listw, xlist;\r
+               NSControl *list = (c->scrollview ? (id)c->scrollview :\r
+                                  (id)c->popupbutton);\r
+\r
+               if (ctrl->listbox.draglist) {\r
+                   assert(listp == 100);\r
+                   listp = 75;\r
+               }\r
+\r
+               rect = [list frame];\r
+               theight = rect.size.height;\r
+\r
+               if (c->label) {\r
+                   rect = [c->label frame];\r
+                   lheight = rect.size.height;\r
+\r
+                   if (labelp == 100)\r
+                       rheight = lheight;\r
+                   else\r
+                       rheight = (lheight < theight ? theight : lheight);\r
+\r
+                   [c->label setFrame:\r
+                    NSMakeRect(xthis, ythis-(rheight+lheight)/2,\r
+                               (wthis + HSPACING) * labelp / 100 - HSPACING,\r
+                               lheight)];\r
+                   if (labelp == 100) {\r
+                       ynext = ythis - rheight - VSPACING;\r
+                       rheight = theight;\r
+                   } else {\r
+                       ynext = ythis;\r
+                   }\r
+               } else {\r
+                   ynext = ythis;\r
+                   rheight = theight;\r
+               }\r
+\r
+               listw = (wthis + HSPACING) * listp / 100 - HSPACING;\r
+\r
+               if (labelp == 100)\r
+                   xlist = xthis;\r
+               else\r
+                   xlist = xthis+wthis-listw;\r
+\r
+               [list setFrame: NSMakeRect(xlist, ynext-(rheight+theight)/2,\r
+                                          listw, theight)];\r
+\r
+               /*\r
+                * Size the columns for the table view.\r
+                */\r
+               if (c->tableview) {\r
+                   int ncols, *percentages;\r
+                   int hundred = 100;\r
+                   int cpercent = 0, cpixels = 0;\r
+                   NSArray *cols;\r
+\r
+                   if (ctrl->listbox.ncols) {\r
+                       ncols = ctrl->listbox.ncols;\r
+                       percentages = ctrl->listbox.percentages;\r
+                   } else {\r
+                       ncols = 1;\r
+                       percentages = &hundred;\r
+                   }\r
+\r
+                   cols = [c->tableview tableColumns];\r
+\r
+                   for (j = 0; j < ncols; j++) {\r
+                       NSTableColumn *col = [cols objectAtIndex:j];\r
+                       int newcpixels;\r
+\r
+                       cpercent += percentages[j];\r
+                       newcpixels = listw * cpercent / 100;\r
+                       [col setWidth:newcpixels-cpixels];\r
+                       cpixels = newcpixels;\r
+                   }\r
+               }\r
+\r
+               ch = (ythis - ynext) + theight;\r
+\r
+               if (c->button) {\r
+                   int b2height, centre;\r
+                   int bx, bw;\r
+\r
+                   /*\r
+                    * Place the Up and Down buttons for a drag list.\r
+                    */\r
+                   assert(c->button2);\r
+\r
+                   rect = [c->button frame];\r
+                   b2height = VSPACING + 2 * rect.size.height;\r
+\r
+                   centre = ynext - rheight/2;\r
+\r
+                   bx = (wthis + HSPACING) * 3 / 4;\r
+                   bw = wthis - bx;\r
+                   bx += leftx;\r
+\r
+                   [c->button setFrame:\r
+                    NSMakeRect(bx, centre+b2height/2-rect.size.height,\r
+                               bw, rect.size.height)];\r
+                   [c->button2 setFrame:\r
+                    NSMakeRect(bx, centre-b2height/2,\r
+                               bw, rect.size.height)];\r
+               }\r
+           }\r
+           break;\r
+       }\r
+\r
+       for (j = colstart; j < colend; j++)\r
+           cypos[j] = ythis - ch - VSPACING;\r
+       if (ret < topy - (ythis - ch))\r
+           ret = topy - (ythis - ch);\r
+    }\r
+\r
+    if (*s->boxname) {\r
+       NSBox *box = find_box(d, s);\r
+       assert(box != NULL);\r
+       [box sizeToFit];\r
+\r
+       if (s->boxtitle) {\r
+           NSRect rect = [box frame];\r
+           rect.size.height += [[box titleFont] pointSize];\r
+           [box setFrame:rect];\r
+       }\r
+\r
+       ret += boxh;\r
+    }\r
+\r
+    //printf("For controlset %s/%s, returning ret=%d\n",\r
+    //       s->pathname, s->boxname, ret);\r
+    return ret;\r
+}\r
+\r
+void select_panel(void *dv, struct controlbox *b, const char *name)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    int i, j, hidden;\r
+    struct controlset *s;\r
+    union control *ctrl;\r
+    struct fe_ctrl *c;\r
+    NSBox *box;\r
+\r
+    for (i = 0; i < b->nctrlsets; i++) {\r
+       s = b->ctrlsets[i];\r
+\r
+       if (*s->pathname) {\r
+           hidden = !strcmp(s->pathname, name) ? NO : YES;\r
+\r
+           if ((box = find_box(d, s)) != NULL) {\r
+               [box setHidden:hidden];\r
+           } else {\r
+               for (j = 0; j < s->ncontrols; j++) {\r
+                   ctrl = s->ctrls[j];\r
+                   c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+                   if (!c)\r
+                       continue;\r
+\r
+                   if (c->label)\r
+                       [c->label setHidden:hidden];\r
+                   if (c->button)\r
+                       [c->button setHidden:hidden];\r
+                   if (c->button2)\r
+                       [c->button2 setHidden:hidden];\r
+                   if (c->editbox)\r
+                       [c->editbox setHidden:hidden];\r
+                   if (c->combobox)\r
+                       [c->combobox setHidden:hidden];\r
+                   if (c->textview)\r
+                       [c->textview setHidden:hidden];\r
+                   if (c->tableview)\r
+                       [c->tableview setHidden:hidden];\r
+                   if (c->scrollview)\r
+                       [c->scrollview setHidden:hidden];\r
+                   if (c->popupbutton)\r
+                       [c->popupbutton setHidden:hidden];\r
+                   if (c->radiobuttons) {\r
+                       int j;\r
+                       for (j = 0; j < c->nradiobuttons; j++)\r
+                           [c->radiobuttons[j] setHidden:hidden];\r
+                   }\r
+                   break;\r
+               }\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+void dlg_radiobutton_set(union control *ctrl, void *dv, int whichbutton)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+    int j;\r
+\r
+    assert(c->radiobuttons);\r
+    for (j = 0; j < c->nradiobuttons; j++)\r
+       [c->radiobuttons[j] setState:\r
+        (j == whichbutton ? NSOnState : NSOffState)];\r
+}\r
+\r
+int dlg_radiobutton_get(union control *ctrl, void *dv)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+    int j;\r
+\r
+    assert(c->radiobuttons);\r
+    for (j = 0; j < c->nradiobuttons; j++)\r
+       if ([c->radiobuttons[j] state] == NSOnState)\r
+           return j;\r
+\r
+    return 0;                         /* should never reach here */\r
+}\r
+\r
+void dlg_checkbox_set(union control *ctrl, void *dv, int checked)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    assert(c->button);\r
+    [c->button setState:(checked ? NSOnState : NSOffState)];\r
+}\r
+\r
+int dlg_checkbox_get(union control *ctrl, void *dv)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    assert(c->button);\r
+    return ([c->button state] == NSOnState);\r
+}\r
+\r
+void dlg_editbox_set(union control *ctrl, void *dv, char const *text)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->editbox) {\r
+       [c->editbox setStringValue:[NSString stringWithCString:text]];\r
+    } else {\r
+       assert(c->combobox);\r
+       [c->combobox setStringValue:[NSString stringWithCString:text]];\r
+    }\r
+}\r
+\r
+void dlg_editbox_get(union control *ctrl, void *dv, char *buffer, int length)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+    NSString *str;\r
+\r
+    if (c->editbox) {\r
+       str = [c->editbox stringValue];\r
+    } else {\r
+       assert(c->combobox);\r
+       str = [c->combobox stringValue];\r
+    }\r
+    if (!str)\r
+       str = @"";\r
+\r
+    /* The length parameter to this method doesn't include a trailing NUL */\r
+    [str getCString:buffer maxLength:length-1];\r
+}\r
+\r
+void dlg_listbox_clear(union control *ctrl, void *dv)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->tableview) {\r
+       [[c->tableview dataSource] clear];\r
+       [c->tableview reloadData];\r
+    } else {\r
+       [c->popupbutton removeAllItems];\r
+    }\r
+}\r
+\r
+void dlg_listbox_del(union control *ctrl, void *dv, int index)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->tableview) {\r
+       [[c->tableview dataSource] removestr:index];\r
+       [c->tableview reloadData];\r
+    } else {\r
+       [c->popupbutton removeItemAtIndex:index];\r
+    }\r
+}\r
+\r
+void dlg_listbox_addwithid(union control *ctrl, void *dv,\r
+                          char const *text, int id)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->tableview) {\r
+       [[c->tableview dataSource] add:text withId:id];\r
+       [c->tableview reloadData];\r
+    } else {\r
+       [c->popupbutton addItemWithTitle:[NSString stringWithCString:text]];\r
+       [[c->popupbutton lastItem] setTag:id];\r
+    }\r
+}\r
+\r
+void dlg_listbox_add(union control *ctrl, void *dv, char const *text)\r
+{\r
+    dlg_listbox_addwithid(ctrl, dv, text, -1);\r
+}\r
+\r
+int dlg_listbox_getid(union control *ctrl, void *dv, int index)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->tableview) {\r
+       return [[c->tableview dataSource] getid:index];\r
+    } else {\r
+       return [[c->popupbutton itemAtIndex:index] tag];\r
+    }\r
+}\r
+\r
+int dlg_listbox_index(union control *ctrl, void *dv)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->tableview) {\r
+       return [c->tableview selectedRow];\r
+    } else {\r
+       return [c->popupbutton indexOfSelectedItem];\r
+    }\r
+}\r
+\r
+int dlg_listbox_issel(union control *ctrl, void *dv, int index)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->tableview) {\r
+       return [c->tableview isRowSelected:index];\r
+    } else {\r
+       return [c->popupbutton indexOfSelectedItem] == index;\r
+    }\r
+}\r
+\r
+void dlg_listbox_select(union control *ctrl, void *dv, int index)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    if (c->tableview) {\r
+       [c->tableview selectRow:index byExtendingSelection:NO];\r
+    } else {\r
+       [c->popupbutton selectItemAtIndex:index];\r
+    }\r
+}\r
+\r
+void dlg_text_set(union control *ctrl, void *dv, char const *text)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+    assert(c->textview);\r
+    [c->textview setString:[NSString stringWithCString:text]];\r
+}\r
+\r
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    /*\r
+     * This function is currently only used by the config box to\r
+     * switch the labels on the host and port boxes between serial\r
+     * and network modes. Since OS X does not (yet?) have a serial\r
+     * back end, this function can safely do nothing for the\r
+     * moment.\r
+     */\r
+}\r
+\r
+void dlg_filesel_set(union control *ctrl, void *dv, Filename fn)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void dlg_filesel_get(union control *ctrl, void *dv, Filename *fn)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void dlg_fontsel_set(union control *ctrl, void *dv, FontSpec fn)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void dlg_fontsel_get(union control *ctrl, void *dv, FontSpec *fn)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void dlg_update_start(union control *ctrl, void *dv)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void dlg_update_done(union control *ctrl, void *dv)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void dlg_set_focus(union control *ctrl, void *dv)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+union control *dlg_last_focused(union control *ctrl, void *dv)\r
+{\r
+    return NULL; /* FIXME */\r
+}\r
+\r
+void dlg_beep(void *dv)\r
+{\r
+    NSBeep();\r
+}\r
+\r
+void dlg_error_msg(void *dv, char *msg)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void dlg_end(void *dv, int value)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    [d->target performSelector:d->action\r
+     withObject:[NSNumber numberWithInt:value]];\r
+}\r
+\r
+void dlg_coloursel_start(union control *ctrl, void *dv,\r
+                        int r, int g, int b)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+int dlg_coloursel_results(union control *ctrl, void *dv,\r
+                         int *r, int *g, int *b)\r
+{\r
+    return 0; /* FIXME */\r
+}\r
+\r
+void dlg_refresh(union control *ctrl, void *dv)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c;\r
+\r
+    if (ctrl) {\r
+       if (ctrl->generic.handler != NULL)\r
+           ctrl->generic.handler(ctrl, d, d->data, EVENT_REFRESH);\r
+    } else {\r
+       int i;\r
+\r
+       for (i = 0; (c = index234(d->byctrl, i)) != NULL; i++) {\r
+           assert(c->ctrl != NULL);\r
+           if (c->ctrl->generic.handler != NULL)\r
+               c->ctrl->generic.handler(c->ctrl, d,\r
+                                        d->data, EVENT_REFRESH);\r
+       }\r
+    }\r
+}\r
+\r
+void *dlg_get_privdata(union control *ctrl, void *dv)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+    return c->privdata;\r
+}\r
+\r
+void dlg_set_privdata(union control *ctrl, void *dv, void *ptr)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+    c->privdata = ptr;\r
+    c->privdata_needs_free = FALSE;\r
+}\r
+\r
+void *dlg_alloc_privdata(union control *ctrl, void *dv, size_t size)\r
+{\r
+    struct fe_dlg *d = (struct fe_dlg *)dv;\r
+    struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+    /*\r
+     * This is an internal allocation routine, so it's allowed to\r
+     * use smalloc directly.\r
+     */\r
+    c->privdata = smalloc(size);\r
+    c->privdata_needs_free = TRUE;\r
+    return c->privdata;\r
+}\r
diff --git a/putty/MACOSX/OSXDLG.M b/putty/MACOSX/OSXDLG.M
new file mode 100644 (file)
index 0000000..9502914
--- /dev/null
@@ -0,0 +1,509 @@
+/*\r
+ * osxdlg.m: various PuTTY dialog boxes for OS X.\r
+ */\r
+\r
+#import <Cocoa/Cocoa.h>\r
+#include "putty.h"\r
+#include "storage.h"\r
+#include "dialog.h"\r
+#include "osxclass.h"\r
+\r
+/*\r
+ * The `ConfigWindow' class is used to start up a new PuTTY\r
+ * session.\r
+ */\r
+\r
+@class ConfigTree;\r
+@interface ConfigTree : NSObject\r
+{\r
+    NSString **paths;\r
+    int *levels;\r
+    int nitems, itemsize;\r
+}\r
+- (void)addPath:(char *)path;\r
+@end\r
+\r
+@implementation ConfigTree\r
+- (id)init\r
+{\r
+    self = [super init];\r
+    paths = NULL;\r
+    levels = NULL;\r
+    nitems = itemsize = 0;\r
+    return self;\r
+}\r
+- (void)addPath:(char *)path\r
+{\r
+    if (nitems >= itemsize) {\r
+       itemsize += 32;\r
+       paths = sresize(paths, itemsize, NSString *);\r
+       levels = sresize(levels, itemsize, int);\r
+    }\r
+    paths[nitems] = [[NSString stringWithCString:path] retain];\r
+    levels[nitems] = ctrl_path_elements(path) - 1;\r
+    nitems++;\r
+}\r
+- (void)dealloc\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < nitems; i++)\r
+       [paths[i] release];\r
+\r
+    sfree(paths);\r
+    sfree(levels);\r
+\r
+    [super dealloc];\r
+}\r
+- (id)iterateChildren:(int)index ofItem:(id)item count:(int *)count\r
+{\r
+    int i, plevel;\r
+\r
+    if (item) {\r
+       for (i = 0; i < nitems; i++)\r
+           if (paths[i] == item)\r
+               break;\r
+       assert(i < nitems);\r
+       plevel = levels[i];\r
+       i++;\r
+    } else {\r
+       i = 0;\r
+       plevel = -1;\r
+    }\r
+\r
+    if (count)\r
+       *count = 0;\r
+\r
+    while (index > 0) {\r
+       if (i >= nitems || levels[i] != plevel+1)\r
+           return nil;\r
+       if (count)\r
+           (*count)++;\r
+       do {\r
+           i++;\r
+       } while (i < nitems && levels[i] > plevel+1);\r
+       index--;\r
+    }\r
+\r
+    return paths[i];\r
+}\r
+- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item\r
+{\r
+    return [self iterateChildren:index ofItem:item count:NULL];\r
+}\r
+- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item\r
+{\r
+    int count = 0;\r
+    /* pass nitems+1 to ensure we run off the end */\r
+    [self iterateChildren:nitems+1 ofItem:item count:&count];\r
+    return count;\r
+}\r
+- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item\r
+{\r
+    return [self outlineView:outlineView numberOfChildrenOfItem:item] > 0;\r
+}\r
+- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item\r
+{\r
+    /*\r
+     * Trim off all path elements except the last one.\r
+     */\r
+    NSArray *components = [item componentsSeparatedByString:@"/"];\r
+    return [components objectAtIndex:[components count]-1];\r
+}\r
+@end\r
+\r
+@implementation ConfigWindow\r
+- (id)initWithConfig:(Config)aCfg\r
+{\r
+    NSScrollView *scrollview;\r
+    NSTableColumn *col;\r
+    ConfigTree *treedata;\r
+    int by = 0, mby = 0;\r
+    int wmin = 0;\r
+    int hmin = 0;\r
+    int panelht = 0;\r
+\r
+    ctrlbox = ctrl_new_box();\r
+    setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol,\r
+                    0 /* protcfginfo */);\r
+    unix_setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol);\r
+\r
+    cfg = aCfg;                               /* structure copy */\r
+\r
+    self = [super initWithContentRect:NSMakeRect(0,0,300,300)\r
+           styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |\r
+                      NSClosableWindowMask)\r
+           backing:NSBackingStoreBuffered\r
+           defer:YES];\r
+    [self setTitle:@"PuTTY Configuration"];\r
+\r
+    [self setIgnoresMouseEvents:NO];\r
+\r
+    dv = fe_dlg_init(&cfg, self, self, @selector(configBoxFinished:));\r
+\r
+    scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(20,20,10,10)];\r
+    treeview = [[NSOutlineView alloc] initWithFrame:[scrollview frame]];\r
+    [scrollview setBorderType:NSLineBorder];\r
+    [scrollview setDocumentView:treeview];\r
+    [[self contentView] addSubview:scrollview];\r
+    [scrollview setHasVerticalScroller:YES];\r
+    [scrollview setAutohidesScrollers:YES];\r
+    /* FIXME: the below is untested. Test it then remove this notice. */\r
+    [treeview setAllowsColumnReordering:NO];\r
+    [treeview setAllowsColumnResizing:NO];\r
+    [treeview setAllowsMultipleSelection:NO];\r
+    [treeview setAllowsEmptySelection:NO];\r
+    [treeview setAllowsColumnSelection:YES];\r
+\r
+    treedata = [[[ConfigTree alloc] init] retain];\r
+\r
+    col = [[NSTableColumn alloc] initWithIdentifier:nil];\r
+    [treeview addTableColumn:col];\r
+    [treeview setOutlineTableColumn:col];\r
+\r
+    [[treeview headerView] setFrame:NSMakeRect(0,0,0,0)];\r
+\r
+    /*\r
+     * Create the controls.\r
+     */\r
+    {\r
+       int i;\r
+       char *path = NULL;\r
+\r
+       for (i = 0; i < ctrlbox->nctrlsets; i++) {\r
+           struct controlset *s = ctrlbox->ctrlsets[i];\r
+           int mw, mh;\r
+\r
+           if (!*s->pathname) {\r
+\r
+               create_ctrls(dv, [self contentView], s, &mw, &mh);\r
+\r
+               by += 20 + mh;\r
+\r
+               if (wmin < mw + 40)\r
+                   wmin = mw + 40;\r
+           } else {\r
+               int j = path ? ctrl_path_compare(s->pathname, path) : 0;\r
+\r
+               if (j != INT_MAX) {    /* add to treeview, start new panel */\r
+                   char *c;\r
+\r
+                   /*\r
+                    * We expect never to find an implicit path\r
+                    * component. For example, we expect never to\r
+                    * see A/B/C followed by A/D/E, because that\r
+                    * would _implicitly_ create A/D. All our path\r
+                    * prefixes are expected to contain actual\r
+                    * controls and be selectable in the treeview;\r
+                    * so we would expect to see A/D _explicitly_\r
+                    * before encountering A/D/E.\r
+                    */\r
+                   assert(j == ctrl_path_elements(s->pathname) - 1);\r
+\r
+                   c = strrchr(s->pathname, '/');\r
+                   if (!c)\r
+                       c = s->pathname;\r
+                   else\r
+                       c++;\r
+\r
+                   [treedata addPath:s->pathname];\r
+                   path = s->pathname;\r
+\r
+                   panelht = 0;\r
+               }\r
+\r
+               create_ctrls(dv, [self contentView], s, &mw, &mh);\r
+               if (wmin < mw + 3*20+150)\r
+                   wmin = mw + 3*20+150;\r
+               panelht += mh + 20;\r
+               if (hmin < panelht - 20)\r
+                   hmin = panelht - 20;\r
+           }\r
+       }\r
+    }\r
+\r
+    {\r
+       int i;\r
+       NSRect r;\r
+\r
+       [treeview setDataSource:treedata];\r
+       for (i = [treeview numberOfRows]; i-- ;)\r
+           [treeview expandItem:[treeview itemAtRow:i] expandChildren:YES];\r
+\r
+       [treeview sizeToFit];\r
+       r = [treeview frame];\r
+       if (hmin < r.size.height)\r
+           hmin = r.size.height;\r
+    }\r
+\r
+    [self setContentSize:NSMakeSize(wmin, hmin+60+by)];\r
+    [scrollview setFrame:NSMakeRect(20, 40+by, 150, hmin)];\r
+    [treeview setDelegate:self];\r
+    mby = by;\r
+\r
+    /*\r
+     * Now place the controls.\r
+     */\r
+    {\r
+       int i;\r
+       char *path = NULL;\r
+       panelht = 0;\r
+\r
+       for (i = 0; i < ctrlbox->nctrlsets; i++) {\r
+           struct controlset *s = ctrlbox->ctrlsets[i];\r
+\r
+           if (!*s->pathname) {\r
+               by -= VSPACING + place_ctrls(dv, s, 20, by, wmin-40);\r
+           } else {\r
+               if (!path || strcmp(s->pathname, path))\r
+                   panelht = 0;\r
+\r
+               panelht += VSPACING + place_ctrls(dv, s, 2*20+150,\r
+                                                 40+mby+hmin-panelht,\r
+                                                 wmin - (3*20+150));\r
+\r
+               path = s->pathname;\r
+           }\r
+       }\r
+    }\r
+\r
+    select_panel(dv, ctrlbox, [[treeview itemAtRow:0] cString]);\r
+\r
+    [treeview reloadData];\r
+\r
+    dlg_refresh(NULL, dv);\r
+\r
+    [self center];                    /* :-) */\r
+\r
+    return self;\r
+}\r
+- (void)configBoxFinished:(id)object\r
+{\r
+    int ret = [object intValue];       /* it'll be an NSNumber */\r
+    if (ret) {\r
+       [controller performSelectorOnMainThread:\r
+        @selector(newSessionWithConfig:)\r
+        withObject:[NSData dataWithBytes:&cfg length:sizeof(cfg)]\r
+        waitUntilDone:NO];\r
+    }\r
+    [self close];\r
+}\r
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification\r
+{\r
+    const char *path = [[treeview itemAtRow:[treeview selectedRow]] cString];\r
+    select_panel(dv, ctrlbox, path);\r
+}\r
+- (BOOL)outlineView:(NSOutlineView *)outlineView\r
+    shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item\r
+{\r
+    return NO;                        /* no editing! */\r
+}\r
+@end\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Various special-purpose dialog boxes.\r
+ */\r
+\r
+struct appendstate {\r
+    void (*callback)(void *ctx, int result);\r
+    void *ctx;\r
+};\r
+\r
+static void askappend_callback(void *ctx, int result)\r
+{\r
+    struct appendstate *state = (struct appendstate *)ctx;\r
+\r
+    state->callback(state->ctx, (result == NSAlertFirstButtonReturn ? 2 :\r
+                                result == NSAlertSecondButtonReturn ? 1 : 0));\r
+    sfree(state);\r
+}\r
+\r
+int askappend(void *frontend, Filename filename,\r
+             void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char msgtemplate[] =\r
+       "The session log file \"%s\" already exists. "\r
+       "You can overwrite it with a new session log, "\r
+       "append your session log to the end of it, "\r
+       "or disable session logging for this session.";\r
+\r
+    char *text;\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    struct appendstate *state;\r
+    NSAlert *alert;\r
+\r
+    text = dupprintf(msgtemplate, filename.path);\r
+\r
+    state = snew(struct appendstate);\r
+    state->callback = callback;\r
+    state->ctx = ctx;\r
+\r
+    alert = [[NSAlert alloc] init];\r
+    [alert setInformativeText:[NSString stringWithCString:text]];\r
+    [alert addButtonWithTitle:@"Overwrite"];\r
+    [alert addButtonWithTitle:@"Append"];\r
+    [alert addButtonWithTitle:@"Disable"];\r
+    [win startAlert:alert withCallback:askappend_callback andCtx:state];\r
+\r
+    return -1;\r
+}\r
+\r
+struct algstate {\r
+    void (*callback)(void *ctx, int result);\r
+    void *ctx;\r
+};\r
+\r
+static void askalg_callback(void *ctx, int result)\r
+{\r
+    struct algstate *state = (struct algstate *)ctx;\r
+\r
+    state->callback(state->ctx, result == NSAlertFirstButtonReturn);\r
+    sfree(state);\r
+}\r
+\r
+int askalg(void *frontend, const char *algtype, const char *algname,\r
+          void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char msg[] =\r
+       "The first %s supported by the server is "\r
+       "%s, which is below the configured warning threshold.\n"\r
+       "Continue with connection?";\r
+\r
+    char *text;\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    struct algstate *state;\r
+    NSAlert *alert;\r
+\r
+    text = dupprintf(msg, algtype, algname);\r
+\r
+    state = snew(struct algstate);\r
+    state->callback = callback;\r
+    state->ctx = ctx;\r
+\r
+    alert = [[NSAlert alloc] init];\r
+    [alert setInformativeText:[NSString stringWithCString:text]];\r
+    [alert addButtonWithTitle:@"Yes"];\r
+    [alert addButtonWithTitle:@"No"];\r
+    [win startAlert:alert withCallback:askalg_callback andCtx:state];\r
+\r
+    return -1;\r
+}\r
+\r
+struct hostkeystate {\r
+    char *host, *keytype, *keystr;\r
+    int port;\r
+    void (*callback)(void *ctx, int result);\r
+    void *ctx;\r
+};\r
+\r
+static void verify_ssh_host_key_callback(void *ctx, int result)\r
+{\r
+    struct hostkeystate *state = (struct hostkeystate *)ctx;\r
+\r
+    if (result == NSAlertThirdButtonReturn)   /* `Accept' */\r
+       store_host_key(state->host, state->port,\r
+                      state->keytype, state->keystr);\r
+    state->callback(state->ctx, result != NSAlertFirstButtonReturn);\r
+    sfree(state->host);\r
+    sfree(state->keytype);\r
+    sfree(state->keystr);\r
+    sfree(state);\r
+}\r
+\r
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
+                        char *keystr, char *fingerprint,\r
+                        void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char absenttxt[] =\r
+       "The server's host key is not cached. You have no guarantee "\r
+       "that the server is the computer you think it is.\n"\r
+       "The server's %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you trust this host, press \"Accept\" to add the key to "\r
+       "PuTTY's cache and carry on connecting.\n"\r
+       "If you want to carry on connecting just once, without "\r
+       "adding the key to the cache, press \"Connect Once\".\n"\r
+       "If you do not trust this host, press \"Cancel\" to abandon the "\r
+       "connection.";\r
+    static const char wrongtxt[] =\r
+       "WARNING - POTENTIAL SECURITY BREACH!\n"\r
+       "The server's host key does not match the one PuTTY has "\r
+       "cached. This means that either the server administrator "\r
+       "has changed the host key, or you have actually connected "\r
+       "to another computer pretending to be the server.\n"\r
+       "The new %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you were expecting this change and trust the new key, "\r
+       "press \"Accept\" to update PuTTY's cache and continue connecting.\n"\r
+       "If you want to carry on connecting but without updating "\r
+       "the cache, press \"Connect Once\".\n"\r
+       "If you want to abandon the connection completely, press "\r
+       "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "\r
+       "safe choice.";\r
+\r
+    int ret;\r
+    char *text;\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    struct hostkeystate *state;\r
+    NSAlert *alert;\r
+\r
+    /*\r
+     * Verify the key.\r
+     */\r
+    ret = verify_host_key(host, port, keytype, keystr);\r
+\r
+    if (ret == 0)\r
+       return 1;\r
+\r
+    text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);\r
+\r
+    state = snew(struct hostkeystate);\r
+    state->callback = callback;\r
+    state->ctx = ctx;\r
+    state->host = dupstr(host);\r
+    state->port = port;\r
+    state->keytype = dupstr(keytype);\r
+    state->keystr = dupstr(keystr);\r
+\r
+    alert = [[NSAlert alloc] init];\r
+    [alert setInformativeText:[NSString stringWithCString:text]];\r
+    [alert addButtonWithTitle:@"Cancel"];\r
+    [alert addButtonWithTitle:@"Connect Once"];\r
+    [alert addButtonWithTitle:@"Accept"];\r
+    [win startAlert:alert withCallback:verify_ssh_host_key_callback\r
+     andCtx:state];\r
+\r
+    return -1;\r
+}\r
+\r
+void old_keyfile_warning(void)\r
+{\r
+    /*\r
+     * This should never happen on OS X. We hope.\r
+     */\r
+}\r
+\r
+static void connection_fatal_callback(void *ctx, int result)\r
+{\r
+    SessionWindow *win = (SessionWindow *)ctx;\r
+\r
+    [win endSession:FALSE];\r
+}\r
+\r
+void connection_fatal(void *frontend, char *p, ...)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    va_list ap;\r
+    char *msg;\r
+    NSAlert *alert;\r
+\r
+    va_start(ap, p);\r
+    msg = dupvprintf(p, ap);\r
+    va_end(ap);\r
+\r
+    alert = [[NSAlert alloc] init];\r
+    [alert setInformativeText:[NSString stringWithCString:msg]];\r
+    [alert addButtonWithTitle:@"Proceed"];\r
+    [win startAlert:alert withCallback:connection_fatal_callback\r
+     andCtx:win];\r
+}\r
diff --git a/putty/MACOSX/OSXMAIN.M b/putty/MACOSX/OSXMAIN.M
new file mode 100644 (file)
index 0000000..1c7599c
--- /dev/null
@@ -0,0 +1,408 @@
+/*\r
+ * osxmain.m: main-program file of Mac OS X PuTTY.\r
+ */\r
+\r
+#import <Cocoa/Cocoa.h>\r
+\r
+#define PUTTY_DO_GLOBALS              /* actually _define_ globals */\r
+\r
+#include "putty.h"\r
+#include "osxclass.h"\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Global variables.\r
+ */\r
+\r
+AppController *controller;\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Miscellaneous elements of the interface to the cross-platform\r
+ * and Unix PuTTY code.\r
+ */\r
+\r
+char *platform_get_x_display(void) {\r
+    return NULL;\r
+}\r
+\r
+FontSpec platform_default_fontspec(const char *name)\r
+{\r
+    FontSpec ret;\r
+    /* FIXME */\r
+    return ret;\r
+}\r
+\r
+Filename platform_default_filename(const char *name)\r
+{\r
+    Filename ret;\r
+    if (!strcmp(name, "LogFileName"))\r
+       strcpy(ret.path, "putty.log");\r
+    else\r
+       *ret.path = '\0';\r
+    return ret;\r
+}\r
+\r
+char *platform_default_s(const char *name)\r
+{\r
+    return NULL;\r
+}\r
+\r
+int platform_default_i(const char *name, int def)\r
+{\r
+    if (!strcmp(name, "CloseOnExit"))\r
+       return 2;  /* maps to FORCE_ON after painful rearrangement :-( */\r
+    return def;\r
+}\r
+\r
+char *x_get_default(const char *key)\r
+{\r
+    return NULL;                      /* this is a stub */\r
+}\r
+\r
+static void commonfatalbox(char *p, va_list ap)\r
+{\r
+    char errorbuf[2048];\r
+    NSAlert *alert;\r
+\r
+    /*\r
+     * We may have come here because we ran out of memory, in which\r
+     * case it's entirely likely that that further memory\r
+     * allocations will fail. So (a) we use vsnprintf to format the\r
+     * error message rather than the usual dupvprintf; and (b) we\r
+     * have a fallback way to get the message out via stderr if\r
+     * even creating an NSAlert fails.\r
+     */\r
+    vsnprintf(errorbuf, lenof(errorbuf), p, ap);\r
+\r
+    alert = [NSAlert alloc];\r
+    if (!alert) {\r
+       fprintf(stderr, "fatal error (and NSAlert failed): %s\n", errorbuf);\r
+    } else {\r
+       alert = [[alert init] autorelease];\r
+       [alert addButtonWithTitle:@"Terminate"];\r
+       [alert setInformativeText:[NSString stringWithCString:errorbuf]];\r
+       [alert runModal];\r
+    }\r
+    exit(1);\r
+}\r
+\r
+void fatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    va_start(ap, p);\r
+    commonfatalbox(p, ap);\r
+    va_end(ap);\r
+}\r
+\r
+void modalfatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    va_start(ap, p);\r
+    commonfatalbox(p, ap);\r
+    va_end(ap);\r
+}\r
+\r
+void cmdline_error(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "%s: ", appname);\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    exit(1);\r
+}\r
+\r
+/*\r
+ * Clean up and exit.\r
+ */\r
+void cleanup_exit(int code)\r
+{\r
+    /*\r
+     * Clean up.\r
+     */\r
+    sk_cleanup();\r
+    random_save_seed();\r
+    exit(code);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Tiny extension to NSMenuItem which carries a payload of a `void\r
+ * *', allowing several menu items to invoke the same message but\r
+ * pass different data through it.\r
+ */\r
+@interface DataMenuItem : NSMenuItem\r
+{\r
+    void *payload;\r
+}\r
+- (void)setPayload:(void *)d;\r
+- (void *)getPayload;\r
+@end\r
+@implementation DataMenuItem\r
+- (void)setPayload:(void *)d\r
+{\r
+    payload = d;\r
+}\r
+- (void *)getPayload\r
+{\r
+    return payload;\r
+}\r
+@end\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Utility routines for constructing OS X menus.\r
+ */\r
+\r
+NSMenu *newmenu(const char *title)\r
+{\r
+    return [[[NSMenu allocWithZone:[NSMenu menuZone]]\r
+            initWithTitle:[NSString stringWithCString:title]]\r
+           autorelease];\r
+}\r
+\r
+NSMenu *newsubmenu(NSMenu *parent, const char *title)\r
+{\r
+    NSMenuItem *item;\r
+    NSMenu *child;\r
+\r
+    item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]]\r
+            initWithTitle:[NSString stringWithCString:title]\r
+            action:NULL\r
+            keyEquivalent:@""]\r
+           autorelease];\r
+    child = newmenu(title);\r
+    [item setEnabled:YES];\r
+    [item setSubmenu:child];\r
+    [parent addItem:item];\r
+    return child;\r
+}\r
+\r
+id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title,\r
+              const char *key, id target, SEL action)\r
+{\r
+    unsigned mask = NSCommandKeyMask;\r
+\r
+    if (key[strcspn(key, "-")]) {\r
+       while (*key && *key != '-') {\r
+           int c = tolower((unsigned char)*key);\r
+           if (c == 's') {\r
+               mask |= NSShiftKeyMask;\r
+           } else if (c == 'o' || c == 'a') {\r
+               mask |= NSAlternateKeyMask;\r
+           }\r
+           key++;\r
+       }\r
+       if (*key)\r
+           key++;\r
+    }\r
+\r
+    item = [[item initWithTitle:[NSString stringWithCString:title]\r
+            action:NULL\r
+            keyEquivalent:[NSString stringWithCString:key]]\r
+           autorelease];\r
+\r
+    if (*key)\r
+       [item setKeyEquivalentModifierMask: mask];\r
+\r
+    [item setEnabled:YES];\r
+    [item setTarget:target];\r
+    [item setAction:action];\r
+\r
+    [parent addItem:item];\r
+\r
+    return item;\r
+}\r
+\r
+NSMenuItem *newitem(NSMenu *parent, char *title, char *key,\r
+                   id target, SEL action)\r
+{\r
+    return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]],\r
+                      parent, title, key, target, action);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * AppController: the object which receives the messages from all\r
+ * menu selections that aren't standard OS X functions.\r
+ */\r
+@implementation AppController\r
+\r
+- (id)init\r
+{\r
+    self = [super init];\r
+    timer = NULL;\r
+    return self;\r
+}\r
+\r
+- (void)newTerminal:(id)sender\r
+{\r
+    id win;\r
+    Config cfg;\r
+\r
+    do_defaults(NULL, &cfg);\r
+\r
+    cfg.protocol = -1;                /* PROT_TERMINAL */\r
+\r
+    win = [[SessionWindow alloc] initWithConfig:cfg];\r
+    [win makeKeyAndOrderFront:self];\r
+}\r
+\r
+- (void)newSessionConfig:(id)sender\r
+{\r
+    id win;\r
+    Config cfg;\r
+\r
+    do_defaults(NULL, &cfg);\r
+\r
+    win = [[ConfigWindow alloc] initWithConfig:cfg];\r
+    [win makeKeyAndOrderFront:self];\r
+}\r
+\r
+- (void)newSessionWithConfig:(id)vdata\r
+{\r
+    id win;\r
+    Config cfg;\r
+    NSData *data = (NSData *)vdata;\r
+\r
+    assert([data length] == sizeof(cfg));\r
+    [data getBytes:&cfg];\r
+\r
+    win = [[SessionWindow alloc] initWithConfig:cfg];\r
+    [win makeKeyAndOrderFront:self];\r
+}\r
+\r
+- (NSMenu *)applicationDockMenu:(NSApplication *)sender\r
+{\r
+    NSMenu *menu = newmenu("Dock Menu");\r
+    /*\r
+     * FIXME: Add some useful things to this, probably including\r
+     * the saved session list.\r
+     */\r
+    return menu;\r
+}\r
+\r
+- (void)timerFired:(id)sender\r
+{\r
+    long now, next;\r
+\r
+    assert(sender == timer);\r
+\r
+    /* `sender' is the timer itself, so its userInfo is an NSNumber. */\r
+    now = [(NSNumber *)[sender userInfo] longValue];\r
+\r
+    [sender invalidate];\r
+\r
+    timer = NULL;\r
+\r
+    if (run_timers(now, &next))\r
+       [self setTimer:next];\r
+}\r
+\r
+- (void)setTimer:(long)next\r
+{\r
+    long interval = next - GETTICKCOUNT();\r
+    float finterval;\r
+\r
+    if (interval <= 0)\r
+       interval = 1;                  /* just in case */\r
+\r
+    finterval = interval / (float)TICKSPERSEC;\r
+\r
+    if (timer) {\r
+       [timer invalidate];\r
+    }\r
+\r
+    timer = [NSTimer scheduledTimerWithTimeInterval:finterval\r
+            target:self selector:@selector(timerFired:)\r
+            userInfo:[NSNumber numberWithLong:next] repeats:NO];\r
+}\r
+\r
+@end\r
+\r
+void timer_change_notify(long next)\r
+{\r
+    [controller setTimer:next];\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Annoyingly, it looks as if I have to actually subclass\r
+ * NSApplication if I want to catch NSApplicationDefined events. So\r
+ * here goes.\r
+ */\r
+@interface MyApplication : NSApplication\r
+{\r
+}\r
+@end\r
+@implementation MyApplication\r
+- (void)sendEvent:(NSEvent *)ev\r
+{\r
+    if ([ev type] == NSApplicationDefined)\r
+       osxsel_process_results();\r
+\r
+    [super sendEvent:ev];\r
+}    \r
+@end\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Main program. Constructs the menus and runs the application.\r
+ */\r
+int main(int argc, char **argv)\r
+{\r
+    NSAutoreleasePool *pool;\r
+    NSMenu *menu;\r
+    NSMenuItem *item;\r
+    NSImage *icon;\r
+\r
+    pool = [[NSAutoreleasePool alloc] init];\r
+\r
+    icon = [NSImage imageNamed:@"NSApplicationIcon"];\r
+    [MyApplication sharedApplication];\r
+    [NSApp setApplicationIconImage:icon];\r
+\r
+    controller = [[[AppController alloc] init] autorelease];\r
+    [NSApp setDelegate:controller];\r
+\r
+    [NSApp setMainMenu: newmenu("Main Menu")];\r
+\r
+    menu = newsubmenu([NSApp mainMenu], "Apple Menu");\r
+    [NSApp setServicesMenu:newsubmenu(menu, "Services")];\r
+    [menu addItem:[NSMenuItem separatorItem]];\r
+    item = newitem(menu, "Hide PuTTY", "h", NSApp, @selector(hide:));\r
+    item = newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:));\r
+    item = newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:));\r
+    [menu addItem:[NSMenuItem separatorItem]];\r
+    item = newitem(menu, "Quit", "q", NSApp, @selector(terminate:));\r
+    [NSApp setAppleMenu: menu];\r
+\r
+    menu = newsubmenu([NSApp mainMenu], "File");\r
+    item = newitem(menu, "New", "n", NULL, @selector(newSessionConfig:));\r
+    item = newitem(menu, "New Terminal", "t", NULL, @selector(newTerminal:));\r
+    item = newitem(menu, "Close", "w", NULL, @selector(performClose:));\r
+\r
+    menu = newsubmenu([NSApp mainMenu], "Window");\r
+    [NSApp setWindowsMenu: menu];\r
+    item = newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:));\r
+\r
+//    menu = newsubmenu([NSApp mainMenu], "Help");\r
+//    item = newitem(menu, "PuTTY Help", "?", NSApp, @selector(showHelp:));\r
+\r
+    /*\r
+     * Start up the sub-thread doing select().\r
+     */\r
+    osxsel_init();\r
+\r
+    /*\r
+     * Start up networking.\r
+     */\r
+    sk_init();\r
+\r
+    /*\r
+     * FIXME: To make initial debugging more convenient I'm going\r
+     * to start by opening a session window unconditionally. This\r
+     * will probably change later on.\r
+     */\r
+    [controller newSessionConfig:nil];\r
+\r
+    [NSApp run];\r
+    [pool release];\r
+\r
+    return 0;\r
+}\r
diff --git a/putty/MACOSX/OSXSEL.M b/putty/MACOSX/OSXSEL.M
new file mode 100644 (file)
index 0000000..8a0d3f7
--- /dev/null
@@ -0,0 +1,308 @@
+/*\r
+ * osxsel.m: OS X implementation of the front end interface to uxsel.\r
+ */\r
+\r
+#import <Cocoa/Cocoa.h>\r
+#include <unistd.h>\r
+#include "putty.h"\r
+#include "osxclass.h"\r
+\r
+/*\r
+ * The unofficial Cocoa FAQ at\r
+ *\r
+ *   http://www.alastairs-place.net/cocoa/faq.txt\r
+ * \r
+ * says that Cocoa has the native ability to be given an fd and\r
+ * tell you when it becomes readable, but cannot tell you when it\r
+ * becomes _writable_. This is unacceptable to PuTTY, which depends\r
+ * for correct functioning on being told both. Therefore, I can't\r
+ * use the Cocoa native mechanism.\r
+ * \r
+ * Instead, I'm going to resort to threads. I start a second thread\r
+ * whose job is to do selects. At the termination of every select,\r
+ * it posts a Cocoa event into the main thread's event queue, so\r
+ * that the main thread gets select results interleaved with other\r
+ * GUI operations. Communication from the main thread _to_ the\r
+ * select thread is performed by writing to a pipe whose other end\r
+ * is one of the file descriptors being selected on. (This is the\r
+ * only sensible way, because we have to be able to interrupt a\r
+ * select in order to provide a new fd list.)\r
+ */\r
+\r
+/*\r
+ * In more detail, the select thread must:\r
+ * \r
+ *  - start off by listening to _just_ the pipe, waiting to be told\r
+ *    to begin a select.\r
+ * \r
+ *  - when it receives the `start' command, it should read the\r
+ *    shared uxsel data (which is protected by a mutex), set up its\r
+ *    select, and begin it.\r
+ * \r
+ *  - when the select terminates, it should write the results\r
+ *    (perhaps minus the inter-thread pipe if it's there) into\r
+ *    shared memory and dispatch a GUI event to let the main thread\r
+ *    know.\r
+ * \r
+ *  - the main thread will then think about it, do some processing,\r
+ *    and _then_ send a command saying `now restart select'. Before\r
+ *    sending that command it might easily have tinkered with the\r
+ *    uxsel structures, which is why it waited before sending it.\r
+ * \r
+ *  - EOF on the inter-thread pipe, of course, means the process\r
+ *    has finished completely, so the select thread terminates.\r
+ * \r
+ *  - The main thread may wish to adjust the uxsel settings in the\r
+ *    middle of a select. In this situation it first writes the new\r
+ *    data to the shared memory area, then notifies the select\r
+ *    thread by writing to the inter-thread pipe.\r
+ * \r
+ * So the upshot is that the sequence of operations performed in\r
+ * the select thread must be:\r
+ * \r
+ *  - read a byte from the pipe (which may block)\r
+ * \r
+ *  - read the shared uxsel data and perform a select\r
+ * \r
+ *  - notify the main thread of interesting select results (if any)\r
+ * \r
+ *  - loop round again from the top.\r
+ * \r
+ * This is sufficient. Notifying the select thread asynchronously\r
+ * by writing to the pipe will cause its select to terminate and\r
+ * another to begin immediately without blocking. If the select\r
+ * thread's select terminates due to network data, its subsequent\r
+ * pipe read will block until the main thread is ready to let it\r
+ * loose again.\r
+ */\r
+\r
+static int osxsel_pipe[2];\r
+\r
+static NSLock *osxsel_inlock;\r
+static fd_set osxsel_rfds_in;\r
+static fd_set osxsel_wfds_in;\r
+static fd_set osxsel_xfds_in;\r
+static int osxsel_inmax;\r
+\r
+static NSLock *osxsel_outlock;\r
+static fd_set osxsel_rfds_out;\r
+static fd_set osxsel_wfds_out;\r
+static fd_set osxsel_xfds_out;\r
+static int osxsel_outmax;\r
+\r
+static int inhibit_start_select;\r
+\r
+/*\r
+ * NSThread requires an object method as its thread procedure, so\r
+ * here I define a trivial holding class.\r
+ */\r
+@class OSXSel;\r
+@interface OSXSel : NSObject\r
+{\r
+}\r
+- (void)runThread:(id)arg;\r
+@end\r
+@implementation OSXSel\r
+- (void)runThread:(id)arg\r
+{\r
+    char c;\r
+    fd_set r, w, x;\r
+    int n, ret;\r
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r
+\r
+    while (1) {\r
+       /*\r
+        * Read one byte from the pipe.\r
+        */\r
+       ret = read(osxsel_pipe[0], &c, 1);\r
+\r
+       if (ret <= 0)\r
+           return;                    /* terminate the thread */\r
+\r
+       /*\r
+        * Now set up the select data.\r
+        */\r
+       [osxsel_inlock lock];\r
+       memcpy(&r, &osxsel_rfds_in, sizeof(fd_set));\r
+       memcpy(&w, &osxsel_wfds_in, sizeof(fd_set));\r
+       memcpy(&x, &osxsel_xfds_in, sizeof(fd_set));\r
+       n = osxsel_inmax;\r
+       [osxsel_inlock unlock];\r
+       FD_SET(osxsel_pipe[0], &r);\r
+       if (n < osxsel_pipe[0]+1)\r
+           n = osxsel_pipe[0]+1;\r
+\r
+       /*\r
+        * Perform the select.\r
+        */\r
+       ret = select(n, &r, &w, &x, NULL);\r
+\r
+       /*\r
+        * Detect the one special case in which the only\r
+        * interesting fd was the inter-thread pipe. In that\r
+        * situation only we are interested - the main thread will\r
+        * not be!\r
+        */\r
+       if (ret == 1 && FD_ISSET(osxsel_pipe[0], &r))\r
+           continue;                  /* just loop round again */\r
+\r
+       /*\r
+        * Write the select results to shared data.\r
+        * \r
+        * I _think_ we don't need this data to be lock-protected:\r
+        * it won't be read by the main thread until after we send\r
+        * a message indicating that we've finished writing it, and\r
+        * we won't start another select (hence potentially writing\r
+        * it again) until the main thread notifies us in return.\r
+        * \r
+        * However, I'm scared of multithreading and not totally\r
+        * convinced of my reasoning, so I'm going to lock it\r
+        * anyway.\r
+        */\r
+       [osxsel_outlock lock];\r
+       memcpy(&osxsel_rfds_out, &r, sizeof(fd_set));\r
+       memcpy(&osxsel_wfds_out, &w, sizeof(fd_set));\r
+       memcpy(&osxsel_xfds_out, &x, sizeof(fd_set));\r
+       osxsel_outmax = n;\r
+       [osxsel_outlock unlock];\r
+\r
+       /*\r
+        * Post a message to the main thread's message queue\r
+        * telling it that select data is available.\r
+        */\r
+       [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined\r
+                         location:NSMakePoint(0,0)\r
+                         modifierFlags:0\r
+                         timestamp:0\r
+                         windowNumber:0\r
+                         context:nil\r
+                         subtype:0\r
+                         data1:0\r
+                         data2:0]\r
+        atStart:NO];\r
+    }\r
+\r
+    [pool release];\r
+}\r
+@end\r
+\r
+void osxsel_init(void)\r
+{\r
+    uxsel_init();\r
+\r
+    if (pipe(osxsel_pipe) < 0) {\r
+       fatalbox("Unable to set up inter-thread pipe for select");\r
+    }\r
+    [NSThread detachNewThreadSelector:@selector(runThread:)\r
+       toTarget:[[[OSXSel alloc] init] retain] withObject:nil];\r
+    /*\r
+     * Also initialise (i.e. clear) the input fd_sets. Need not\r
+     * start a select just yet - the select thread will block until\r
+     * we have at least one fd for it!\r
+     */\r
+    FD_ZERO(&osxsel_rfds_in);\r
+    FD_ZERO(&osxsel_wfds_in);\r
+    FD_ZERO(&osxsel_xfds_in);\r
+    osxsel_inmax = 0;\r
+    /*\r
+     * Initialise the mutex locks used to protect the data passed\r
+     * between threads.\r
+     */\r
+    osxsel_inlock = [[[NSLock alloc] init] retain];\r
+    osxsel_outlock = [[[NSLock alloc] init] retain];\r
+}\r
+\r
+static void osxsel_start_select(void)\r
+{\r
+    char c = 'g';                     /* for `Go!' :-) but it's never used */\r
+\r
+    if (!inhibit_start_select)\r
+       write(osxsel_pipe[1], &c, 1);\r
+}\r
+\r
+int uxsel_input_add(int fd, int rwx)\r
+{\r
+    /*\r
+     * Add the new fd to the appropriate input fd_sets, then write\r
+     * to the inter-thread pipe.\r
+     */\r
+    [osxsel_inlock lock];\r
+    if (rwx & 1)\r
+       FD_SET(fd, &osxsel_rfds_in);\r
+    else\r
+       FD_CLR(fd, &osxsel_rfds_in);\r
+    if (rwx & 2)\r
+       FD_SET(fd, &osxsel_wfds_in);\r
+    else\r
+       FD_CLR(fd, &osxsel_wfds_in);\r
+    if (rwx & 4)\r
+       FD_SET(fd, &osxsel_xfds_in);\r
+    else\r
+       FD_CLR(fd, &osxsel_xfds_in);\r
+    if (osxsel_inmax < fd+1)\r
+       osxsel_inmax = fd+1;\r
+    [osxsel_inlock unlock];\r
+    osxsel_start_select();\r
+\r
+    /*\r
+     * We must return an `id' which will be passed back to us at\r
+     * the time of uxsel_input_remove. Since we have no need to\r
+     * store ids in that sense, we might as well go with the fd\r
+     * itself.\r
+     */\r
+    return fd;\r
+}\r
+\r
+void uxsel_input_remove(int id)\r
+{\r
+    /*\r
+     * Remove the fd from all the input fd_sets. In this\r
+     * implementation, the simplest way to do that is to call\r
+     * uxsel_input_add with rwx==0!\r
+     */\r
+    uxsel_input_add(id, 0);\r
+}\r
+\r
+/*\r
+ * Function called in the main thread to process results. It will\r
+ * have to read the output fd_sets, go through them, call back to\r
+ * uxsel with the results, and then write to the inter-thread pipe.\r
+ * \r
+ * This function will have to be called from an event handler in\r
+ * osxmain.m, which will therefore necessarily contain a small part\r
+ * of this mechanism (along with calling osxsel_init).\r
+ */\r
+void osxsel_process_results(void)\r
+{\r
+    int i;\r
+\r
+    /*\r
+     * We must write to the pipe to start a fresh select _even if_\r
+     * there were no changes. So for efficiency, we set a flag here\r
+     * which inhibits uxsel_input_{add,remove} from writing to the\r
+     * pipe; then once we finish processing, we clear the flag\r
+     * again and write a single byte ourselves. It's cleaner,\r
+     * because it wakes up the select thread fewer times.\r
+     */\r
+    inhibit_start_select = TRUE;\r
+\r
+    [osxsel_outlock lock];\r
+\r
+    for (i = 0; i < osxsel_outmax; i++) {\r
+       if (FD_ISSET(i, &osxsel_xfds_out))\r
+           select_result(i, 4);\r
+    }\r
+    for (i = 0; i < osxsel_outmax; i++) {\r
+       if (FD_ISSET(i, &osxsel_rfds_out))\r
+           select_result(i, 1);\r
+    }\r
+    for (i = 0; i < osxsel_outmax; i++) {\r
+       if (FD_ISSET(i, &osxsel_wfds_out))\r
+           select_result(i, 2);\r
+    }\r
+\r
+    [osxsel_outlock unlock];\r
+\r
+    inhibit_start_select = FALSE;\r
+    osxsel_start_select();\r
+}\r
diff --git a/putty/MACOSX/OSXWIN.M b/putty/MACOSX/OSXWIN.M
new file mode 100644 (file)
index 0000000..9de1e08
--- /dev/null
@@ -0,0 +1,1227 @@
+/*\r
+ * osxwin.m: code to manage a session window in Mac OS X PuTTY.\r
+ */\r
+\r
+#import <Cocoa/Cocoa.h>\r
+#include "putty.h"\r
+#include "terminal.h"\r
+#include "osxclass.h"\r
+\r
+/* Colours come in two flavours: configurable, and xterm-extended. */\r
+#define NCFGCOLOURS (lenof(((Config *)0)->colours))\r
+#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */\r
+#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)\r
+\r
+/*\r
+ * The key component of the per-session data is the SessionWindow\r
+ * class. A pointer to this is used as the frontend handle, to be\r
+ * passed to all the platform-independent subsystems that require\r
+ * one.\r
+ */\r
+\r
+@interface TerminalView : NSImageView\r
+{\r
+    NSFont *font;\r
+    NSImage *image;\r
+    Terminal *term;\r
+    Config cfg;\r
+    NSColor *colours[NALLCOLOURS];\r
+    float fw, fasc, fdesc, fh;\r
+}\r
+- (void)drawStartFinish:(BOOL)start;\r
+- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b;\r
+- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y\r
+    attr:(unsigned long)attr lattr:(int)lattr;\r
+@end\r
+\r
+@implementation TerminalView\r
+- (BOOL)isFlipped\r
+{\r
+    return YES;\r
+}\r
+- (id)initWithTerminal:(Terminal *)aTerm config:(Config)aCfg\r
+{\r
+    float w, h;\r
+\r
+    self = [self initWithFrame:NSMakeRect(0,0,100,100)];\r
+\r
+    term = aTerm;\r
+    cfg = aCfg;\r
+\r
+    /*\r
+     * Initialise the fonts we're going to use.\r
+     * \r
+     * FIXME: for the moment I'm sticking with exactly one default font.\r
+     */\r
+    font = [NSFont userFixedPitchFontOfSize:0];\r
+\r
+    /*\r
+     * Now determine the size of the primary font.\r
+     * \r
+     * FIXME: If we have multiple fonts, we may need to set fasc\r
+     * and fdesc to the _maximum_ asc and desc out of all the\r
+     * fonts, _before_ adding them together to get fh.\r
+     */\r
+    fw = [font widthOfString:@"A"];\r
+    fasc = [font ascender];\r
+    fdesc = -[font descender];\r
+    fh = fasc + fdesc;\r
+    fh = (int)fh + (fh > (int)fh);     /* round up, ickily */\r
+\r
+    /*\r
+     * Use this to figure out the size of the terminal view.\r
+     */\r
+    w = fw * term->cols;\r
+    h = fh * term->rows;\r
+\r
+    /*\r
+     * And set our size and subimage.\r
+     */\r
+    image = [[NSImage alloc] initWithSize:NSMakeSize(w,h)];\r
+    [image setFlipped:YES];\r
+    [self setImage:image];\r
+    [self setFrame:NSMakeRect(0,0,w,h)];\r
+\r
+    term_invalidate(term);\r
+\r
+    return self;\r
+}\r
+- (void)drawStartFinish:(BOOL)start\r
+{\r
+    if (start)\r
+       [image lockFocus];\r
+    else\r
+       [image unlockFocus];\r
+}\r
+- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y\r
+    attr:(unsigned long)attr lattr:(int)lattr\r
+{\r
+    int nfg, nbg, rlen, widefactor;\r
+    float ox, oy, tw, th;\r
+    NSDictionary *attrdict;\r
+\r
+    /* FIXME: TATTR_COMBINING */\r
+\r
+    nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);\r
+    nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);\r
+    if (attr & ATTR_REVERSE) {\r
+       int t = nfg;\r
+       nfg = nbg;\r
+       nbg = t;\r
+    }\r
+    if (cfg.bold_colour && (attr & ATTR_BOLD)) {\r
+       if (nfg < 16) nfg |= 8;\r
+       else if (nfg >= 256) nfg |= 1;\r
+    }\r
+    if (cfg.bold_colour && (attr & ATTR_BLINK)) {\r
+       if (nbg < 16) nbg |= 8;\r
+       else if (nbg >= 256) nbg |= 1;\r
+    }\r
+    if (attr & TATTR_ACTCURS) {\r
+       nfg = 260;\r
+       nbg = 261;\r
+    }\r
+\r
+    if (attr & ATTR_WIDE) {\r
+       widefactor = 2;\r
+       /* FIXME: what do we actually have to do about wide characters? */\r
+    } else {\r
+       widefactor = 1;\r
+    }\r
+\r
+    /* FIXME: ATTR_BOLD without cfg.bold_colour */\r
+\r
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
+       x *= 2;\r
+       if (x >= term->cols)\r
+           return;\r
+       if (x + len*2*widefactor > term->cols)\r
+           len = (term->cols-x)/2/widefactor;/* trim to LH half */\r
+       rlen = len * 2;\r
+    } else\r
+       rlen = len;\r
+\r
+    /* FIXME: how do we actually implement double-{width,height} lattrs? */\r
+\r
+    ox = x * fw;\r
+    oy = y * fh;\r
+    tw = rlen * widefactor * fw;\r
+    th = fh;\r
+\r
+    /*\r
+     * Set the clipping rectangle.\r
+     */\r
+    [[NSGraphicsContext currentContext] saveGraphicsState];\r
+    [NSBezierPath clipRect:NSMakeRect(ox, oy, tw, th)];\r
+\r
+    attrdict = [NSDictionary dictionaryWithObjectsAndKeys:\r
+               colours[nfg], NSForegroundColorAttributeName,\r
+               colours[nbg], NSBackgroundColorAttributeName,\r
+               font, NSFontAttributeName, nil];\r
+\r
+    /*\r
+     * Create an NSString and draw it.\r
+     * \r
+     * Annoyingly, although our input is wchar_t which is four\r
+     * bytes wide on OS X and terminal.c supports 32-bit Unicode,\r
+     * we must convert into the two-byte type `unichar' to store in\r
+     * NSString, so we lose display capability for extra-BMP stuff\r
+     * at this point.\r
+     */\r
+    {\r
+       NSString *string;\r
+       unichar *utext;\r
+       int i;\r
+\r
+       utext = snewn(len, unichar);\r
+       for (i = 0; i < len; i++)\r
+           utext[i] = (text[i] >= 0x10000 ? 0xFFFD : text[i]);\r
+\r
+       string = [NSString stringWithCharacters:utext length:len];\r
+       [string drawAtPoint:NSMakePoint(ox, oy) withAttributes:attrdict];\r
+\r
+       sfree(utext);\r
+    }\r
+\r
+    /*\r
+     * Restore the graphics state from before the clipRect: call.\r
+     */\r
+    [[NSGraphicsContext currentContext] restoreGraphicsState];\r
+\r
+    /*\r
+     * And flag this area as needing display.\r
+     */\r
+    [self setNeedsDisplayInRect:NSMakeRect(ox, oy, tw, th)];\r
+}\r
+\r
+- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b\r
+{\r
+    assert(n >= 0 && n < lenof(colours));\r
+    colours[n] = [[NSColor colorWithDeviceRed:r green:g blue:b alpha:1.0]\r
+                 retain];\r
+}\r
+@end\r
+\r
+@implementation SessionWindow\r
+- (id)initWithConfig:(Config)aCfg\r
+{\r
+    NSRect rect = { {0,0}, {0,0} };\r
+\r
+    alert_ctx = NULL;\r
+\r
+    cfg = aCfg;                               /* structure copy */\r
+\r
+    init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override,\r
+            CS_UTF8, cfg.vtmode);\r
+    term = term_init(&cfg, &ucsdata, self);\r
+    logctx = log_init(self, &cfg);\r
+    term_provide_logctx(term, logctx);\r
+    term_size(term, cfg.height, cfg.width, cfg.savelines);\r
+\r
+    termview = [[[TerminalView alloc] initWithTerminal:term config:cfg]\r
+               autorelease];\r
+\r
+    /*\r
+     * Now work out the size of the window.\r
+     */\r
+    rect = [termview frame];\r
+    rect.origin = NSMakePoint(0,0);\r
+    rect.size.width += 2 * cfg.window_border;\r
+    rect.size.height += 2 * cfg.window_border;\r
+\r
+    /*\r
+     * Set up a backend.\r
+     */\r
+    back = backend_from_proto(cfg.protocol);\r
+    if (!back)\r
+       back = &pty_backend;\r
+\r
+    {\r
+       const char *error;\r
+       char *realhost = NULL;\r
+       error = back->init(self, &backhandle, &cfg, cfg.host, cfg.port,\r
+                          &realhost, cfg.tcp_nodelay, cfg.tcp_keepalives);\r
+       if (error) {\r
+           fatalbox("%s\n", error);   /* FIXME: connection_fatal at worst */\r
+       }\r
+\r
+       if (realhost)\r
+           sfree(realhost);           /* FIXME: do something with this */\r
+    }\r
+    back->provide_logctx(backhandle, logctx);\r
+\r
+    /*\r
+     * Create a line discipline. (This must be done after creating\r
+     * the terminal _and_ the backend, since it needs to be passed\r
+     * pointers to both.)\r
+     */\r
+    ldisc = ldisc_create(&cfg, term, back, backhandle, self);\r
+\r
+    /*\r
+     * FIXME: Set up a scrollbar.\r
+     */\r
+\r
+    self = [super initWithContentRect:rect\r
+           styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |\r
+                      NSClosableWindowMask)\r
+           backing:NSBackingStoreBuffered\r
+           defer:YES];\r
+    [self setTitle:@"PuTTY"];\r
+\r
+    [self setIgnoresMouseEvents:NO];\r
+\r
+    /*\r
+     * Put the terminal view in the window.\r
+     */\r
+    rect = [termview frame];\r
+    rect.origin = NSMakePoint(cfg.window_border, cfg.window_border);\r
+    [termview setFrame:rect];\r
+    [[self contentView] addSubview:termview];\r
+\r
+    /*\r
+     * Set up the colour palette.\r
+     */\r
+    palette_reset(self);\r
+\r
+    /*\r
+     * FIXME: Only the _first_ document window should be centred.\r
+     * The subsequent ones should appear down and to the right of\r
+     * it, probably using the cascade function provided by Cocoa.\r
+     * Also we're apparently required by the HIG to remember and\r
+     * reuse previous positions of windows, although I'm not sure\r
+     * how that works if the user opens more than one of the same\r
+     * session type.\r
+     */\r
+    [self center];                    /* :-) */\r
+\r
+    exited = FALSE;\r
+\r
+    return self;\r
+}\r
+\r
+- (void)dealloc\r
+{\r
+    /*\r
+     * FIXME: Here we must deallocate all sorts of stuff: the\r
+     * terminal, the backend, the ldisc, the logctx, you name it.\r
+     * Do so.\r
+     */\r
+    sfree(alert_ctx);\r
+    if (back)\r
+       back->free(backhandle);\r
+    if (ldisc)\r
+       ldisc_free(ldisc);\r
+    /* ldisc must be freed before term, since ldisc_free expects term\r
+     * still to be around. */\r
+    if (logctx)\r
+       log_free(logctx);\r
+    if (term)\r
+       term_free(term);\r
+    [super dealloc];\r
+}\r
+\r
+- (void)drawStartFinish:(BOOL)start\r
+{\r
+    [termview drawStartFinish:start];\r
+}\r
+\r
+- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b\r
+{\r
+    [termview setColour:n r:r g:g b:b];\r
+}\r
+\r
+- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y\r
+    attr:(unsigned long)attr lattr:(int)lattr\r
+{\r
+    /* Pass this straight on to the TerminalView. */\r
+    [termview doText:text len:len x:x y:y attr:attr lattr:lattr];\r
+}\r
+\r
+- (Config *)cfg\r
+{\r
+    return &cfg;\r
+}\r
+\r
+- (void)keyDown:(NSEvent *)ev\r
+{\r
+    NSString *s = [ev characters];\r
+    int i;\r
+    int n = [s length], c = [s characterAtIndex:0], m = [ev modifierFlags];\r
+    int cm = [[ev charactersIgnoringModifiers] characterAtIndex:0];\r
+    wchar_t output[32];\r
+    char coutput[32];\r
+    int use_coutput = FALSE, special = FALSE, start, end;\r
+\r
+//printf("n=%d c=U+%04x cm=U+%04x m=%08x\n", n, c, cm, m);\r
+\r
+    /*\r
+     * FIXME: Alt+numberpad codes.\r
+     */\r
+\r
+    /*\r
+     * Shift and Ctrl with PageUp/PageDown for scrollback.\r
+     */\r
+    if (n == 1 && c == NSPageUpFunctionKey && (m & NSShiftKeyMask)) {\r
+       term_scroll(term, 0, -term->rows/2);\r
+       return;\r
+    }\r
+    if (n == 1 && c == NSPageUpFunctionKey && (m & NSControlKeyMask)) {\r
+       term_scroll(term, 0, -1);\r
+       return;\r
+    }\r
+    if (n == 1 && c == NSPageDownFunctionKey && (m & NSShiftKeyMask)) {\r
+       term_scroll(term, 0, +term->rows/2);\r
+       return;\r
+    }\r
+    if (n == 1 && c == NSPageDownFunctionKey && (m & NSControlKeyMask)) {\r
+       term_scroll(term, 0, +1);\r
+       return;\r
+    }\r
+\r
+    /*\r
+     * FIXME: Shift-Ins for paste? Or is that not Maccy enough?\r
+     */\r
+\r
+    /*\r
+     * FIXME: Alt (Option? Command?) prefix in general.\r
+     * \r
+     * (Note that Alt-Shift-thing will work just by looking at\r
+     * charactersIgnoringModifiers; but Alt-Ctrl-thing will need\r
+     * processing properly, and Alt-as-in-Option won't happen at\r
+     * all. Hmmm.)\r
+     * \r
+     * (Note also that we need to be able to override menu key\r
+     * equivalents before this is particularly useful.)\r
+     */\r
+    start = 1;\r
+    end = start;\r
+\r
+    /*\r
+     * Ctrl-` is the same as Ctrl-\, unless we already have a\r
+     * better idea.\r
+     */\r
+    if ((m & NSControlKeyMask) && n == 1 && cm == '`' && c == '`') {\r
+       output[1] = '\x1c';\r
+       end = 2;\r
+    }\r
+\r
+    /* We handle Return ourselves, because it needs to be flagged as\r
+     * special to ldisc. */\r
+    if (n == 1 && c == '\015') {\r
+       coutput[1] = '\015';\r
+       use_coutput = TRUE;\r
+       end = 2;\r
+       special = TRUE;\r
+    }\r
+\r
+    /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */\r
+    if (n == 1 && (m & NSControlKeyMask) && (m & NSShiftKeyMask) &&\r
+       cm == ' ') {\r
+       output[1] = '\240';\r
+       end = 2;\r
+    }\r
+\r
+    /* Control-2, Control-Space and Control-@ are all NUL. */\r
+    if ((m & NSControlKeyMask) && n == 1 &&\r
+       (cm == '2' || cm == '@' || cm == ' ') && c == cm) {\r
+       output[1] = '\0';\r
+       end = 2;\r
+    }\r
+\r
+    /* We don't let MacOS tell us what Backspace is! We know better. */\r
+    if (cm == 0x7F && !(m & NSShiftKeyMask)) {\r
+       coutput[1] = cfg.bksp_is_delete ? '\x7F' : '\x08';\r
+       end = 2;\r
+       use_coutput = special = TRUE;\r
+    }\r
+    /* For Shift Backspace, do opposite of what is configured. */\r
+    if (cm == 0x7F && (m & NSShiftKeyMask)) {\r
+       coutput[1] = cfg.bksp_is_delete ? '\x08' : '\x7F';\r
+       end = 2;\r
+       use_coutput = special = TRUE;\r
+    }\r
+\r
+    /* Shift-Tab is ESC [ Z. Oddly, this combination generates ^Y by\r
+     * default on MacOS! */\r
+    if (cm == 0x19 && (m & NSShiftKeyMask) && !(m & NSControlKeyMask)) {\r
+       end = 1;\r
+       output[end++] = '\033';\r
+       output[end++] = '[';\r
+       output[end++] = 'Z';\r
+    }\r
+\r
+    /*\r
+     * NetHack keypad mode.\r
+     */\r
+    if (cfg.nethack_keypad && (m & NSNumericPadKeyMask)) {\r
+       wchar_t *keys = NULL;\r
+       switch (cm) {\r
+         case '1': keys = L"bB"; break;\r
+         case '2': keys = L"jJ"; break;\r
+         case '3': keys = L"nN"; break;\r
+         case '4': keys = L"hH"; break;\r
+         case '5': keys = L".."; break;\r
+         case '6': keys = L"lL"; break;\r
+         case '7': keys = L"yY"; break;\r
+         case '8': keys = L"kK"; break;\r
+         case '9': keys = L"uU"; break;\r
+       }\r
+       if (keys) {\r
+           end = 2;\r
+           if (m & NSShiftKeyMask)\r
+               output[1] = keys[1];\r
+           else\r
+               output[1] = keys[0];\r
+           goto done;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Application keypad mode.\r
+     */\r
+    if (term->app_keypad_keys && !cfg.no_applic_k &&\r
+       (m & NSNumericPadKeyMask)) {\r
+       int xkey = 0;\r
+       switch (cm) {\r
+         case NSClearLineFunctionKey: xkey = 'P'; break;\r
+         case '=': xkey = 'Q'; break;\r
+         case '/': xkey = 'R'; break;\r
+         case '*': xkey = 'S'; break;\r
+           /*\r
+            * FIXME: keypad - and + need to be mapped to ESC O l\r
+            * and ESC O k, or ESC O l and ESC O m, depending on\r
+            * xterm function key mode, and I can't remember which\r
+            * goes where.\r
+            */\r
+         case '\003': xkey = 'M'; break;\r
+         case '0': xkey = 'p'; break;\r
+         case '1': xkey = 'q'; break;\r
+         case '2': xkey = 'r'; break;\r
+         case '3': xkey = 's'; break;\r
+         case '4': xkey = 't'; break;\r
+         case '5': xkey = 'u'; break;\r
+         case '6': xkey = 'v'; break;\r
+         case '7': xkey = 'w'; break;\r
+         case '8': xkey = 'x'; break;\r
+         case '9': xkey = 'y'; break;\r
+         case '.': xkey = 'n'; break;\r
+       }\r
+       if (xkey) {\r
+           if (term->vt52_mode) {\r
+               if (xkey >= 'P' && xkey <= 'S') {\r
+                   output[end++] = '\033';\r
+                   output[end++] = xkey;\r
+               } else {\r
+                   output[end++] = '\033';\r
+                   output[end++] = '?';\r
+                   output[end++] = xkey;\r
+               }\r
+           } else {\r
+               output[end++] = '\033';\r
+               output[end++] = 'O';\r
+               output[end++] = xkey;\r
+           }\r
+           goto done;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Next, all the keys that do tilde codes. (ESC '[' nn '~',\r
+     * for integer decimal nn.)\r
+     *\r
+     * We also deal with the weird ones here. Linux VCs replace F1\r
+     * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but\r
+     * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w\r
+     * respectively.\r
+     */\r
+    {\r
+       int code = 0;\r
+       switch (cm) {\r
+         case NSF1FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 23 : 11);\r
+           break;\r
+         case NSF2FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 24 : 12);\r
+           break;\r
+         case NSF3FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 25 : 13);\r
+           break;\r
+         case NSF4FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 26 : 14);\r
+           break;\r
+         case NSF5FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 28 : 15);\r
+           break;\r
+         case NSF6FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 29 : 17);\r
+           break;\r
+         case NSF7FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 31 : 18);\r
+           break;\r
+         case NSF8FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 32 : 19);\r
+           break;\r
+         case NSF9FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 33 : 20);\r
+           break;\r
+         case NSF10FunctionKey:\r
+           code = (m & NSShiftKeyMask ? 34 : 21);\r
+           break;\r
+         case NSF11FunctionKey:\r
+           code = 23;\r
+           break;\r
+         case NSF12FunctionKey:\r
+           code = 24;\r
+           break;\r
+         case NSF13FunctionKey:\r
+           code = 25;\r
+           break;\r
+         case NSF14FunctionKey:\r
+           code = 26;\r
+           break;\r
+         case NSF15FunctionKey:\r
+           code = 28;\r
+           break;\r
+         case NSF16FunctionKey:\r
+           code = 29;\r
+           break;\r
+         case NSF17FunctionKey:\r
+           code = 31;\r
+           break;\r
+         case NSF18FunctionKey:\r
+           code = 32;\r
+           break;\r
+         case NSF19FunctionKey:\r
+           code = 33;\r
+           break;\r
+         case NSF20FunctionKey:\r
+           code = 34;\r
+           break;\r
+       }\r
+       if (!(m & NSControlKeyMask)) switch (cm) {\r
+         case NSHomeFunctionKey:\r
+           code = 1;\r
+           break;\r
+#ifdef FIXME\r
+         case GDK_Insert: case GDK_KP_Insert:\r
+           code = 2;\r
+           break;\r
+#endif\r
+         case NSDeleteFunctionKey:\r
+           code = 3;\r
+           break;\r
+         case NSEndFunctionKey:\r
+           code = 4;\r
+           break;\r
+         case NSPageUpFunctionKey:\r
+           code = 5;\r
+           break;\r
+         case NSPageDownFunctionKey:\r
+           code = 6;\r
+           break;\r
+       }\r
+       /* Reorder edit keys to physical order */\r
+       if (cfg.funky_type == FUNKY_VT400 && code <= 6)\r
+           code = "\0\2\1\4\5\3\6"[code];\r
+\r
+       if (term->vt52_mode && code > 0 && code <= 6) {\r
+           output[end++] = '\033';\r
+           output[end++] = " HLMEIG"[code];\r
+           goto done;\r
+       }\r
+\r
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */\r
+           code >= 11 && code <= 34) {\r
+           char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";\r
+           int index = 0;\r
+           switch (cm) {\r
+             case NSF1FunctionKey: index = 0; break;\r
+             case NSF2FunctionKey: index = 1; break;\r
+             case NSF3FunctionKey: index = 2; break;\r
+             case NSF4FunctionKey: index = 3; break;\r
+             case NSF5FunctionKey: index = 4; break;\r
+             case NSF6FunctionKey: index = 5; break;\r
+             case NSF7FunctionKey: index = 6; break;\r
+             case NSF8FunctionKey: index = 7; break;\r
+             case NSF9FunctionKey: index = 8; break;\r
+             case NSF10FunctionKey: index = 9; break;\r
+             case NSF11FunctionKey: index = 10; break;\r
+             case NSF12FunctionKey: index = 11; break;\r
+           }\r
+           if (m & NSShiftKeyMask) index += 12;\r
+           if (m & NSControlKeyMask) index += 24;\r
+           output[end++] = '\033';\r
+           output[end++] = '[';\r
+           output[end++] = codes[index];\r
+           goto done;\r
+       }\r
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */\r
+           code >= 1 && code <= 6) {\r
+           char codes[] = "HL.FIG";\r
+           if (code == 3) {\r
+               output[1] = '\x7F';\r
+               end = 2;\r
+           } else {\r
+               output[end++] = '\033';\r
+               output[end++] = '[';\r
+               output[end++] = codes[code-1];\r
+           }\r
+           goto done;\r
+       }\r
+       if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) &&\r
+           code >= 11 && code <= 24) {\r
+           int offt = 0;\r
+           if (code > 15)\r
+               offt++;\r
+           if (code > 21)\r
+               offt++;\r
+           if (term->vt52_mode) {\r
+               output[end++] = '\033';\r
+               output[end++] = code + 'P' - 11 - offt;\r
+           } else {\r
+               output[end++] = '\033';\r
+               output[end++] = 'O';\r
+               output[end++] = code + 'P' - 11 - offt;\r
+           }\r
+           goto done;\r
+       }\r
+       if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {\r
+           output[end++] = '\033';\r
+           output[end++] = '[';\r
+           output[end++] = '[';        \r
+           output[end++] = code + 'A' - 11;\r
+           goto done;\r
+       }\r
+       if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {\r
+           if (term->vt52_mode) {\r
+               output[end++] = '\033';\r
+               output[end++] = code + 'P' - 11;\r
+           } else {\r
+               output[end++] = '\033';\r
+               output[end++] = 'O';\r
+               output[end++] = code + 'P' - 11;\r
+           }\r
+           goto done;\r
+       }\r
+       if (cfg.rxvt_homeend && (code == 1 || code == 4)) {\r
+           if (code == 1) {\r
+               output[end++] = '\033';\r
+               output[end++] = '[';\r
+               output[end++] = 'H';\r
+           } else {\r
+               output[end++] = '\033';\r
+               output[end++] = 'O';\r
+               output[end++] = 'w';\r
+           }\r
+           goto done;\r
+       }\r
+       if (code) {\r
+           char buf[20];\r
+           sprintf(buf, "\x1B[%d~", code);\r
+           for (i = 0; buf[i]; i++)\r
+               output[end++] = buf[i];\r
+           goto done;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Cursor keys. (This includes the numberpad cursor keys,\r
+     * if we haven't already done them due to app keypad mode.)\r
+     */\r
+    {\r
+       int xkey = 0;\r
+       switch (cm) {\r
+         case NSUpArrowFunctionKey: xkey = 'A'; break;\r
+         case NSDownArrowFunctionKey: xkey = 'B'; break;\r
+         case NSRightArrowFunctionKey: xkey = 'C'; break;\r
+         case NSLeftArrowFunctionKey: xkey = 'D'; break;\r
+       }\r
+       if (xkey) {\r
+           end += format_arrow_key(output+end, term, xkey,\r
+                                   m & NSControlKeyMask);\r
+           goto done;\r
+       }\r
+    }\r
+\r
+    done:\r
+\r
+    /*\r
+     * Failing everything else, send the exact Unicode we got from\r
+     * OS X.\r
+     */\r
+    if (end == start) {\r
+       if (n > lenof(output)-start)\r
+           n = lenof(output)-start;   /* _shouldn't_ happen! */\r
+       for (i = 0; i < n; i++) {\r
+           output[i+start] = [s characterAtIndex:i];\r
+       }\r
+       end = n+start;\r
+    }\r
+\r
+    if (use_coutput) {\r
+       assert(special);\r
+       assert(end < lenof(coutput));\r
+       coutput[end] = '\0';\r
+       ldisc_send(ldisc, coutput+start, -2, TRUE);\r
+    } else {\r
+       luni_send(ldisc, output+start, end-start, TRUE);\r
+    }\r
+}\r
+\r
+- (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr\r
+{\r
+    return term_data(term, is_stderr, data, len);\r
+}\r
+\r
+- (int)fromBackendUntrusted:(const char *)data len:(int)len\r
+{\r
+    return term_data_untrusted(term, data, len);\r
+}\r
+\r
+- (void)startAlert:(NSAlert *)alert\r
+    withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx\r
+{\r
+    if (alert_ctx || alert_qhead) {\r
+       /*\r
+        * Queue this alert to be shown later.\r
+        */\r
+       struct alert_queue *qitem = snew(struct alert_queue);\r
+       qitem->next = NULL;\r
+       qitem->alert = alert;\r
+       qitem->callback = callback;\r
+       qitem->ctx = ctx;\r
+       if (alert_qtail)\r
+           alert_qtail->next = qitem;\r
+       else\r
+           alert_qhead = qitem;\r
+       alert_qtail = qitem;\r
+    } else {\r
+       alert_callback = callback;\r
+       alert_ctx = ctx;               /* NB this is assumed to need freeing! */\r
+       [alert beginSheetModalForWindow:self modalDelegate:self\r
+        didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)\r
+        contextInfo:NULL];\r
+    }\r
+}\r
+\r
+- (void)alertSheetDidEnd:(NSAlert *)alert returnCode:(int)returnCode\r
+    contextInfo:(void *)contextInfo\r
+{\r
+    [self performSelectorOnMainThread:\r
+     @selector(alertSheetDidFinishEnding:)\r
+     withObject:[NSNumber numberWithInt:returnCode]\r
+     waitUntilDone:NO];\r
+}\r
+\r
+- (void)alertSheetDidFinishEnding:(id)object\r
+{\r
+    int returnCode = [object intValue];\r
+\r
+    alert_callback(alert_ctx, returnCode);   /* transfers ownership of ctx */\r
+\r
+    /*\r
+     * If there's an alert in our queue (either already or because\r
+     * the callback just queued it), start it.\r
+     */\r
+    if (alert_qhead) {\r
+       struct alert_queue *qnext;\r
+\r
+       alert_callback = alert_qhead->callback;\r
+       alert_ctx = alert_qhead->ctx;\r
+       [alert_qhead->alert beginSheetModalForWindow:self modalDelegate:self\r
+        didEndSelector:@selector(alertSheetDidEnd:returnCode:contextInfo:)\r
+        contextInfo:NULL];\r
+\r
+       qnext = alert_qhead->next;\r
+       sfree(alert_qhead);\r
+       alert_qhead = qnext;\r
+       if (!qnext)\r
+           alert_qtail = NULL;\r
+    } else {\r
+       alert_ctx = NULL;\r
+    }\r
+}\r
+\r
+- (void)notifyRemoteExit\r
+{\r
+    int exitcode;\r
+\r
+    if (!exited && (exitcode = back->exitcode(backhandle)) >= 0)\r
+       [self endSession:(exitcode == 0)];\r
+}\r
+\r
+- (void)endSession:(int)clean\r
+{\r
+    exited = TRUE;\r
+    if (ldisc) {\r
+       ldisc_free(ldisc);\r
+       ldisc = NULL;\r
+    }\r
+    if (back) {\r
+       back->free(backhandle);\r
+       backhandle = NULL;\r
+       back = NULL;\r
+       //FIXME: update specials menu;\r
+    }\r
+    if (cfg.close_on_exit == FORCE_ON ||\r
+       (cfg.close_on_exit == AUTO && clean))\r
+       [self close];\r
+    // FIXME: else show restart menu item\r
+}\r
+\r
+- (Terminal *)term\r
+{\r
+    return term;\r
+}\r
+\r
+@end\r
+\r
+int from_backend(void *frontend, int is_stderr, const char *data, int len)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    return [win fromBackend:data len:len isStderr:is_stderr];\r
+}\r
+\r
+int from_backend_untrusted(void *frontend, const char *data, int len)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    return [win fromBackendUntrusted:data len:len];\r
+}\r
+\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    SessionWindow *win = (SessionWindow *)p->frontend;\r
+    Terminal *term = [win term];\r
+    return term_get_userpass_input(term, p, in, inlen);\r
+}\r
+\r
+void frontend_keypress(void *handle)\r
+{\r
+    /* FIXME */\r
+}\r
+\r
+void notify_remote_exit(void *frontend)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+\r
+    [win notifyRemoteExit];\r
+}\r
+\r
+void ldisc_update(void *frontend, int echo, int edit)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /*\r
+     * In a GUI front end, this need do nothing.\r
+     */\r
+}\r
+\r
+char *get_ttymode(void *frontend, const char *mode)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    Terminal *term = [win term];\r
+    return term_get_ttymode(term, mode);\r
+}\r
+\r
+void update_specials_menu(void *frontend)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * This is still called when mode==BELL_VISUAL, even though the\r
+ * visual bell is handled entirely within terminal.c, because we\r
+ * may want to perform additional actions on any kind of bell (for\r
+ * example, taskbar flashing in Windows).\r
+ */\r
+void do_beep(void *frontend, int mode)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    if (mode != BELL_VISUAL)\r
+       NSBeep();\r
+}\r
+\r
+int char_width(Context ctx, int uc)\r
+{\r
+    /*\r
+     * Under X, any fixed-width font really _is_ fixed-width.\r
+     * Double-width characters will be dealt with using a separate\r
+     * font. For the moment we can simply return 1.\r
+     */\r
+    return 1;\r
+}\r
+\r
+void palette_set(void *frontend, int n, int r, int g, int b)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+\r
+    if (n >= 16)\r
+       n += 256 - 16;\r
+    if (n > NALLCOLOURS)\r
+       return;\r
+    [win setColour:n r:r/255.0 g:g/255.0 b:b/255.0];\r
+\r
+    /*\r
+     * FIXME: do we need an OS X equivalent of set_window_background?\r
+     */\r
+}\r
+\r
+void palette_reset(void *frontend)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+    Config *cfg = [win cfg];\r
+\r
+    /* This maps colour indices in cfg to those used in colours[]. */\r
+    static const int ww[] = {\r
+       256, 257, 258, 259, 260, 261,\r
+       0, 8, 1, 9, 2, 10, 3, 11,\r
+       4, 12, 5, 13, 6, 14, 7, 15\r
+    };\r
+\r
+    int i;\r
+\r
+    for (i = 0; i < NCFGCOLOURS; i++) {\r
+       [win setColour:ww[i] r:cfg->colours[i][0]/255.0\r
+        g:cfg->colours[i][1]/255.0 b:cfg->colours[i][2]/255.0];\r
+    }\r
+\r
+    for (i = 0; i < NEXTCOLOURS; i++) {\r
+       if (i < 216) {\r
+           int r = i / 36, g = (i / 6) % 6, b = i % 6;\r
+           r = r ? r*40+55 : 0; g = g ? b*40+55 : 0; b = b ? b*40+55 : 0;\r
+           [win setColour:i+16 r:r/255.0 g:g/255.0 b:b/255.0];\r
+       } else {\r
+           int shade = i - 216;\r
+           float fshade = (shade * 10 + 8) / 255.0;\r
+           [win setColour:i+16 r:fshade g:fshade b:fshade];\r
+       }\r
+    }\r
+\r
+    /*\r
+     * FIXME: do we need an OS X equivalent of set_window_background?\r
+     */\r
+}\r
+\r
+Context get_ctx(void *frontend)\r
+{\r
+    SessionWindow *win = (SessionWindow *)frontend;\r
+\r
+    /*\r
+     * Lock the drawing focus on the image inside the TerminalView.\r
+     */\r
+    [win drawStartFinish:YES];\r
+\r
+    [[NSGraphicsContext currentContext] setShouldAntialias:YES];\r
+\r
+    /*\r
+     * Cocoa drawing functions don't take a graphics context: that\r
+     * parameter is implicit. Therefore, we'll use the frontend\r
+     * handle itself as the context, on the grounds that it's as\r
+     * good a thing to use as any.\r
+     */\r
+    return frontend;\r
+}\r
+\r
+void free_ctx(Context ctx)\r
+{\r
+    SessionWindow *win = (SessionWindow *)ctx;\r
+\r
+    [win drawStartFinish:NO];\r
+}\r
+\r
+void do_text(Context ctx, int x, int y, wchar_t *text, int len,\r
+            unsigned long attr, int lattr)\r
+{\r
+    SessionWindow *win = (SessionWindow *)ctx;\r
+\r
+    [win doText:text len:len x:x y:y attr:attr lattr:lattr];\r
+}\r
+\r
+void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,\r
+              unsigned long attr, int lattr)\r
+{\r
+    SessionWindow *win = (SessionWindow *)ctx;\r
+    Config *cfg = [win cfg];\r
+    int active, passive;\r
+\r
+    if (attr & TATTR_PASCURS) {\r
+       attr &= ~TATTR_PASCURS;\r
+       passive = 1;\r
+    } else\r
+       passive = 0;\r
+    if ((attr & TATTR_ACTCURS) && cfg->cursor_type != 0) {\r
+       attr &= ~TATTR_ACTCURS;\r
+        active = 1;\r
+    } else\r
+        active = 0;\r
+\r
+    [win doText:text len:len x:x y:y attr:attr lattr:lattr];\r
+\r
+    /*\r
+     * FIXME: now draw the various cursor types (both passive and\r
+     * active underlines and vertical lines, plus passive blocks).\r
+     */\r
+}\r
+\r
+/*\r
+ * Minimise or restore the window in response to a server-side\r
+ * request.\r
+ */\r
+void set_iconic(void *frontend, int iconic)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * Move the window in response to a server-side request.\r
+ */\r
+void move_window(void *frontend, int x, int y)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend; \r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * Move the window to the top or bottom of the z-order in response\r
+ * to a server-side request.\r
+ */\r
+void set_zorder(void *frontend, int top)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * Refresh the window in response to a server-side request.\r
+ */\r
+void refresh_window(void *frontend)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * Maximise or restore the window in response to a server-side\r
+ * request.\r
+ */\r
+void set_zoomed(void *frontend, int zoomed)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * Report whether the window is iconic, for terminal reports.\r
+ */\r
+int is_iconic(void *frontend)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    return NO;                                /* FIXME */\r
+}\r
+\r
+/*\r
+ * Report the window's position, for terminal reports.\r
+ */\r
+void get_window_pos(void *frontend, int *x, int *y)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * Report the window's pixel size, for terminal reports.\r
+ */\r
+void get_window_pixels(void *frontend, int *x, int *y)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+/*\r
+ * Return the window or icon title.\r
+ */\r
+char *get_window_title(void *frontend, int icon)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    return NULL; /* FIXME */\r
+}\r
+\r
+void set_title(void *frontend, char *title)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void set_icon(void *frontend, char *title)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void set_sbar(void *frontend, int total, int start, int page)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void get_clip(void *frontend, wchar_t ** p, int *len)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void write_clip(void *frontend, wchar_t *data, int *attr, int len, int must_deselect)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void request_paste(void *frontend)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void set_raw_mouse_mode(void *frontend, int activate)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void request_resize(void *frontend, int w, int h)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+}\r
+\r
+void sys_cursor(void *frontend, int x, int y)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /*\r
+     * This is probably meaningless under OS X. FIXME: find out for\r
+     * sure.\r
+     */\r
+}\r
+\r
+void logevent(void *frontend, const char *string)\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    /* FIXME */\r
+printf("logevent: %s\n", string);\r
+}\r
+\r
+int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */\r
+{\r
+    //SessionWindow *win = (SessionWindow *)frontend;\r
+    return 1; /* FIXME */\r
+}\r
+\r
+void set_busy_status(void *frontend, int status)\r
+{\r
+    /*\r
+     * We need do nothing here: the OS X `application is busy'\r
+     * beachball pointer appears _automatically_ when the\r
+     * application isn't responding to GUI messages.\r
+     */\r
+}\r
diff --git a/putty/MACOSX/PUTTY.ICN b/putty/MACOSX/PUTTY.ICN
new file mode 100644 (file)
index 0000000..72eab29
Binary files /dev/null and b/putty/MACOSX/PUTTY.ICN differ
diff --git a/putty/MACOSX/README.OSX b/putty/MACOSX/README.OSX
new file mode 100644 (file)
index 0000000..e9243a0
--- /dev/null
@@ -0,0 +1,83 @@
+This directory contains a Mac OS X port of PuTTY/pterm, running as a\r
+native Aqua GUI application.\r
+\r
+THIS PORT IS CURRENTLY UNFINISHED AND EXPERIMENTAL. It is _not_\r
+considered to be of release quality, even if you've found it (and\r
+are reading this) in a PuTTY release source archive. You are welcome\r
+to try using it, but don't be surprised at unexpected behaviour. I'm\r
+not kidding.\r
+\r
+In particular, I have not yet decided where OS X PuTTY should store\r
+its configuration data. Options include storing it in ~/.putty to be\r
+compatible with Unix PuTTY, storing it wherever is compatible with\r
+Mac Classic PuTTY, storing it in a natively OS X location, or\r
+sorting out the `config-locations' wishlist item and doing all\r
+three. Therefore, if you start using this port and create a whole\r
+load of saved sessions, you should not be surprised if a future\r
+version of the port decides to look somewhere completely different\r
+for the data and therefore loses them all. If that happens, don't\r
+say you weren't warned!\r
+\r
+Other ways in which the port is currently unfinished include:\r
+\r
+Missing terminal window features\r
+--------------------------------\r
+\r
+ - terminal display is horribly slow\r
+\r
+ - fonts aren't configurable\r
+\r
+ - several features are unimplemented in the terminal display:\r
+   underlining, non-solid-block cursors, double-width and\r
+   double-height line attributes, bold as font rather than as\r
+   colour, wide (CJK) characters, combining characters.\r
+\r
+ - there's no scrollbar\r
+\r
+ - terminal window resizing isn't implemented yet\r
+\r
+ - proper window placement (cascading down and right from the\r
+   starting position, plus remembering previous window positions per\r
+   the Apple HIG) is not implemented\r
+\r
+Missing alert box features\r
+--------------------------\r
+\r
+ - warn-on-close isn't implemented\r
+\r
+Missing input features\r
+----------------------\r
+\r
+ - use of Alt+numberpad to enter arbitrary numeric character codes\r
+   is not yet supported\r
+\r
+ - there's no Meta key yet. (I'd like to at least have the\r
+   possibility of using Command rather than Option as the Meta key,\r
+   since the latter is necessary to send some characters, including\r
+   the rather important # on Apple UK keyboards; but trapping\r
+   Command-<key> and sending it to the window rather than the\r
+   application menu requires me to make a positive effort of some\r
+   sort and I haven't got round to it yet. For those Mac users who\r
+   consider their Command key sacrosanct, don't worry, this option\r
+   _will_ be configurable and _will_ be off by default.)\r
+\r
+ - there's no specials menu\r
+\r
+ - mouse activity isn't supported (neither cut-and-paste nor xterm\r
+   mouse tracking)\r
+\r
+Missing terminal emulation features\r
+-----------------------------------\r
+\r
+ - currently no support for server-side window management requests\r
+   (i.e. escape sequences to minimise or maximise the window,\r
+   request or change its position and size, change its title etc)\r
+\r
+ - window title is currently fixed\r
+\r
+Other missing features\r
+----------------------\r
+\r
+ - no Event Log\r
+\r
+ - no mid-session Change Settings\r
diff --git a/putty/MINIBIDI.C b/putty/MINIBIDI.C
new file mode 100644 (file)
index 0000000..4c2eea5
--- /dev/null
@@ -0,0 +1,2031 @@
+/************************************************************************\r
+ * $Id: minibidi.c 9169 2011-05-07 10:57:19Z simon $\r
+ *\r
+ * ------------\r
+ * Description:\r
+ * ------------\r
+ * This is an implemention of Unicode's Bidirectional Algorithm\r
+ * (known as UAX #9).\r
+ *\r
+ *   http://www.unicode.org/reports/tr9/\r
+ *\r
+ * Author: Ahmad Khalifa\r
+ *\r
+ * -----------------\r
+ * Revision Details:    (Updated by Revision Control System)\r
+ * -----------------\r
+ *  $Date: 2011-05-07 11:57:19 +0100 (Sat, 07 May 2011) $\r
+ *  $Author: simon $\r
+ *  $Revision: 9169 $\r
+ *\r
+ * (www.arabeyes.org - under MIT license)\r
+ *\r
+ ************************************************************************/\r
+\r
+/*\r
+ * TODO:\r
+ * =====\r
+ * - Explicit marks need to be handled (they are not 100% now)\r
+ * - Ligatures\r
+ */\r
+\r
+#include <stdlib.h>    /* definition of wchar_t*/\r
+\r
+#include "misc.h"\r
+\r
+#define LMASK  0x3F    /* Embedding Level mask */\r
+#define OMASK  0xC0    /* Override mask */\r
+#define OISL   0x80    /* Override is L */\r
+#define OISR   0x40    /* Override is R */\r
+\r
+/* For standalone compilation in a testing mode.\r
+ * Still depends on the PuTTY headers for snewn and sfree, but can avoid\r
+ * _linking_ with any other PuTTY code. */\r
+#ifdef TEST_GETTYPE\r
+#define safemalloc malloc\r
+#define safefree free\r
+#endif\r
+\r
+/* Shaping Helpers */\r
+#define STYPE(xh) ((((xh) >= SHAPE_FIRST) && ((xh) <= SHAPE_LAST)) ? \\r
+shapetypes[(xh)-SHAPE_FIRST].type : SU) /*))*/\r
+#define SISOLATED(xh) (shapetypes[(xh)-SHAPE_FIRST].form_b)\r
+#define SFINAL(xh) ((xh)+1)\r
+#define SINITIAL(xh) ((xh)+2)\r
+#define SMEDIAL(ch) ((ch)+3)\r
+\r
+#define leastGreaterOdd(x) ( ((x)+1) | 1 )\r
+#define leastGreaterEven(x) ( ((x)+2) &~ 1 )\r
+\r
+typedef struct bidi_char {\r
+    wchar_t origwc, wc;\r
+    unsigned short index;\r
+} bidi_char;\r
+\r
+/* function declarations */\r
+void flipThisRun(bidi_char *from, unsigned char* level, int max, int count);\r
+int findIndexOfRun(unsigned char* level , int start, int count, int tlevel);\r
+unsigned char getType(int ch);\r
+unsigned char setOverrideBits(unsigned char level, unsigned char override);\r
+int getPreviousLevel(unsigned char* level, int from);\r
+int do_shape(bidi_char *line, bidi_char *to, int count);\r
+int do_bidi(bidi_char *line, int count);\r
+void doMirror(wchar_t* ch);\r
+\r
+/* character types */\r
+enum {\r
+    L,\r
+    LRE,\r
+    LRO,\r
+    R,\r
+    AL,\r
+    RLE,\r
+    RLO,\r
+    PDF,\r
+    EN,\r
+    ES,\r
+    ET,\r
+    AN,\r
+    CS,\r
+    NSM,\r
+    BN,\r
+    B,\r
+    S,\r
+    WS,\r
+    ON\r
+};\r
+\r
+/* Shaping Types */\r
+enum {\r
+    SL, /* Left-Joining, doesnt exist in U+0600 - U+06FF */\r
+    SR, /* Right-Joining, ie has Isolated, Final */\r
+    SD, /* Dual-Joining, ie has Isolated, Final, Initial, Medial */\r
+    SU, /* Non-Joining */\r
+    SC  /* Join-Causing, like U+0640 (TATWEEL) */\r
+};\r
+\r
+typedef struct {\r
+    char type;\r
+    wchar_t form_b;\r
+} shape_node;\r
+\r
+/* Kept near the actual table, for verification. */\r
+#define SHAPE_FIRST 0x621\r
+#define SHAPE_LAST (SHAPE_FIRST + lenof(shapetypes) - 1)\r
+\r
+const shape_node shapetypes[] = {\r
+    /* index, Typ, Iso, Ligature Index*/\r
+    /* 621 */ {SU, 0xFE80},\r
+    /* 622 */ {SR, 0xFE81},\r
+    /* 623 */ {SR, 0xFE83},\r
+    /* 624 */ {SR, 0xFE85},\r
+    /* 625 */ {SR, 0xFE87},\r
+    /* 626 */ {SD, 0xFE89},\r
+    /* 627 */ {SR, 0xFE8D},\r
+    /* 628 */ {SD, 0xFE8F},\r
+    /* 629 */ {SR, 0xFE93},\r
+    /* 62A */ {SD, 0xFE95},\r
+    /* 62B */ {SD, 0xFE99},\r
+    /* 62C */ {SD, 0xFE9D},\r
+    /* 62D */ {SD, 0xFEA1},\r
+    /* 62E */ {SD, 0xFEA5},\r
+    /* 62F */ {SR, 0xFEA9},\r
+    /* 630 */ {SR, 0xFEAB},\r
+    /* 631 */ {SR, 0xFEAD},\r
+    /* 632 */ {SR, 0xFEAF},\r
+    /* 633 */ {SD, 0xFEB1},\r
+    /* 634 */ {SD, 0xFEB5},\r
+    /* 635 */ {SD, 0xFEB9},\r
+    /* 636 */ {SD, 0xFEBD},\r
+    /* 637 */ {SD, 0xFEC1},\r
+    /* 638 */ {SD, 0xFEC5},\r
+    /* 639 */ {SD, 0xFEC9},\r
+    /* 63A */ {SD, 0xFECD},\r
+    /* 63B */ {SU, 0x0},\r
+    /* 63C */ {SU, 0x0},\r
+    /* 63D */ {SU, 0x0},\r
+    /* 63E */ {SU, 0x0},\r
+    /* 63F */ {SU, 0x0},\r
+    /* 640 */ {SC, 0x0},\r
+    /* 641 */ {SD, 0xFED1},\r
+    /* 642 */ {SD, 0xFED5},\r
+    /* 643 */ {SD, 0xFED9},\r
+    /* 644 */ {SD, 0xFEDD},\r
+    /* 645 */ {SD, 0xFEE1},\r
+    /* 646 */ {SD, 0xFEE5},\r
+    /* 647 */ {SD, 0xFEE9},\r
+    /* 648 */ {SR, 0xFEED},\r
+    /* 649 */ {SR, 0xFEEF}, /* SD */\r
+    /* 64A */ {SD, 0xFEF1},\r
+    /* 64B */ {SU, 0x0},\r
+    /* 64C */ {SU, 0x0},\r
+    /* 64D */ {SU, 0x0},\r
+    /* 64E */ {SU, 0x0},\r
+    /* 64F */ {SU, 0x0},\r
+    /* 650 */ {SU, 0x0},\r
+    /* 651 */ {SU, 0x0},\r
+    /* 652 */ {SU, 0x0},\r
+    /* 653 */ {SU, 0x0},\r
+    /* 654 */ {SU, 0x0},\r
+    /* 655 */ {SU, 0x0},\r
+    /* 656 */ {SU, 0x0},\r
+    /* 657 */ {SU, 0x0},\r
+    /* 658 */ {SU, 0x0},\r
+    /* 659 */ {SU, 0x0},\r
+    /* 65A */ {SU, 0x0},\r
+    /* 65B */ {SU, 0x0},\r
+    /* 65C */ {SU, 0x0},\r
+    /* 65D */ {SU, 0x0},\r
+    /* 65E */ {SU, 0x0},\r
+    /* 65F */ {SU, 0x0},\r
+    /* 660 */ {SU, 0x0},\r
+    /* 661 */ {SU, 0x0},\r
+    /* 662 */ {SU, 0x0},\r
+    /* 663 */ {SU, 0x0},\r
+    /* 664 */ {SU, 0x0},\r
+    /* 665 */ {SU, 0x0},\r
+    /* 666 */ {SU, 0x0},\r
+    /* 667 */ {SU, 0x0},\r
+    /* 668 */ {SU, 0x0},\r
+    /* 669 */ {SU, 0x0},\r
+    /* 66A */ {SU, 0x0},\r
+    /* 66B */ {SU, 0x0},\r
+    /* 66C */ {SU, 0x0},\r
+    /* 66D */ {SU, 0x0},\r
+    /* 66E */ {SU, 0x0},\r
+    /* 66F */ {SU, 0x0},\r
+    /* 670 */ {SU, 0x0},\r
+    /* 671 */ {SR, 0xFB50},\r
+    /* 672 */ {SU, 0x0},\r
+    /* 673 */ {SU, 0x0},\r
+    /* 674 */ {SU, 0x0},\r
+    /* 675 */ {SU, 0x0},\r
+    /* 676 */ {SU, 0x0},\r
+    /* 677 */ {SU, 0x0},\r
+    /* 678 */ {SU, 0x0},\r
+    /* 679 */ {SD, 0xFB66},\r
+    /* 67A */ {SD, 0xFB5E},\r
+    /* 67B */ {SD, 0xFB52},\r
+    /* 67C */ {SU, 0x0},\r
+    /* 67D */ {SU, 0x0},\r
+    /* 67E */ {SD, 0xFB56},\r
+    /* 67F */ {SD, 0xFB62},\r
+    /* 680 */ {SD, 0xFB5A},\r
+    /* 681 */ {SU, 0x0},\r
+    /* 682 */ {SU, 0x0},\r
+    /* 683 */ {SD, 0xFB76},\r
+    /* 684 */ {SD, 0xFB72},\r
+    /* 685 */ {SU, 0x0},\r
+    /* 686 */ {SD, 0xFB7A},\r
+    /* 687 */ {SD, 0xFB7E},\r
+    /* 688 */ {SR, 0xFB88},\r
+    /* 689 */ {SU, 0x0},\r
+    /* 68A */ {SU, 0x0},\r
+    /* 68B */ {SU, 0x0},\r
+    /* 68C */ {SR, 0xFB84},\r
+    /* 68D */ {SR, 0xFB82},\r
+    /* 68E */ {SR, 0xFB86},\r
+    /* 68F */ {SU, 0x0},\r
+    /* 690 */ {SU, 0x0},\r
+    /* 691 */ {SR, 0xFB8C},\r
+    /* 692 */ {SU, 0x0},\r
+    /* 693 */ {SU, 0x0},\r
+    /* 694 */ {SU, 0x0},\r
+    /* 695 */ {SU, 0x0},\r
+    /* 696 */ {SU, 0x0},\r
+    /* 697 */ {SU, 0x0},\r
+    /* 698 */ {SR, 0xFB8A},\r
+    /* 699 */ {SU, 0x0},\r
+    /* 69A */ {SU, 0x0},\r
+    /* 69B */ {SU, 0x0},\r
+    /* 69C */ {SU, 0x0},\r
+    /* 69D */ {SU, 0x0},\r
+    /* 69E */ {SU, 0x0},\r
+    /* 69F */ {SU, 0x0},\r
+    /* 6A0 */ {SU, 0x0},\r
+    /* 6A1 */ {SU, 0x0},\r
+    /* 6A2 */ {SU, 0x0},\r
+    /* 6A3 */ {SU, 0x0},\r
+    /* 6A4 */ {SD, 0xFB6A},\r
+    /* 6A5 */ {SU, 0x0},\r
+    /* 6A6 */ {SD, 0xFB6E},\r
+    /* 6A7 */ {SU, 0x0},\r
+    /* 6A8 */ {SU, 0x0},\r
+    /* 6A9 */ {SD, 0xFB8E},\r
+    /* 6AA */ {SU, 0x0},\r
+    /* 6AB */ {SU, 0x0},\r
+    /* 6AC */ {SU, 0x0},\r
+    /* 6AD */ {SD, 0xFBD3},\r
+    /* 6AE */ {SU, 0x0},\r
+    /* 6AF */ {SD, 0xFB92},\r
+    /* 6B0 */ {SU, 0x0},\r
+    /* 6B1 */ {SD, 0xFB9A},\r
+    /* 6B2 */ {SU, 0x0},\r
+    /* 6B3 */ {SD, 0xFB96},\r
+    /* 6B4 */ {SU, 0x0},\r
+    /* 6B5 */ {SU, 0x0},\r
+    /* 6B6 */ {SU, 0x0},\r
+    /* 6B7 */ {SU, 0x0},\r
+    /* 6B8 */ {SU, 0x0},\r
+    /* 6B9 */ {SU, 0x0},\r
+    /* 6BA */ {SR, 0xFB9E},\r
+    /* 6BB */ {SD, 0xFBA0},\r
+    /* 6BC */ {SU, 0x0},\r
+    /* 6BD */ {SU, 0x0},\r
+    /* 6BE */ {SD, 0xFBAA},\r
+    /* 6BF */ {SU, 0x0},\r
+    /* 6C0 */ {SR, 0xFBA4},\r
+    /* 6C1 */ {SD, 0xFBA6},\r
+    /* 6C2 */ {SU, 0x0},\r
+    /* 6C3 */ {SU, 0x0},\r
+    /* 6C4 */ {SU, 0x0},\r
+    /* 6C5 */ {SR, 0xFBE0},\r
+    /* 6C6 */ {SR, 0xFBD9},\r
+    /* 6C7 */ {SR, 0xFBD7},\r
+    /* 6C8 */ {SR, 0xFBDB},\r
+    /* 6C9 */ {SR, 0xFBE2},\r
+    /* 6CA */ {SU, 0x0},\r
+    /* 6CB */ {SR, 0xFBDE},\r
+    /* 6CC */ {SD, 0xFBFC},\r
+    /* 6CD */ {SU, 0x0},\r
+    /* 6CE */ {SU, 0x0},\r
+    /* 6CF */ {SU, 0x0},\r
+    /* 6D0 */ {SU, 0x0},\r
+    /* 6D1 */ {SU, 0x0},\r
+    /* 6D2 */ {SR, 0xFBAE},\r
+};\r
+\r
+/*\r
+ * Flips the text buffer, according to max level, and\r
+ * all higher levels\r
+ *\r
+ * Input:\r
+ * from: text buffer, on which to apply flipping\r
+ * level: resolved levels buffer\r
+ * max: the maximum level found in this line (should be unsigned char)\r
+ * count: line size in bidi_char\r
+ */\r
+void flipThisRun(bidi_char *from, unsigned char *level, int max, int count)\r
+{\r
+    int i, j, k, tlevel;\r
+    bidi_char temp;\r
+\r
+    j = i = 0;\r
+    while (i<count && j<count) {\r
+\r
+       /* find the start of the run of level=max */\r
+       tlevel = max;\r
+       i = j = findIndexOfRun(level, i, count, max);\r
+       /* find the end of the run */\r
+       while (i<count && tlevel <= level[i]) {\r
+           i++;\r
+       }\r
+       for (k = i - 1; k > j; k--, j++) {\r
+           temp = from[k];\r
+           from[k] = from[j];\r
+           from[j] = temp;\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Finds the index of a run with level equals tlevel\r
+ */\r
+int findIndexOfRun(unsigned char* level , int start, int count, int tlevel)\r
+{\r
+    int i;\r
+    for (i=start; i<count; i++) {\r
+       if (tlevel == level[i]) {\r
+           return i;\r
+       }\r
+    }\r
+    return count;\r
+}\r
+\r
+/*\r
+ * Returns the bidi character type of ch.\r
+ *\r
+ * The data table in this function is constructed from the Unicode\r
+ * Character Database, downloadable from unicode.org at the URL\r
+ * \r
+ *     http://www.unicode.org/Public/UNIDATA/UnicodeData.txt\r
+ * \r
+ * by the following fragment of Perl:\r
+\r
+perl -ne 'split ";"; $num = hex $_[0]; $type = $_[4];' \\r
+      -e '$fl = ($_[1] =~ /First/ ? 1 : $_[1] =~ /Last/ ? 2 : 0);' \\r
+      -e 'if ($type eq $runtype and ($runend == $num-1 or ' \\r
+      -e '    ($fl==2 and $pfl==1))) {$runend = $num;} else { &reset; }' \\r
+      -e '$pfl=$fl; END { &reset }; sub reset {' \\r
+      -e 'printf"        {0x%04x, 0x%04x, %s},\n",$runstart,$runend,$runtype' \\r
+      -e '  if defined $runstart and $runtype ne "ON";' \\r
+      -e '$runstart=$runend=$num; $runtype=$type;}' \\r
+    UnicodeData.txt\r
+\r
+ */\r
+unsigned char getType(int ch)\r
+{\r
+    static const struct {\r
+       int first, last, type;\r
+    } lookup[] = {\r
+        {0x0000, 0x0008, BN},\r
+        {0x0009, 0x0009, S},\r
+        {0x000a, 0x000a, B},\r
+        {0x000b, 0x000b, S},\r
+        {0x000c, 0x000c, WS},\r
+        {0x000d, 0x000d, B},\r
+        {0x000e, 0x001b, BN},\r
+        {0x001c, 0x001e, B},\r
+        {0x001f, 0x001f, S},\r
+        {0x0020, 0x0020, WS},\r
+        {0x0023, 0x0025, ET},\r
+        {0x002b, 0x002b, ES},\r
+        {0x002c, 0x002c, CS},\r
+        {0x002d, 0x002d, ES},\r
+        {0x002e, 0x002f, CS},\r
+        {0x0030, 0x0039, EN},\r
+        {0x003a, 0x003a, CS},\r
+        {0x0041, 0x005a, L},\r
+        {0x0061, 0x007a, L},\r
+        {0x007f, 0x0084, BN},\r
+        {0x0085, 0x0085, B},\r
+        {0x0086, 0x009f, BN},\r
+        {0x00a0, 0x00a0, CS},\r
+        {0x00a2, 0x00a5, ET},\r
+        {0x00aa, 0x00aa, L},\r
+        {0x00ad, 0x00ad, BN},\r
+        {0x00b0, 0x00b1, ET},\r
+        {0x00b2, 0x00b3, EN},\r
+        {0x00b5, 0x00b5, L},\r
+        {0x00b9, 0x00b9, EN},\r
+        {0x00ba, 0x00ba, L},\r
+        {0x00c0, 0x00d6, L},\r
+        {0x00d8, 0x00f6, L},\r
+        {0x00f8, 0x0236, L},\r
+        {0x0250, 0x02b8, L},\r
+        {0x02bb, 0x02c1, L},\r
+        {0x02d0, 0x02d1, L},\r
+        {0x02e0, 0x02e4, L},\r
+        {0x02ee, 0x02ee, L},\r
+        {0x0300, 0x0357, NSM},\r
+        {0x035d, 0x036f, NSM},\r
+        {0x037a, 0x037a, L},\r
+        {0x0386, 0x0386, L},\r
+        {0x0388, 0x038a, L},\r
+        {0x038c, 0x038c, L},\r
+        {0x038e, 0x03a1, L},\r
+        {0x03a3, 0x03ce, L},\r
+        {0x03d0, 0x03f5, L},\r
+        {0x03f7, 0x03fb, L},\r
+        {0x0400, 0x0482, L},\r
+        {0x0483, 0x0486, NSM},\r
+        {0x0488, 0x0489, NSM},\r
+        {0x048a, 0x04ce, L},\r
+        {0x04d0, 0x04f5, L},\r
+        {0x04f8, 0x04f9, L},\r
+        {0x0500, 0x050f, L},\r
+        {0x0531, 0x0556, L},\r
+        {0x0559, 0x055f, L},\r
+        {0x0561, 0x0587, L},\r
+        {0x0589, 0x0589, L},\r
+        {0x0591, 0x05a1, NSM},\r
+        {0x05a3, 0x05b9, NSM},\r
+        {0x05bb, 0x05bd, NSM},\r
+        {0x05be, 0x05be, R},\r
+        {0x05bf, 0x05bf, NSM},\r
+        {0x05c0, 0x05c0, R},\r
+        {0x05c1, 0x05c2, NSM},\r
+        {0x05c3, 0x05c3, R},\r
+        {0x05c4, 0x05c4, NSM},\r
+        {0x05d0, 0x05ea, R},\r
+        {0x05f0, 0x05f4, R},\r
+        {0x0600, 0x0603, AL},\r
+        {0x060c, 0x060c, CS},\r
+        {0x060d, 0x060d, AL},\r
+        {0x0610, 0x0615, NSM},\r
+        {0x061b, 0x061b, AL},\r
+        {0x061f, 0x061f, AL},\r
+        {0x0621, 0x063a, AL},\r
+        {0x0640, 0x064a, AL},\r
+        {0x064b, 0x0658, NSM},\r
+        {0x0660, 0x0669, AN},\r
+        {0x066a, 0x066a, ET},\r
+        {0x066b, 0x066c, AN},\r
+        {0x066d, 0x066f, AL},\r
+        {0x0670, 0x0670, NSM},\r
+        {0x0671, 0x06d5, AL},\r
+        {0x06d6, 0x06dc, NSM},\r
+        {0x06dd, 0x06dd, AL},\r
+        {0x06de, 0x06e4, NSM},\r
+        {0x06e5, 0x06e6, AL},\r
+        {0x06e7, 0x06e8, NSM},\r
+        {0x06ea, 0x06ed, NSM},\r
+        {0x06ee, 0x06ef, AL},\r
+        {0x06f0, 0x06f9, EN},\r
+        {0x06fa, 0x070d, AL},\r
+        {0x070f, 0x070f, BN},\r
+        {0x0710, 0x0710, AL},\r
+        {0x0711, 0x0711, NSM},\r
+        {0x0712, 0x072f, AL},\r
+        {0x0730, 0x074a, NSM},\r
+        {0x074d, 0x074f, AL},\r
+        {0x0780, 0x07a5, AL},\r
+        {0x07a6, 0x07b0, NSM},\r
+        {0x07b1, 0x07b1, AL},\r
+        {0x0901, 0x0902, NSM},\r
+        {0x0903, 0x0939, L},\r
+        {0x093c, 0x093c, NSM},\r
+        {0x093d, 0x0940, L},\r
+        {0x0941, 0x0948, NSM},\r
+        {0x0949, 0x094c, L},\r
+        {0x094d, 0x094d, NSM},\r
+        {0x0950, 0x0950, L},\r
+        {0x0951, 0x0954, NSM},\r
+        {0x0958, 0x0961, L},\r
+        {0x0962, 0x0963, NSM},\r
+        {0x0964, 0x0970, L},\r
+        {0x0981, 0x0981, NSM},\r
+        {0x0982, 0x0983, L},\r
+        {0x0985, 0x098c, L},\r
+        {0x098f, 0x0990, L},\r
+        {0x0993, 0x09a8, L},\r
+        {0x09aa, 0x09b0, L},\r
+        {0x09b2, 0x09b2, L},\r
+        {0x09b6, 0x09b9, L},\r
+        {0x09bc, 0x09bc, NSM},\r
+        {0x09bd, 0x09c0, L},\r
+        {0x09c1, 0x09c4, NSM},\r
+        {0x09c7, 0x09c8, L},\r
+        {0x09cb, 0x09cc, L},\r
+        {0x09cd, 0x09cd, NSM},\r
+        {0x09d7, 0x09d7, L},\r
+        {0x09dc, 0x09dd, L},\r
+        {0x09df, 0x09e1, L},\r
+        {0x09e2, 0x09e3, NSM},\r
+        {0x09e6, 0x09f1, L},\r
+        {0x09f2, 0x09f3, ET},\r
+        {0x09f4, 0x09fa, L},\r
+        {0x0a01, 0x0a02, NSM},\r
+        {0x0a03, 0x0a03, L},\r
+        {0x0a05, 0x0a0a, L},\r
+        {0x0a0f, 0x0a10, L},\r
+        {0x0a13, 0x0a28, L},\r
+        {0x0a2a, 0x0a30, L},\r
+        {0x0a32, 0x0a33, L},\r
+        {0x0a35, 0x0a36, L},\r
+        {0x0a38, 0x0a39, L},\r
+        {0x0a3c, 0x0a3c, NSM},\r
+        {0x0a3e, 0x0a40, L},\r
+        {0x0a41, 0x0a42, NSM},\r
+        {0x0a47, 0x0a48, NSM},\r
+        {0x0a4b, 0x0a4d, NSM},\r
+        {0x0a59, 0x0a5c, L},\r
+        {0x0a5e, 0x0a5e, L},\r
+        {0x0a66, 0x0a6f, L},\r
+        {0x0a70, 0x0a71, NSM},\r
+        {0x0a72, 0x0a74, L},\r
+        {0x0a81, 0x0a82, NSM},\r
+        {0x0a83, 0x0a83, L},\r
+        {0x0a85, 0x0a8d, L},\r
+        {0x0a8f, 0x0a91, L},\r
+        {0x0a93, 0x0aa8, L},\r
+        {0x0aaa, 0x0ab0, L},\r
+        {0x0ab2, 0x0ab3, L},\r
+        {0x0ab5, 0x0ab9, L},\r
+        {0x0abc, 0x0abc, NSM},\r
+        {0x0abd, 0x0ac0, L},\r
+        {0x0ac1, 0x0ac5, NSM},\r
+        {0x0ac7, 0x0ac8, NSM},\r
+        {0x0ac9, 0x0ac9, L},\r
+        {0x0acb, 0x0acc, L},\r
+        {0x0acd, 0x0acd, NSM},\r
+        {0x0ad0, 0x0ad0, L},\r
+        {0x0ae0, 0x0ae1, L},\r
+        {0x0ae2, 0x0ae3, NSM},\r
+        {0x0ae6, 0x0aef, L},\r
+        {0x0af1, 0x0af1, ET},\r
+        {0x0b01, 0x0b01, NSM},\r
+        {0x0b02, 0x0b03, L},\r
+        {0x0b05, 0x0b0c, L},\r
+        {0x0b0f, 0x0b10, L},\r
+        {0x0b13, 0x0b28, L},\r
+        {0x0b2a, 0x0b30, L},\r
+        {0x0b32, 0x0b33, L},\r
+        {0x0b35, 0x0b39, L},\r
+        {0x0b3c, 0x0b3c, NSM},\r
+        {0x0b3d, 0x0b3e, L},\r
+        {0x0b3f, 0x0b3f, NSM},\r
+        {0x0b40, 0x0b40, L},\r
+        {0x0b41, 0x0b43, NSM},\r
+        {0x0b47, 0x0b48, L},\r
+        {0x0b4b, 0x0b4c, L},\r
+        {0x0b4d, 0x0b4d, NSM},\r
+        {0x0b56, 0x0b56, NSM},\r
+        {0x0b57, 0x0b57, L},\r
+        {0x0b5c, 0x0b5d, L},\r
+        {0x0b5f, 0x0b61, L},\r
+        {0x0b66, 0x0b71, L},\r
+        {0x0b82, 0x0b82, NSM},\r
+        {0x0b83, 0x0b83, L},\r
+        {0x0b85, 0x0b8a, L},\r
+        {0x0b8e, 0x0b90, L},\r
+        {0x0b92, 0x0b95, L},\r
+        {0x0b99, 0x0b9a, L},\r
+        {0x0b9c, 0x0b9c, L},\r
+        {0x0b9e, 0x0b9f, L},\r
+        {0x0ba3, 0x0ba4, L},\r
+        {0x0ba8, 0x0baa, L},\r
+        {0x0bae, 0x0bb5, L},\r
+        {0x0bb7, 0x0bb9, L},\r
+        {0x0bbe, 0x0bbf, L},\r
+        {0x0bc0, 0x0bc0, NSM},\r
+        {0x0bc1, 0x0bc2, L},\r
+        {0x0bc6, 0x0bc8, L},\r
+        {0x0bca, 0x0bcc, L},\r
+        {0x0bcd, 0x0bcd, NSM},\r
+        {0x0bd7, 0x0bd7, L},\r
+        {0x0be7, 0x0bf2, L},\r
+        {0x0bf9, 0x0bf9, ET},\r
+        {0x0c01, 0x0c03, L},\r
+        {0x0c05, 0x0c0c, L},\r
+        {0x0c0e, 0x0c10, L},\r
+        {0x0c12, 0x0c28, L},\r
+        {0x0c2a, 0x0c33, L},\r
+        {0x0c35, 0x0c39, L},\r
+        {0x0c3e, 0x0c40, NSM},\r
+        {0x0c41, 0x0c44, L},\r
+        {0x0c46, 0x0c48, NSM},\r
+        {0x0c4a, 0x0c4d, NSM},\r
+        {0x0c55, 0x0c56, NSM},\r
+        {0x0c60, 0x0c61, L},\r
+        {0x0c66, 0x0c6f, L},\r
+        {0x0c82, 0x0c83, L},\r
+        {0x0c85, 0x0c8c, L},\r
+        {0x0c8e, 0x0c90, L},\r
+        {0x0c92, 0x0ca8, L},\r
+        {0x0caa, 0x0cb3, L},\r
+        {0x0cb5, 0x0cb9, L},\r
+        {0x0cbc, 0x0cbc, NSM},\r
+        {0x0cbd, 0x0cc4, L},\r
+        {0x0cc6, 0x0cc8, L},\r
+        {0x0cca, 0x0ccb, L},\r
+        {0x0ccc, 0x0ccd, NSM},\r
+        {0x0cd5, 0x0cd6, L},\r
+        {0x0cde, 0x0cde, L},\r
+        {0x0ce0, 0x0ce1, L},\r
+        {0x0ce6, 0x0cef, L},\r
+        {0x0d02, 0x0d03, L},\r
+        {0x0d05, 0x0d0c, L},\r
+        {0x0d0e, 0x0d10, L},\r
+        {0x0d12, 0x0d28, L},\r
+        {0x0d2a, 0x0d39, L},\r
+        {0x0d3e, 0x0d40, L},\r
+        {0x0d41, 0x0d43, NSM},\r
+        {0x0d46, 0x0d48, L},\r
+        {0x0d4a, 0x0d4c, L},\r
+        {0x0d4d, 0x0d4d, NSM},\r
+        {0x0d57, 0x0d57, L},\r
+        {0x0d60, 0x0d61, L},\r
+        {0x0d66, 0x0d6f, L},\r
+        {0x0d82, 0x0d83, L},\r
+        {0x0d85, 0x0d96, L},\r
+        {0x0d9a, 0x0db1, L},\r
+        {0x0db3, 0x0dbb, L},\r
+        {0x0dbd, 0x0dbd, L},\r
+        {0x0dc0, 0x0dc6, L},\r
+        {0x0dca, 0x0dca, NSM},\r
+        {0x0dcf, 0x0dd1, L},\r
+        {0x0dd2, 0x0dd4, NSM},\r
+        {0x0dd6, 0x0dd6, NSM},\r
+        {0x0dd8, 0x0ddf, L},\r
+        {0x0df2, 0x0df4, L},\r
+        {0x0e01, 0x0e30, L},\r
+        {0x0e31, 0x0e31, NSM},\r
+        {0x0e32, 0x0e33, L},\r
+        {0x0e34, 0x0e3a, NSM},\r
+        {0x0e3f, 0x0e3f, ET},\r
+        {0x0e40, 0x0e46, L},\r
+        {0x0e47, 0x0e4e, NSM},\r
+        {0x0e4f, 0x0e5b, L},\r
+        {0x0e81, 0x0e82, L},\r
+        {0x0e84, 0x0e84, L},\r
+        {0x0e87, 0x0e88, L},\r
+        {0x0e8a, 0x0e8a, L},\r
+        {0x0e8d, 0x0e8d, L},\r
+        {0x0e94, 0x0e97, L},\r
+        {0x0e99, 0x0e9f, L},\r
+        {0x0ea1, 0x0ea3, L},\r
+        {0x0ea5, 0x0ea5, L},\r
+        {0x0ea7, 0x0ea7, L},\r
+        {0x0eaa, 0x0eab, L},\r
+        {0x0ead, 0x0eb0, L},\r
+        {0x0eb1, 0x0eb1, NSM},\r
+        {0x0eb2, 0x0eb3, L},\r
+        {0x0eb4, 0x0eb9, NSM},\r
+        {0x0ebb, 0x0ebc, NSM},\r
+        {0x0ebd, 0x0ebd, L},\r
+        {0x0ec0, 0x0ec4, L},\r
+        {0x0ec6, 0x0ec6, L},\r
+        {0x0ec8, 0x0ecd, NSM},\r
+        {0x0ed0, 0x0ed9, L},\r
+        {0x0edc, 0x0edd, L},\r
+        {0x0f00, 0x0f17, L},\r
+        {0x0f18, 0x0f19, NSM},\r
+        {0x0f1a, 0x0f34, L},\r
+        {0x0f35, 0x0f35, NSM},\r
+        {0x0f36, 0x0f36, L},\r
+        {0x0f37, 0x0f37, NSM},\r
+        {0x0f38, 0x0f38, L},\r
+        {0x0f39, 0x0f39, NSM},\r
+        {0x0f3e, 0x0f47, L},\r
+        {0x0f49, 0x0f6a, L},\r
+        {0x0f71, 0x0f7e, NSM},\r
+        {0x0f7f, 0x0f7f, L},\r
+        {0x0f80, 0x0f84, NSM},\r
+        {0x0f85, 0x0f85, L},\r
+        {0x0f86, 0x0f87, NSM},\r
+        {0x0f88, 0x0f8b, L},\r
+        {0x0f90, 0x0f97, NSM},\r
+        {0x0f99, 0x0fbc, NSM},\r
+        {0x0fbe, 0x0fc5, L},\r
+        {0x0fc6, 0x0fc6, NSM},\r
+        {0x0fc7, 0x0fcc, L},\r
+        {0x0fcf, 0x0fcf, L},\r
+        {0x1000, 0x1021, L},\r
+        {0x1023, 0x1027, L},\r
+        {0x1029, 0x102a, L},\r
+        {0x102c, 0x102c, L},\r
+        {0x102d, 0x1030, NSM},\r
+        {0x1031, 0x1031, L},\r
+        {0x1032, 0x1032, NSM},\r
+        {0x1036, 0x1037, NSM},\r
+        {0x1038, 0x1038, L},\r
+        {0x1039, 0x1039, NSM},\r
+        {0x1040, 0x1057, L},\r
+        {0x1058, 0x1059, NSM},\r
+        {0x10a0, 0x10c5, L},\r
+        {0x10d0, 0x10f8, L},\r
+        {0x10fb, 0x10fb, L},\r
+        {0x1100, 0x1159, L},\r
+        {0x115f, 0x11a2, L},\r
+        {0x11a8, 0x11f9, L},\r
+        {0x1200, 0x1206, L},\r
+        {0x1208, 0x1246, L},\r
+        {0x1248, 0x1248, L},\r
+        {0x124a, 0x124d, L},\r
+        {0x1250, 0x1256, L},\r
+        {0x1258, 0x1258, L},\r
+        {0x125a, 0x125d, L},\r
+        {0x1260, 0x1286, L},\r
+        {0x1288, 0x1288, L},\r
+        {0x128a, 0x128d, L},\r
+        {0x1290, 0x12ae, L},\r
+        {0x12b0, 0x12b0, L},\r
+        {0x12b2, 0x12b5, L},\r
+        {0x12b8, 0x12be, L},\r
+        {0x12c0, 0x12c0, L},\r
+        {0x12c2, 0x12c5, L},\r
+        {0x12c8, 0x12ce, L},\r
+        {0x12d0, 0x12d6, L},\r
+        {0x12d8, 0x12ee, L},\r
+        {0x12f0, 0x130e, L},\r
+        {0x1310, 0x1310, L},\r
+        {0x1312, 0x1315, L},\r
+        {0x1318, 0x131e, L},\r
+        {0x1320, 0x1346, L},\r
+        {0x1348, 0x135a, L},\r
+        {0x1361, 0x137c, L},\r
+        {0x13a0, 0x13f4, L},\r
+        {0x1401, 0x1676, L},\r
+        {0x1680, 0x1680, WS},\r
+        {0x1681, 0x169a, L},\r
+        {0x16a0, 0x16f0, L},\r
+        {0x1700, 0x170c, L},\r
+        {0x170e, 0x1711, L},\r
+        {0x1712, 0x1714, NSM},\r
+        {0x1720, 0x1731, L},\r
+        {0x1732, 0x1734, NSM},\r
+        {0x1735, 0x1736, L},\r
+        {0x1740, 0x1751, L},\r
+        {0x1752, 0x1753, NSM},\r
+        {0x1760, 0x176c, L},\r
+        {0x176e, 0x1770, L},\r
+        {0x1772, 0x1773, NSM},\r
+        {0x1780, 0x17b6, L},\r
+        {0x17b7, 0x17bd, NSM},\r
+        {0x17be, 0x17c5, L},\r
+        {0x17c6, 0x17c6, NSM},\r
+        {0x17c7, 0x17c8, L},\r
+        {0x17c9, 0x17d3, NSM},\r
+        {0x17d4, 0x17da, L},\r
+        {0x17db, 0x17db, ET},\r
+        {0x17dc, 0x17dc, L},\r
+        {0x17dd, 0x17dd, NSM},\r
+        {0x17e0, 0x17e9, L},\r
+        {0x180b, 0x180d, NSM},\r
+        {0x180e, 0x180e, WS},\r
+        {0x1810, 0x1819, L},\r
+        {0x1820, 0x1877, L},\r
+        {0x1880, 0x18a8, L},\r
+        {0x18a9, 0x18a9, NSM},\r
+        {0x1900, 0x191c, L},\r
+        {0x1920, 0x1922, NSM},\r
+        {0x1923, 0x1926, L},\r
+        {0x1927, 0x192b, NSM},\r
+        {0x1930, 0x1931, L},\r
+        {0x1932, 0x1932, NSM},\r
+        {0x1933, 0x1938, L},\r
+        {0x1939, 0x193b, NSM},\r
+        {0x1946, 0x196d, L},\r
+        {0x1970, 0x1974, L},\r
+        {0x1d00, 0x1d6b, L},\r
+        {0x1e00, 0x1e9b, L},\r
+        {0x1ea0, 0x1ef9, L},\r
+        {0x1f00, 0x1f15, L},\r
+        {0x1f18, 0x1f1d, L},\r
+        {0x1f20, 0x1f45, L},\r
+        {0x1f48, 0x1f4d, L},\r
+        {0x1f50, 0x1f57, L},\r
+        {0x1f59, 0x1f59, L},\r
+        {0x1f5b, 0x1f5b, L},\r
+        {0x1f5d, 0x1f5d, L},\r
+        {0x1f5f, 0x1f7d, L},\r
+        {0x1f80, 0x1fb4, L},\r
+        {0x1fb6, 0x1fbc, L},\r
+        {0x1fbe, 0x1fbe, L},\r
+        {0x1fc2, 0x1fc4, L},\r
+        {0x1fc6, 0x1fcc, L},\r
+        {0x1fd0, 0x1fd3, L},\r
+        {0x1fd6, 0x1fdb, L},\r
+        {0x1fe0, 0x1fec, L},\r
+        {0x1ff2, 0x1ff4, L},\r
+        {0x1ff6, 0x1ffc, L},\r
+        {0x2000, 0x200a, WS},\r
+        {0x200b, 0x200d, BN},\r
+        {0x200e, 0x200e, L},\r
+        {0x200f, 0x200f, R},\r
+        {0x2028, 0x2028, WS},\r
+        {0x2029, 0x2029, B},\r
+        {0x202a, 0x202a, LRE},\r
+        {0x202b, 0x202b, RLE},\r
+        {0x202c, 0x202c, PDF},\r
+        {0x202d, 0x202d, LRO},\r
+        {0x202e, 0x202e, RLO},\r
+        {0x202f, 0x202f, WS},\r
+        {0x2030, 0x2034, ET},\r
+        {0x2044, 0x2044, CS},\r
+        {0x205f, 0x205f, WS},\r
+        {0x2060, 0x2063, BN},\r
+        {0x206a, 0x206f, BN},\r
+        {0x2070, 0x2070, EN},\r
+        {0x2071, 0x2071, L},\r
+        {0x2074, 0x2079, EN},\r
+        {0x207a, 0x207b, ET},\r
+        {0x207f, 0x207f, L},\r
+        {0x2080, 0x2089, EN},\r
+        {0x208a, 0x208b, ET},\r
+        {0x20a0, 0x20b1, ET},\r
+        {0x20d0, 0x20ea, NSM},\r
+        {0x2102, 0x2102, L},\r
+        {0x2107, 0x2107, L},\r
+        {0x210a, 0x2113, L},\r
+        {0x2115, 0x2115, L},\r
+        {0x2119, 0x211d, L},\r
+        {0x2124, 0x2124, L},\r
+        {0x2126, 0x2126, L},\r
+        {0x2128, 0x2128, L},\r
+        {0x212a, 0x212d, L},\r
+        {0x212e, 0x212e, ET},\r
+        {0x212f, 0x2131, L},\r
+        {0x2133, 0x2139, L},\r
+        {0x213d, 0x213f, L},\r
+        {0x2145, 0x2149, L},\r
+        {0x2160, 0x2183, L},\r
+        {0x2212, 0x2213, ET},\r
+        {0x2336, 0x237a, L},\r
+        {0x2395, 0x2395, L},\r
+        {0x2488, 0x249b, EN},\r
+        {0x249c, 0x24e9, L},\r
+        {0x2800, 0x28ff, L},\r
+        {0x3000, 0x3000, WS},\r
+        {0x3005, 0x3007, L},\r
+        {0x3021, 0x3029, L},\r
+        {0x302a, 0x302f, NSM},\r
+        {0x3031, 0x3035, L},\r
+        {0x3038, 0x303c, L},\r
+        {0x3041, 0x3096, L},\r
+        {0x3099, 0x309a, NSM},\r
+        {0x309d, 0x309f, L},\r
+        {0x30a1, 0x30fa, L},\r
+        {0x30fc, 0x30ff, L},\r
+        {0x3105, 0x312c, L},\r
+        {0x3131, 0x318e, L},\r
+        {0x3190, 0x31b7, L},\r
+        {0x31f0, 0x321c, L},\r
+        {0x3220, 0x3243, L},\r
+        {0x3260, 0x327b, L},\r
+        {0x327f, 0x32b0, L},\r
+        {0x32c0, 0x32cb, L},\r
+        {0x32d0, 0x32fe, L},\r
+        {0x3300, 0x3376, L},\r
+        {0x337b, 0x33dd, L},\r
+        {0x33e0, 0x33fe, L},\r
+        {0x3400, 0x4db5, L},\r
+        {0x4e00, 0x9fa5, L},\r
+        {0xa000, 0xa48c, L},\r
+        {0xac00, 0xd7a3, L},\r
+        {0xd800, 0xfa2d, L},\r
+        {0xfa30, 0xfa6a, L},\r
+        {0xfb00, 0xfb06, L},\r
+        {0xfb13, 0xfb17, L},\r
+        {0xfb1d, 0xfb1d, R},\r
+        {0xfb1e, 0xfb1e, NSM},\r
+        {0xfb1f, 0xfb28, R},\r
+        {0xfb29, 0xfb29, ET},\r
+        {0xfb2a, 0xfb36, R},\r
+        {0xfb38, 0xfb3c, R},\r
+        {0xfb3e, 0xfb3e, R},\r
+        {0xfb40, 0xfb41, R},\r
+        {0xfb43, 0xfb44, R},\r
+        {0xfb46, 0xfb4f, R},\r
+        {0xfb50, 0xfbb1, AL},\r
+        {0xfbd3, 0xfd3d, AL},\r
+        {0xfd50, 0xfd8f, AL},\r
+        {0xfd92, 0xfdc7, AL},\r
+        {0xfdf0, 0xfdfc, AL},\r
+        {0xfe00, 0xfe0f, NSM},\r
+        {0xfe20, 0xfe23, NSM},\r
+        {0xfe50, 0xfe50, CS},\r
+        {0xfe52, 0xfe52, CS},\r
+        {0xfe55, 0xfe55, CS},\r
+        {0xfe5f, 0xfe5f, ET},\r
+        {0xfe62, 0xfe63, ET},\r
+        {0xfe69, 0xfe6a, ET},\r
+        {0xfe70, 0xfe74, AL},\r
+        {0xfe76, 0xfefc, AL},\r
+        {0xfeff, 0xfeff, BN},\r
+        {0xff03, 0xff05, ET},\r
+        {0xff0b, 0xff0b, ET},\r
+        {0xff0c, 0xff0c, CS},\r
+        {0xff0d, 0xff0d, ET},\r
+        {0xff0e, 0xff0e, CS},\r
+        {0xff0f, 0xff0f, ES},\r
+        {0xff10, 0xff19, EN},\r
+        {0xff1a, 0xff1a, CS},\r
+        {0xff21, 0xff3a, L},\r
+        {0xff41, 0xff5a, L},\r
+        {0xff66, 0xffbe, L},\r
+        {0xffc2, 0xffc7, L},\r
+        {0xffca, 0xffcf, L},\r
+        {0xffd2, 0xffd7, L},\r
+        {0xffda, 0xffdc, L},\r
+        {0xffe0, 0xffe1, ET},\r
+        {0xffe5, 0xffe6, ET},\r
+        {0x10000, 0x1000b, L},\r
+        {0x1000d, 0x10026, L},\r
+        {0x10028, 0x1003a, L},\r
+        {0x1003c, 0x1003d, L},\r
+        {0x1003f, 0x1004d, L},\r
+        {0x10050, 0x1005d, L},\r
+        {0x10080, 0x100fa, L},\r
+        {0x10100, 0x10100, L},\r
+        {0x10102, 0x10102, L},\r
+        {0x10107, 0x10133, L},\r
+        {0x10137, 0x1013f, L},\r
+        {0x10300, 0x1031e, L},\r
+        {0x10320, 0x10323, L},\r
+        {0x10330, 0x1034a, L},\r
+        {0x10380, 0x1039d, L},\r
+        {0x1039f, 0x1039f, L},\r
+        {0x10400, 0x1049d, L},\r
+        {0x104a0, 0x104a9, L},\r
+        {0x10800, 0x10805, R},\r
+        {0x10808, 0x10808, R},\r
+        {0x1080a, 0x10835, R},\r
+        {0x10837, 0x10838, R},\r
+        {0x1083c, 0x1083c, R},\r
+        {0x1083f, 0x1083f, R},\r
+        {0x1d000, 0x1d0f5, L},\r
+        {0x1d100, 0x1d126, L},\r
+        {0x1d12a, 0x1d166, L},\r
+        {0x1d167, 0x1d169, NSM},\r
+        {0x1d16a, 0x1d172, L},\r
+        {0x1d173, 0x1d17a, BN},\r
+        {0x1d17b, 0x1d182, NSM},\r
+        {0x1d183, 0x1d184, L},\r
+        {0x1d185, 0x1d18b, NSM},\r
+        {0x1d18c, 0x1d1a9, L},\r
+        {0x1d1aa, 0x1d1ad, NSM},\r
+        {0x1d1ae, 0x1d1dd, L},\r
+        {0x1d400, 0x1d454, L},\r
+        {0x1d456, 0x1d49c, L},\r
+        {0x1d49e, 0x1d49f, L},\r
+        {0x1d4a2, 0x1d4a2, L},\r
+        {0x1d4a5, 0x1d4a6, L},\r
+        {0x1d4a9, 0x1d4ac, L},\r
+        {0x1d4ae, 0x1d4b9, L},\r
+        {0x1d4bb, 0x1d4bb, L},\r
+        {0x1d4bd, 0x1d4c3, L},\r
+        {0x1d4c5, 0x1d505, L},\r
+        {0x1d507, 0x1d50a, L},\r
+        {0x1d50d, 0x1d514, L},\r
+        {0x1d516, 0x1d51c, L},\r
+        {0x1d51e, 0x1d539, L},\r
+        {0x1d53b, 0x1d53e, L},\r
+        {0x1d540, 0x1d544, L},\r
+        {0x1d546, 0x1d546, L},\r
+        {0x1d54a, 0x1d550, L},\r
+        {0x1d552, 0x1d6a3, L},\r
+        {0x1d6a8, 0x1d7c9, L},\r
+        {0x1d7ce, 0x1d7ff, EN},\r
+        {0x20000, 0x2a6d6, L},\r
+        {0x2f800, 0x2fa1d, L},\r
+        {0xe0001, 0xe0001, BN},\r
+        {0xe0020, 0xe007f, BN},\r
+        {0xe0100, 0xe01ef, NSM},\r
+        {0xf0000, 0xffffd, L},\r
+        {0x100000, 0x10fffd, L}\r
+    };\r
+\r
+    int i, j, k;\r
+\r
+    i = -1;\r
+    j = lenof(lookup);\r
+\r
+    while (j - i > 1) {\r
+       k = (i + j) / 2;\r
+       if (ch < lookup[k].first)\r
+           j = k;\r
+       else if (ch > lookup[k].last)\r
+           i = k;\r
+       else\r
+           return lookup[k].type;\r
+    }\r
+\r
+    /*\r
+     * If we reach here, the character was not in any of the\r
+     * intervals listed in the lookup table. This means we return\r
+     * ON (`Other Neutrals'). This is the appropriate code for any\r
+     * character genuinely not listed in the Unicode table, and\r
+     * also the table above has deliberately left out any\r
+     * characters _explicitly_ listed as ON (to save space!).\r
+     */\r
+    return ON;\r
+}\r
+\r
+/*\r
+ * Function exported to front ends to allow them to identify\r
+ * bidi-active characters (in case, for example, the platform's\r
+ * text display function can't conveniently be prevented from doing\r
+ * its own bidi and so special treatment is required for characters\r
+ * that would cause the bidi algorithm to activate).\r
+ * \r
+ * This function is passed a single Unicode code point, and returns\r
+ * nonzero if the presence of this code point can possibly cause\r
+ * the bidi algorithm to do any reordering. Thus, any string\r
+ * composed entirely of characters for which is_rtl() returns zero\r
+ * should be safe to pass to a bidi-active platform display\r
+ * function without fear.\r
+ * \r
+ * (is_rtl() must therefore also return true for any character\r
+ * which would be affected by Arabic shaping, but this isn't\r
+ * important because all such characters are right-to-left so it\r
+ * would have flagged them anyway.)\r
+ */\r
+int is_rtl(int c)\r
+{\r
+    /*\r
+     * After careful reading of the Unicode bidi algorithm (URL as\r
+     * given at the top of this file) I believe that the only\r
+     * character classes which can possibly cause trouble are R,\r
+     * AL, RLE and RLO. I think that any string containing no\r
+     * character in any of those classes will be displayed\r
+     * uniformly left-to-right by the Unicode bidi algorithm.\r
+     */\r
+    const int mask = (1<<R) | (1<<AL) | (1<<RLE) | (1<<RLO);\r
+\r
+    return mask & (1 << (getType(c)));\r
+}\r
+\r
+/*\r
+ * The most significant 2 bits of each level are used to store\r
+ * Override status of each character\r
+ * This function sets the override bits of level according\r
+ * to the value in override, and reurns the new byte.\r
+ */\r
+unsigned char setOverrideBits(unsigned char level, unsigned char override)\r
+{\r
+    if (override == ON)\r
+       return level;\r
+    else if (override == R)\r
+       return level | OISR;\r
+    else if (override == L)\r
+       return level | OISL;\r
+    return level;\r
+}\r
+\r
+/*\r
+ * Find the most recent run of the same value in `level', and\r
+ * return the value _before_ it. Used to process U+202C POP\r
+ * DIRECTIONAL FORMATTING.\r
+ */\r
+int getPreviousLevel(unsigned char* level, int from)\r
+{\r
+    if (from > 0) {\r
+        unsigned char current = level[--from];\r
+\r
+        while (from >= 0 && level[from] == current)\r
+            from--;\r
+\r
+        if (from >= 0)\r
+            return level[from];\r
+\r
+        return -1;\r
+    } else\r
+        return -1;\r
+}\r
+\r
+/* The Main shaping function, and the only one to be used\r
+ * by the outside world.\r
+ *\r
+ * line: buffer to apply shaping to. this must be passed by doBidi() first\r
+ * to: output buffer for the shaped data\r
+ * count: number of characters in line\r
+ */\r
+int do_shape(bidi_char *line, bidi_char *to, int count)\r
+{\r
+    int i, tempShape, ligFlag;\r
+\r
+    for (ligFlag=i=0; i<count; i++) {\r
+       to[i] = line[i];\r
+       tempShape = STYPE(line[i].wc);\r
+       switch (tempShape) {\r
+         case SC:\r
+           break;\r
+\r
+         case SU:\r
+           break;\r
+\r
+         case SR:\r
+           tempShape = (i+1 < count ? STYPE(line[i+1].wc) : SU);\r
+           if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC))\r
+               to[i].wc = SFINAL((SISOLATED(line[i].wc)));\r
+           else\r
+               to[i].wc = SISOLATED(line[i].wc);\r
+           break;\r
+\r
+\r
+         case SD:\r
+           /* Make Ligatures */\r
+           tempShape = (i+1 < count ? STYPE(line[i+1].wc) : SU);\r
+           if (line[i].wc == 0x644) {\r
+               if (i > 0) switch (line[i-1].wc) {\r
+                 case 0x622:\r
+                   ligFlag = 1;\r
+                   if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC))\r
+                       to[i].wc = 0xFEF6;\r
+                   else\r
+                       to[i].wc = 0xFEF5;\r
+                   break;\r
+                 case 0x623:\r
+                   ligFlag = 1;\r
+                   if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC))\r
+                       to[i].wc = 0xFEF8;\r
+                   else\r
+                       to[i].wc = 0xFEF7;\r
+                   break;\r
+                 case 0x625:\r
+                   ligFlag = 1;\r
+                   if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC))\r
+                       to[i].wc = 0xFEFA;\r
+                   else\r
+                       to[i].wc = 0xFEF9;\r
+                   break;\r
+                 case 0x627:\r
+                   ligFlag = 1;\r
+                   if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC))\r
+                       to[i].wc = 0xFEFC;\r
+                   else\r
+                       to[i].wc = 0xFEFB;\r
+                   break;\r
+               }\r
+               if (ligFlag) {\r
+                   to[i-1].wc = 0x20;\r
+                   ligFlag = 0;\r
+                   break;\r
+               }\r
+           }\r
+\r
+           if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) {\r
+                tempShape = (i > 0 ? STYPE(line[i-1].wc) : SU);\r
+               if ((tempShape == SR) || (tempShape == SD) || (tempShape == SC))\r
+                   to[i].wc = SMEDIAL((SISOLATED(line[i].wc)));\r
+               else\r
+                   to[i].wc = SFINAL((SISOLATED(line[i].wc)));\r
+               break;\r
+           }\r
+\r
+            tempShape = (i > 0 ? STYPE(line[i-1].wc) : SU);\r
+           if ((tempShape == SR) || (tempShape == SD) || (tempShape == SC))\r
+               to[i].wc = SINITIAL((SISOLATED(line[i].wc)));\r
+           else\r
+               to[i].wc = SISOLATED(line[i].wc);\r
+           break;\r
+\r
+\r
+       }\r
+    }\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * The Main Bidi Function, and the only function that should\r
+ * be used by the outside world.\r
+ *\r
+ * line: a buffer of size count containing text to apply\r
+ * the Bidirectional algorithm to.\r
+ */\r
+\r
+int do_bidi(bidi_char *line, int count)\r
+{\r
+    unsigned char* types;\r
+    unsigned char* levels;\r
+    unsigned char paragraphLevel;\r
+    unsigned char currentEmbedding;\r
+    unsigned char currentOverride;\r
+    unsigned char tempType;\r
+    int i, j, yes, bover;\r
+\r
+    /* Check the presence of R or AL types as optimization */\r
+    yes = 0;\r
+    for (i=0; i<count; i++) {\r
+       int type = getType(line[i].wc);\r
+       if (type == R || type == AL) {\r
+           yes = 1;\r
+           break;\r
+       }\r
+    }\r
+    if (yes == 0)\r
+       return L;\r
+\r
+    /* Initialize types, levels */\r
+    types = snewn(count, unsigned char);\r
+    levels = snewn(count, unsigned char);\r
+\r
+    /* Rule (P1)  NOT IMPLEMENTED\r
+     * P1. Split the text into separate paragraphs. A paragraph separator is\r
+     * kept with the previous paragraph. Within each paragraph, apply all the\r
+     * other rules of this algorithm.\r
+     */\r
+\r
+    /* Rule (P2), (P3)\r
+     * P2. In each paragraph, find the first character of type L, AL, or R.\r
+     * P3. If a character is found in P2 and it is of type AL or R, then set\r
+     * the paragraph embedding level to one; otherwise, set it to zero.\r
+     */\r
+    paragraphLevel = 0;\r
+    for (i=0; i<count ; i++) {\r
+       int type = getType(line[i].wc);\r
+       if (type == R || type == AL) {\r
+           paragraphLevel = 1;\r
+           break;\r
+       } else if (type == L)\r
+           break;\r
+    }\r
+\r
+    /* Rule (X1)\r
+     * X1. Begin by setting the current embedding level to the paragraph\r
+     * embedding level. Set the directional override status to neutral.\r
+     */\r
+    currentEmbedding = paragraphLevel;\r
+    currentOverride = ON;\r
+\r
+    /* Rule (X2), (X3), (X4), (X5), (X6), (X7), (X8)\r
+     * X2. With each RLE, compute the least greater odd embedding level.\r
+     * X3. With each LRE, compute the least greater even embedding level.\r
+     * X4. With each RLO, compute the least greater odd embedding level.\r
+     * X5. With each LRO, compute the least greater even embedding level.\r
+     * X6. For all types besides RLE, LRE, RLO, LRO, and PDF:\r
+     *         a. Set the level of the current character to the current\r
+     *             embedding level.\r
+     *         b.  Whenever the directional override status is not neutral,\r
+     *               reset the current character type to the directional\r
+     *               override status.\r
+     * X7. With each PDF, determine the matching embedding or override code.\r
+     * If there was a valid matching code, restore (pop) the last\r
+     * remembered (pushed) embedding level and directional override.\r
+     * X8. All explicit directional embeddings and overrides are completely\r
+     * terminated at the end of each paragraph. Paragraph separators are not\r
+     * included in the embedding. (Useless here) NOT IMPLEMENTED\r
+     */\r
+    bover = 0;\r
+    for (i=0; i<count; i++) {\r
+       tempType = getType(line[i].wc);\r
+       switch (tempType) {\r
+         case RLE:\r
+           currentEmbedding = levels[i] = leastGreaterOdd(currentEmbedding);\r
+           levels[i] = setOverrideBits(levels[i], currentOverride);\r
+           currentOverride = ON;\r
+           break;\r
+\r
+         case LRE:\r
+           currentEmbedding = levels[i] = leastGreaterEven(currentEmbedding);\r
+           levels[i] = setOverrideBits(levels[i], currentOverride);\r
+           currentOverride = ON;\r
+           break;\r
+\r
+         case RLO:\r
+           currentEmbedding = levels[i] = leastGreaterOdd(currentEmbedding);\r
+           tempType = currentOverride = R;\r
+           bover = 1;\r
+           break;\r
+\r
+         case LRO:\r
+           currentEmbedding = levels[i] = leastGreaterEven(currentEmbedding);\r
+           tempType = currentOverride = L;\r
+           bover = 1;\r
+           break;\r
+\r
+         case PDF:\r
+            {\r
+                int prevlevel = getPreviousLevel(levels, i);\r
+\r
+                if (prevlevel == -1) {\r
+                    currentEmbedding = paragraphLevel;\r
+                    currentOverride = ON;\r
+                } else {\r
+                    currentOverride = currentEmbedding & OMASK;\r
+                    currentEmbedding = currentEmbedding & ~OMASK;\r
+                }\r
+            }\r
+           levels[i] = currentEmbedding;\r
+           break;\r
+\r
+           /* Whitespace is treated as neutral for now */\r
+         case WS:\r
+         case S:\r
+           levels[i] = currentEmbedding;\r
+           tempType = ON;\r
+           if (currentOverride != ON)\r
+               tempType = currentOverride;\r
+           break;\r
+\r
+         default:\r
+           levels[i] = currentEmbedding;\r
+           if (currentOverride != ON)\r
+               tempType = currentOverride;\r
+           break;\r
+\r
+       }\r
+       types[i] = tempType;\r
+    }\r
+    /* this clears out all overrides, so we can use levels safely... */\r
+    /* checks bover first */\r
+    if (bover)\r
+       for (i=0; i<count; i++)\r
+           levels[i] = levels[i] & LMASK;\r
+\r
+    /* Rule (X9)\r
+     * X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.\r
+     * Here, they're converted to BN.\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       switch (types[i]) {\r
+         case RLE:\r
+         case LRE:\r
+         case RLO:\r
+         case LRO:\r
+         case PDF:\r
+           types[i] = BN;\r
+           break;\r
+       }\r
+    }\r
+\r
+    /* Rule (W1)\r
+     * W1. Examine each non-spacing mark (NSM) in the level run, and change\r
+     * the type of the NSM to the type of the previous character. If the NSM\r
+     * is at the start of the level run, it will get the type of sor.\r
+     */\r
+    if (types[0] == NSM)\r
+       types[0] = paragraphLevel;\r
+\r
+    for (i=1; i<count; i++) {\r
+       if (types[i] == NSM)\r
+           types[i] = types[i-1];\r
+       /* Is this a safe assumption?\r
+        * I assumed the previous, IS a character.\r
+        */\r
+    }\r
+\r
+    /* Rule (W2)\r
+     * W2. Search backwards from each instance of a European number until the\r
+     * first strong type (R, L, AL, or sor) is found.  If an AL is found,\r
+     * change the type of the European number to Arabic number.\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       if (types[i] == EN) {\r
+           j=i;\r
+           while (j >= 0) {\r
+               if (types[j] == AL) {\r
+                   types[i] = AN;\r
+                   break;\r
+               } else if (types[j] == R || types[j] == L) {\r
+                    break;\r
+                }\r
+               j--;\r
+           }\r
+       }\r
+    }\r
+\r
+    /* Rule (W3)\r
+     * W3. Change all ALs to R.\r
+     *\r
+     * Optimization: on Rule Xn, we might set a flag on AL type\r
+     * to prevent this loop in L R lines only...\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       if (types[i] == AL)\r
+           types[i] = R;\r
+    }\r
+\r
+    /* Rule (W4)\r
+     * W4. A single European separator between two European numbers changes\r
+     * to a European number. A single common separator between two numbers\r
+     * of the same type changes to that type.\r
+     */\r
+    for (i=1; i<(count-1); i++) {\r
+       if (types[i] == ES) {\r
+           if (types[i-1] == EN && types[i+1] == EN)\r
+               types[i] = EN;\r
+       } else if (types[i] == CS) {\r
+            if (types[i-1] == EN && types[i+1] == EN)\r
+                types[i] = EN;\r
+            else if (types[i-1] == AN && types[i+1] == AN)\r
+                types[i] = AN;\r
+        }\r
+    }\r
+\r
+    /* Rule (W5)\r
+     * W5. A sequence of European terminators adjacent to European numbers\r
+     * changes to all European numbers.\r
+     *\r
+     * Optimization: lots here... else ifs need rearrangement\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       if (types[i] == ET) {\r
+           if (i > 0 && types[i-1] == EN) {\r
+               types[i] = EN;\r
+               continue;\r
+           } else if (i < count-1 && types[i+1] == EN) {\r
+                types[i] = EN;\r
+                continue;\r
+            } else if (i < count-1 && types[i+1] == ET) {\r
+                j=i;\r
+                while (j <count && types[j] == ET) {\r
+                    j++;\r
+                }\r
+                if (types[j] == EN)\r
+                    types[i] = EN;\r
+            }\r
+       }\r
+    }\r
+\r
+    /* Rule (W6)\r
+     * W6. Otherwise, separators and terminators change to Other Neutral:\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       switch (types[i]) {\r
+         case ES:\r
+         case ET:\r
+         case CS:\r
+           types[i] = ON;\r
+           break;\r
+       }\r
+    }\r
+\r
+    /* Rule (W7)\r
+     * W7. Search backwards from each instance of a European number until\r
+     * the first strong type (R, L, or sor) is found. If an L is found,\r
+     * then change the type of the European number to L.\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       if (types[i] == EN) {\r
+           j=i;\r
+           while (j >= 0) {\r
+               if (types[j] == L) {\r
+                   types[i] = L;\r
+                   break;\r
+               } else if (types[j] == R || types[j] == AL) {\r
+                   break;\r
+               }\r
+               j--;\r
+           }\r
+       }\r
+    }\r
+\r
+    /* Rule (N1)\r
+     * N1. A sequence of neutrals takes the direction of the surrounding\r
+     * strong text if the text on both sides has the same direction. European\r
+     * and Arabic numbers are treated as though they were R.\r
+     */\r
+    if (count >= 2 && types[0] == ON) {\r
+       if ((types[1] == R) || (types[1] == EN) || (types[1] == AN))\r
+           types[0] = R;\r
+       else if (types[1] == L)\r
+           types[0] = L;\r
+    }\r
+    for (i=1; i<(count-1); i++) {\r
+       if (types[i] == ON) {\r
+           if (types[i-1] == L) {\r
+               j=i;\r
+               while (j<(count-1) && types[j] == ON) {\r
+                   j++;\r
+               }\r
+               if (types[j] == L) {\r
+                   while (i<j) {\r
+                       types[i] = L;\r
+                       i++;\r
+                   }\r
+               }\r
+\r
+           } else if ((types[i-1] == R)  ||\r
+                       (types[i-1] == EN) ||\r
+                       (types[i-1] == AN)) {\r
+                j=i;\r
+                while (j<(count-1) && types[j] == ON) {\r
+                    j++;\r
+                }\r
+                if ((types[j] == R)  ||\r
+                    (types[j] == EN) ||\r
+                    (types[j] == AN)) {\r
+                    while (i<j) {\r
+                        types[i] = R;\r
+                        i++;\r
+                    }\r
+                }\r
+            }\r
+       }\r
+    }\r
+    if (count >= 2 && types[count-1] == ON) {\r
+       if (types[count-2] == R || types[count-2] == EN || types[count-2] == AN)\r
+           types[count-1] = R;\r
+       else if (types[count-2] == L)\r
+           types[count-1] = L;\r
+    }\r
+\r
+    /* Rule (N2)\r
+     * N2. Any remaining neutrals take the embedding direction.\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       if (types[i] == ON) {\r
+           if ((levels[i] % 2) == 0)\r
+               types[i] = L;\r
+           else\r
+               types[i] = R;\r
+       }\r
+    }\r
+\r
+    /* Rule (I1)\r
+     * I1. For all characters with an even (left-to-right) embedding\r
+     * direction, those of type R go up one level and those of type AN or\r
+     * EN go up two levels.\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       if ((levels[i] % 2) == 0) {\r
+           if (types[i] == R)\r
+               levels[i] += 1;\r
+           else if (types[i] == AN || types[i] == EN)\r
+               levels[i] += 2;\r
+       }\r
+    }\r
+\r
+    /* Rule (I2)\r
+     * I2. For all characters with an odd (right-to-left) embedding direction,\r
+     * those of type L, EN or AN go up one level.\r
+     */\r
+    for (i=0; i<count; i++) {\r
+       if ((levels[i] % 2) == 1) {\r
+           if (types[i] == L || types[i] == EN || types[i] == AN)\r
+               levels[i] += 1;\r
+       }\r
+    }\r
+\r
+    /* Rule (L1)\r
+     * L1. On each line, reset the embedding level of the following characters\r
+     * to the paragraph embedding level:\r
+     *         (1)segment separators, (2)paragraph separators,\r
+     *           (3)any sequence of whitespace characters preceding\r
+     *           a segment separator or paragraph separator,\r
+     *           (4)and any sequence of white space characters\r
+     *           at the end of the line.\r
+     * The types of characters used here are the original types, not those\r
+     * modified by the previous phase.\r
+     */\r
+    j=count-1;\r
+    while (j>0 && (getType(line[j].wc) == WS)) {\r
+       j--;\r
+    }\r
+    if (j < (count-1)) {\r
+       for (j++; j<count; j++)\r
+           levels[j] = paragraphLevel;\r
+    }\r
+    for (i=0; i<count; i++) {\r
+       tempType = getType(line[i].wc);\r
+       if (tempType == WS) {\r
+           j=i;\r
+           while (j<count && (getType(line[j].wc) == WS)) {\r
+               j++;\r
+           }\r
+           if (j==count || getType(line[j].wc) == B ||\r
+                getType(line[j].wc) == S) {\r
+               for (j--; j>=i ; j--) {\r
+                   levels[j] = paragraphLevel;\r
+               }\r
+           }\r
+       } else if (tempType == B || tempType == S) {\r
+            levels[i] = paragraphLevel;\r
+        }\r
+    }\r
+\r
+    /* Rule (L4) NOT IMPLEMENTED\r
+     * L4. A character that possesses the mirrored property as specified by\r
+     * Section 4.7, Mirrored, must be depicted by a mirrored glyph if the\r
+     * resolved directionality of that character is R.\r
+     */\r
+    /* Note: this is implemented before L2 for efficiency */\r
+    for (i=0; i<count; i++)\r
+       if ((levels[i] % 2) == 1)\r
+           doMirror(&line[i].wc);\r
+\r
+    /* Rule (L2)\r
+     * L2. From the highest level found in the text to the lowest odd level on\r
+     * each line, including intermediate levels not actually present in the\r
+     * text, reverse any contiguous sequence of characters that are at that\r
+     * level or higher\r
+     */\r
+    /* we flip the character string and leave the level array */\r
+    i=0;\r
+    tempType = levels[0];\r
+    while (i < count) {\r
+       if (levels[i] > tempType)\r
+           tempType = levels[i];\r
+       i++;\r
+    }\r
+    /* maximum level in tempType. */\r
+    while (tempType > 0) {     /* loop from highest level to the least odd, */\r
+        /* which i assume is 1 */\r
+       flipThisRun(line, levels, tempType, count);\r
+       tempType--;\r
+    }\r
+\r
+    /* Rule (L3) NOT IMPLEMENTED\r
+     * L3. Combining marks applied to a right-to-left base character will at\r
+     * this point precede their base character. If the rendering engine\r
+     * expects them to follow the base characters in the final display\r
+     * process, then the ordering of the marks and the base character must\r
+     * be reversed.\r
+     */\r
+    sfree(types);\r
+    sfree(levels);\r
+    return R;\r
+}\r
+\r
+\r
+/*\r
+ * Bad, Horrible function\r
+ * takes a pointer to a character that is checked for\r
+ * having a mirror glyph.\r
+ */\r
+void doMirror(wchar_t* ch)\r
+{\r
+    if ((*ch & 0xFF00) == 0) {\r
+       switch (*ch) {\r
+         case 0x0028: *ch = 0x0029; break;\r
+         case 0x0029: *ch = 0x0028; break;\r
+         case 0x003C: *ch = 0x003E; break;\r
+         case 0x003E: *ch = 0x003C; break;\r
+         case 0x005B: *ch = 0x005D; break;\r
+         case 0x005D: *ch = 0x005B; break;\r
+         case 0x007B: *ch = 0x007D; break;\r
+         case 0x007D: *ch = 0x007B; break;\r
+         case 0x00AB: *ch = 0x00BB; break;\r
+         case 0x00BB: *ch = 0x00AB; break;\r
+       }\r
+    } else if ((*ch & 0xFF00) == 0x2000) {\r
+       switch (*ch) {\r
+         case 0x2039: *ch = 0x203A; break;\r
+         case 0x203A: *ch = 0x2039; break;\r
+         case 0x2045: *ch = 0x2046; break;\r
+         case 0x2046: *ch = 0x2045; break;\r
+         case 0x207D: *ch = 0x207E; break;\r
+         case 0x207E: *ch = 0x207D; break;\r
+         case 0x208D: *ch = 0x208E; break;\r
+         case 0x208E: *ch = 0x208D; break;\r
+       }\r
+    } else if ((*ch & 0xFF00) == 0x2200) {\r
+       switch (*ch) {\r
+         case 0x2208: *ch = 0x220B; break;\r
+         case 0x2209: *ch = 0x220C; break;\r
+         case 0x220A: *ch = 0x220D; break;\r
+         case 0x220B: *ch = 0x2208; break;\r
+         case 0x220C: *ch = 0x2209; break;\r
+         case 0x220D: *ch = 0x220A; break;\r
+         case 0x2215: *ch = 0x29F5; break;\r
+         case 0x223C: *ch = 0x223D; break;\r
+         case 0x223D: *ch = 0x223C; break;\r
+         case 0x2243: *ch = 0x22CD; break;\r
+         case 0x2252: *ch = 0x2253; break;\r
+         case 0x2253: *ch = 0x2252; break;\r
+         case 0x2254: *ch = 0x2255; break;\r
+         case 0x2255: *ch = 0x2254; break;\r
+         case 0x2264: *ch = 0x2265; break;\r
+         case 0x2265: *ch = 0x2264; break;\r
+         case 0x2266: *ch = 0x2267; break;\r
+         case 0x2267: *ch = 0x2266; break;\r
+         case 0x2268: *ch = 0x2269; break;\r
+         case 0x2269: *ch = 0x2268; break;\r
+         case 0x226A: *ch = 0x226B; break;\r
+         case 0x226B: *ch = 0x226A; break;\r
+         case 0x226E: *ch = 0x226F; break;\r
+         case 0x226F: *ch = 0x226E; break;\r
+         case 0x2270: *ch = 0x2271; break;\r
+         case 0x2271: *ch = 0x2270; break;\r
+         case 0x2272: *ch = 0x2273; break;\r
+         case 0x2273: *ch = 0x2272; break;\r
+         case 0x2274: *ch = 0x2275; break;\r
+         case 0x2275: *ch = 0x2274; break;\r
+         case 0x2276: *ch = 0x2277; break;\r
+         case 0x2277: *ch = 0x2276; break;\r
+         case 0x2278: *ch = 0x2279; break;\r
+         case 0x2279: *ch = 0x2278; break;\r
+         case 0x227A: *ch = 0x227B; break;\r
+         case 0x227B: *ch = 0x227A; break;\r
+         case 0x227C: *ch = 0x227D; break;\r
+         case 0x227D: *ch = 0x227C; break;\r
+         case 0x227E: *ch = 0x227F; break;\r
+         case 0x227F: *ch = 0x227E; break;\r
+         case 0x2280: *ch = 0x2281; break;\r
+         case 0x2281: *ch = 0x2280; break;\r
+         case 0x2282: *ch = 0x2283; break;\r
+         case 0x2283: *ch = 0x2282; break;\r
+         case 0x2284: *ch = 0x2285; break;\r
+         case 0x2285: *ch = 0x2284; break;\r
+         case 0x2286: *ch = 0x2287; break;\r
+         case 0x2287: *ch = 0x2286; break;\r
+         case 0x2288: *ch = 0x2289; break;\r
+         case 0x2289: *ch = 0x2288; break;\r
+         case 0x228A: *ch = 0x228B; break;\r
+         case 0x228B: *ch = 0x228A; break;\r
+         case 0x228F: *ch = 0x2290; break;\r
+         case 0x2290: *ch = 0x228F; break;\r
+         case 0x2291: *ch = 0x2292; break;\r
+         case 0x2292: *ch = 0x2291; break;\r
+         case 0x2298: *ch = 0x29B8; break;\r
+         case 0x22A2: *ch = 0x22A3; break;\r
+         case 0x22A3: *ch = 0x22A2; break;\r
+         case 0x22A6: *ch = 0x2ADE; break;\r
+         case 0x22A8: *ch = 0x2AE4; break;\r
+         case 0x22A9: *ch = 0x2AE3; break;\r
+         case 0x22AB: *ch = 0x2AE5; break;\r
+         case 0x22B0: *ch = 0x22B1; break;\r
+         case 0x22B1: *ch = 0x22B0; break;\r
+         case 0x22B2: *ch = 0x22B3; break;\r
+         case 0x22B3: *ch = 0x22B2; break;\r
+         case 0x22B4: *ch = 0x22B5; break;\r
+         case 0x22B5: *ch = 0x22B4; break;\r
+         case 0x22B6: *ch = 0x22B7; break;\r
+         case 0x22B7: *ch = 0x22B6; break;\r
+         case 0x22C9: *ch = 0x22CA; break;\r
+         case 0x22CA: *ch = 0x22C9; break;\r
+         case 0x22CB: *ch = 0x22CC; break;\r
+         case 0x22CC: *ch = 0x22CB; break;\r
+         case 0x22CD: *ch = 0x2243; break;\r
+         case 0x22D0: *ch = 0x22D1; break;\r
+         case 0x22D1: *ch = 0x22D0; break;\r
+         case 0x22D6: *ch = 0x22D7; break;\r
+         case 0x22D7: *ch = 0x22D6; break;\r
+         case 0x22D8: *ch = 0x22D9; break;\r
+         case 0x22D9: *ch = 0x22D8; break;\r
+         case 0x22DA: *ch = 0x22DB; break;\r
+         case 0x22DB: *ch = 0x22DA; break;\r
+         case 0x22DC: *ch = 0x22DD; break;\r
+         case 0x22DD: *ch = 0x22DC; break;\r
+         case 0x22DE: *ch = 0x22DF; break;\r
+         case 0x22DF: *ch = 0x22DE; break;\r
+         case 0x22E0: *ch = 0x22E1; break;\r
+         case 0x22E1: *ch = 0x22E0; break;\r
+         case 0x22E2: *ch = 0x22E3; break;\r
+         case 0x22E3: *ch = 0x22E2; break;\r
+         case 0x22E4: *ch = 0x22E5; break;\r
+         case 0x22E5: *ch = 0x22E4; break;\r
+         case 0x22E6: *ch = 0x22E7; break;\r
+         case 0x22E7: *ch = 0x22E6; break;\r
+         case 0x22E8: *ch = 0x22E9; break;\r
+         case 0x22E9: *ch = 0x22E8; break;\r
+         case 0x22EA: *ch = 0x22EB; break;\r
+         case 0x22EB: *ch = 0x22EA; break;\r
+         case 0x22EC: *ch = 0x22ED; break;\r
+         case 0x22ED: *ch = 0x22EC; break;\r
+         case 0x22F0: *ch = 0x22F1; break;\r
+         case 0x22F1: *ch = 0x22F0; break;\r
+         case 0x22F2: *ch = 0x22FA; break;\r
+         case 0x22F3: *ch = 0x22FB; break;\r
+         case 0x22F4: *ch = 0x22FC; break;\r
+         case 0x22F6: *ch = 0x22FD; break;\r
+         case 0x22F7: *ch = 0x22FE; break;\r
+         case 0x22FA: *ch = 0x22F2; break;\r
+         case 0x22FB: *ch = 0x22F3; break;\r
+         case 0x22FC: *ch = 0x22F4; break;\r
+         case 0x22FD: *ch = 0x22F6; break;\r
+         case 0x22FE: *ch = 0x22F7; break;\r
+       }\r
+    } else if ((*ch & 0xFF00) == 0x2300) {\r
+        switch (*ch) {\r
+          case 0x2308: *ch = 0x2309; break;\r
+          case 0x2309: *ch = 0x2308; break;\r
+          case 0x230A: *ch = 0x230B; break;\r
+          case 0x230B: *ch = 0x230A; break;\r
+          case 0x2329: *ch = 0x232A; break;\r
+          case 0x232A: *ch = 0x2329; break;\r
+        }\r
+    } else if ((*ch & 0xFF00) == 0x2700) {\r
+       switch (*ch) {\r
+         case 0x2768: *ch = 0x2769; break;\r
+         case 0x2769: *ch = 0x2768; break;\r
+         case 0x276A: *ch = 0x276B; break;\r
+         case 0x276B: *ch = 0x276A; break;\r
+         case 0x276C: *ch = 0x276D; break;\r
+         case 0x276D: *ch = 0x276C; break;\r
+         case 0x276E: *ch = 0x276F; break;\r
+         case 0x276F: *ch = 0x276E; break;\r
+         case 0x2770: *ch = 0x2771; break;\r
+         case 0x2771: *ch = 0x2770; break;\r
+         case 0x2772: *ch = 0x2773; break;\r
+         case 0x2773: *ch = 0x2772; break;\r
+         case 0x2774: *ch = 0x2775; break;\r
+         case 0x2775: *ch = 0x2774; break;\r
+         case 0x27D5: *ch = 0x27D6; break;\r
+         case 0x27D6: *ch = 0x27D5; break;\r
+         case 0x27DD: *ch = 0x27DE; break;\r
+         case 0x27DE: *ch = 0x27DD; break;\r
+         case 0x27E2: *ch = 0x27E3; break;\r
+         case 0x27E3: *ch = 0x27E2; break;\r
+         case 0x27E4: *ch = 0x27E5; break;\r
+         case 0x27E5: *ch = 0x27E4; break;\r
+         case 0x27E6: *ch = 0x27E7; break;\r
+         case 0x27E7: *ch = 0x27E6; break;\r
+         case 0x27E8: *ch = 0x27E9; break;\r
+         case 0x27E9: *ch = 0x27E8; break;\r
+         case 0x27EA: *ch = 0x27EB; break;\r
+         case 0x27EB: *ch = 0x27EA; break;\r
+       }\r
+    } else if ((*ch & 0xFF00) == 0x2900) {\r
+       switch (*ch) {\r
+         case 0x2983: *ch = 0x2984; break;\r
+         case 0x2984: *ch = 0x2983; break;\r
+         case 0x2985: *ch = 0x2986; break;\r
+         case 0x2986: *ch = 0x2985; break;\r
+         case 0x2987: *ch = 0x2988; break;\r
+         case 0x2988: *ch = 0x2987; break;\r
+         case 0x2989: *ch = 0x298A; break;\r
+         case 0x298A: *ch = 0x2989; break;\r
+         case 0x298B: *ch = 0x298C; break;\r
+         case 0x298C: *ch = 0x298B; break;\r
+         case 0x298D: *ch = 0x2990; break;\r
+         case 0x298E: *ch = 0x298F; break;\r
+         case 0x298F: *ch = 0x298E; break;\r
+         case 0x2990: *ch = 0x298D; break;\r
+         case 0x2991: *ch = 0x2992; break;\r
+         case 0x2992: *ch = 0x2991; break;\r
+         case 0x2993: *ch = 0x2994; break;\r
+         case 0x2994: *ch = 0x2993; break;\r
+         case 0x2995: *ch = 0x2996; break;\r
+         case 0x2996: *ch = 0x2995; break;\r
+         case 0x2997: *ch = 0x2998; break;\r
+         case 0x2998: *ch = 0x2997; break;\r
+         case 0x29B8: *ch = 0x2298; break;\r
+         case 0x29C0: *ch = 0x29C1; break;\r
+         case 0x29C1: *ch = 0x29C0; break;\r
+         case 0x29C4: *ch = 0x29C5; break;\r
+         case 0x29C5: *ch = 0x29C4; break;\r
+         case 0x29CF: *ch = 0x29D0; break;\r
+         case 0x29D0: *ch = 0x29CF; break;\r
+         case 0x29D1: *ch = 0x29D2; break;\r
+         case 0x29D2: *ch = 0x29D1; break;\r
+         case 0x29D4: *ch = 0x29D5; break;\r
+         case 0x29D5: *ch = 0x29D4; break;\r
+         case 0x29D8: *ch = 0x29D9; break;\r
+         case 0x29D9: *ch = 0x29D8; break;\r
+         case 0x29DA: *ch = 0x29DB; break;\r
+         case 0x29DB: *ch = 0x29DA; break;\r
+         case 0x29F5: *ch = 0x2215; break;\r
+         case 0x29F8: *ch = 0x29F9; break;\r
+         case 0x29F9: *ch = 0x29F8; break;\r
+         case 0x29FC: *ch = 0x29FD; break;\r
+         case 0x29FD: *ch = 0x29FC; break;\r
+       }\r
+    } else if ((*ch & 0xFF00) == 0x2A00) {\r
+       switch (*ch) {\r
+         case 0x2A2B: *ch = 0x2A2C; break;\r
+         case 0x2A2C: *ch = 0x2A2B; break;\r
+         case 0x2A2D: *ch = 0x2A2C; break;\r
+         case 0x2A2E: *ch = 0x2A2D; break;\r
+         case 0x2A34: *ch = 0x2A35; break;\r
+         case 0x2A35: *ch = 0x2A34; break;\r
+         case 0x2A3C: *ch = 0x2A3D; break;\r
+         case 0x2A3D: *ch = 0x2A3C; break;\r
+         case 0x2A64: *ch = 0x2A65; break;\r
+         case 0x2A65: *ch = 0x2A64; break;\r
+         case 0x2A79: *ch = 0x2A7A; break;\r
+         case 0x2A7A: *ch = 0x2A79; break;\r
+         case 0x2A7D: *ch = 0x2A7E; break;\r
+         case 0x2A7E: *ch = 0x2A7D; break;\r
+         case 0x2A7F: *ch = 0x2A80; break;\r
+         case 0x2A80: *ch = 0x2A7F; break;\r
+         case 0x2A81: *ch = 0x2A82; break;\r
+         case 0x2A82: *ch = 0x2A81; break;\r
+         case 0x2A83: *ch = 0x2A84; break;\r
+         case 0x2A84: *ch = 0x2A83; break;\r
+         case 0x2A8B: *ch = 0x2A8C; break;\r
+         case 0x2A8C: *ch = 0x2A8B; break;\r
+         case 0x2A91: *ch = 0x2A92; break;\r
+         case 0x2A92: *ch = 0x2A91; break;\r
+         case 0x2A93: *ch = 0x2A94; break;\r
+         case 0x2A94: *ch = 0x2A93; break;\r
+         case 0x2A95: *ch = 0x2A96; break;\r
+         case 0x2A96: *ch = 0x2A95; break;\r
+         case 0x2A97: *ch = 0x2A98; break;\r
+         case 0x2A98: *ch = 0x2A97; break;\r
+         case 0x2A99: *ch = 0x2A9A; break;\r
+         case 0x2A9A: *ch = 0x2A99; break;\r
+         case 0x2A9B: *ch = 0x2A9C; break;\r
+         case 0x2A9C: *ch = 0x2A9B; break;\r
+         case 0x2AA1: *ch = 0x2AA2; break;\r
+         case 0x2AA2: *ch = 0x2AA1; break;\r
+         case 0x2AA6: *ch = 0x2AA7; break;\r
+         case 0x2AA7: *ch = 0x2AA6; break;\r
+         case 0x2AA8: *ch = 0x2AA9; break;\r
+         case 0x2AA9: *ch = 0x2AA8; break;\r
+         case 0x2AAA: *ch = 0x2AAB; break;\r
+         case 0x2AAB: *ch = 0x2AAA; break;\r
+         case 0x2AAC: *ch = 0x2AAD; break;\r
+         case 0x2AAD: *ch = 0x2AAC; break;\r
+         case 0x2AAF: *ch = 0x2AB0; break;\r
+         case 0x2AB0: *ch = 0x2AAF; break;\r
+         case 0x2AB3: *ch = 0x2AB4; break;\r
+         case 0x2AB4: *ch = 0x2AB3; break;\r
+         case 0x2ABB: *ch = 0x2ABC; break;\r
+         case 0x2ABC: *ch = 0x2ABB; break;\r
+         case 0x2ABD: *ch = 0x2ABE; break;\r
+         case 0x2ABE: *ch = 0x2ABD; break;\r
+         case 0x2ABF: *ch = 0x2AC0; break;\r
+         case 0x2AC0: *ch = 0x2ABF; break;\r
+         case 0x2AC1: *ch = 0x2AC2; break;\r
+         case 0x2AC2: *ch = 0x2AC1; break;\r
+         case 0x2AC3: *ch = 0x2AC4; break;\r
+         case 0x2AC4: *ch = 0x2AC3; break;\r
+         case 0x2AC5: *ch = 0x2AC6; break;\r
+         case 0x2AC6: *ch = 0x2AC5; break;\r
+         case 0x2ACD: *ch = 0x2ACE; break;\r
+         case 0x2ACE: *ch = 0x2ACD; break;\r
+         case 0x2ACF: *ch = 0x2AD0; break;\r
+         case 0x2AD0: *ch = 0x2ACF; break;\r
+         case 0x2AD1: *ch = 0x2AD2; break;\r
+         case 0x2AD2: *ch = 0x2AD1; break;\r
+         case 0x2AD3: *ch = 0x2AD4; break;\r
+         case 0x2AD4: *ch = 0x2AD3; break;\r
+         case 0x2AD5: *ch = 0x2AD6; break;\r
+         case 0x2AD6: *ch = 0x2AD5; break;\r
+         case 0x2ADE: *ch = 0x22A6; break;\r
+         case 0x2AE3: *ch = 0x22A9; break;\r
+         case 0x2AE4: *ch = 0x22A8; break;\r
+         case 0x2AE5: *ch = 0x22AB; break;\r
+         case 0x2AEC: *ch = 0x2AED; break;\r
+         case 0x2AED: *ch = 0x2AEC; break;\r
+         case 0x2AF7: *ch = 0x2AF8; break;\r
+         case 0x2AF8: *ch = 0x2AF7; break;\r
+         case 0x2AF9: *ch = 0x2AFA; break;\r
+         case 0x2AFA: *ch = 0x2AF9; break;\r
+       }\r
+    } else if ((*ch & 0xFF00) == 0x3000) {\r
+       switch (*ch) {\r
+         case 0x3008: *ch = 0x3009; break;\r
+         case 0x3009: *ch = 0x3008; break;\r
+         case 0x300A: *ch = 0x300B; break;\r
+         case 0x300B: *ch = 0x300A; break;\r
+         case 0x300C: *ch = 0x300D; break;\r
+         case 0x300D: *ch = 0x300C; break;\r
+         case 0x300E: *ch = 0x300F; break;\r
+         case 0x300F: *ch = 0x300E; break;\r
+         case 0x3010: *ch = 0x3011; break;\r
+         case 0x3011: *ch = 0x3010; break;\r
+         case 0x3014: *ch = 0x3015; break;\r
+         case 0x3015: *ch = 0x3014; break;\r
+         case 0x3016: *ch = 0x3017; break;\r
+         case 0x3017: *ch = 0x3016; break;\r
+         case 0x3018: *ch = 0x3019; break;\r
+         case 0x3019: *ch = 0x3018; break;\r
+         case 0x301A: *ch = 0x301B; break;\r
+         case 0x301B: *ch = 0x301A; break;\r
+       }\r
+    } else if ((*ch & 0xFF00) == 0xFF00) {\r
+       switch (*ch) {\r
+         case 0xFF08: *ch = 0xFF09; break;\r
+         case 0xFF09: *ch = 0xFF08; break;\r
+         case 0xFF1C: *ch = 0xFF1E; break;\r
+         case 0xFF1E: *ch = 0xFF1C; break;\r
+         case 0xFF3B: *ch = 0xFF3D; break;\r
+         case 0xFF3D: *ch = 0xFF3B; break;\r
+         case 0xFF5B: *ch = 0xFF5D; break;\r
+         case 0xFF5D: *ch = 0xFF5B; break;\r
+         case 0xFF5F: *ch = 0xFF60; break;\r
+         case 0xFF60: *ch = 0xFF5F; break;\r
+         case 0xFF62: *ch = 0xFF63; break;\r
+         case 0xFF63: *ch = 0xFF62; break;\r
+       }\r
+    }\r
+}\r
+\r
+#ifdef TEST_GETTYPE\r
+\r
+#include <stdio.h>\r
+#include <assert.h>\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    static const struct { int type; char *name; } typetoname[] = {\r
+#define TYPETONAME(X) { X , #X }\r
+       TYPETONAME(L),\r
+       TYPETONAME(LRE),\r
+       TYPETONAME(LRO),\r
+       TYPETONAME(R),\r
+       TYPETONAME(AL),\r
+       TYPETONAME(RLE),\r
+       TYPETONAME(RLO),\r
+       TYPETONAME(PDF),\r
+       TYPETONAME(EN),\r
+       TYPETONAME(ES),\r
+       TYPETONAME(ET),\r
+       TYPETONAME(AN),\r
+       TYPETONAME(CS),\r
+       TYPETONAME(NSM),\r
+       TYPETONAME(BN),\r
+       TYPETONAME(B),\r
+       TYPETONAME(S),\r
+       TYPETONAME(WS),\r
+       TYPETONAME(ON),\r
+#undef TYPETONAME\r
+    };\r
+    int i;\r
+\r
+    for (i = 1; i < argc; i++) {\r
+       unsigned long chr = strtoul(argv[i], NULL, 0);\r
+       int type = getType(chr);\r
+       assert(typetoname[type].type == type);\r
+       printf("U+%04x: %s\n", chr, typetoname[type].name);\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/MISC.C b/putty/MISC.C
new file mode 100644 (file)
index 0000000..4aeab50
--- /dev/null
@@ -0,0 +1,655 @@
+/*\r
+ * Platform-independent routines shared between all PuTTY programs.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <limits.h>\r
+#include <ctype.h>\r
+#include <assert.h>\r
+#include "putty.h"\r
+\r
+/*\r
+ * Parse a string block size specification. This is approximately a\r
+ * subset of the block size specs supported by GNU fileutils:\r
+ *  "nk" = n kilobytes\r
+ *  "nM" = n megabytes\r
+ *  "nG" = n gigabytes\r
+ * All numbers are decimal, and suffixes refer to powers of two.\r
+ * Case-insensitive.\r
+ */\r
+unsigned long parse_blocksize(const char *bs)\r
+{\r
+    char *suf;\r
+    unsigned long r = strtoul(bs, &suf, 10);\r
+    if (*suf != '\0') {\r
+       while (*suf && isspace((unsigned char)*suf)) suf++;\r
+       switch (*suf) {\r
+         case 'k': case 'K':\r
+           r *= 1024ul;\r
+           break;\r
+         case 'm': case 'M':\r
+           r *= 1024ul * 1024ul;\r
+           break;\r
+         case 'g': case 'G':\r
+           r *= 1024ul * 1024ul * 1024ul;\r
+           break;\r
+         case '\0':\r
+         default:\r
+           break;\r
+       }\r
+    }\r
+    return r;\r
+}\r
+\r
+/*\r
+ * Parse a ^C style character specification.\r
+ * Returns NULL in `next' if we didn't recognise it as a control character,\r
+ * in which case `c' should be ignored.\r
+ * The precise current parsing is an oddity inherited from the terminal\r
+ * answerback-string parsing code. All sequences start with ^; all except\r
+ * ^<123> are two characters. The ones that are worth keeping are probably:\r
+ *   ^?                    127\r
+ *   ^@A-Z[\]^_            0-31\r
+ *   a-z           1-26\r
+ *   <num>         specified by number (decimal, 0octal, 0xHEX)\r
+ *   ~             ^ escape\r
+ */\r
+char ctrlparse(char *s, char **next)\r
+{\r
+    char c = 0;\r
+    if (*s != '^') {\r
+       *next = NULL;\r
+    } else {\r
+       s++;\r
+       if (*s == '\0') {\r
+           *next = NULL;\r
+       } else if (*s == '<') {\r
+           s++;\r
+           c = (char)strtol(s, next, 0);\r
+           if ((*next == s) || (**next != '>')) {\r
+               c = 0;\r
+               *next = NULL;\r
+           } else\r
+               (*next)++;\r
+       } else if (*s >= 'a' && *s <= 'z') {\r
+           c = (*s - ('a' - 1));\r
+           *next = s+1;\r
+       } else if ((*s >= '@' && *s <= '_') || *s == '?' || (*s & 0x80)) {\r
+           c = ('@' ^ *s);\r
+           *next = s+1;\r
+       } else if (*s == '~') {\r
+           c = '^';\r
+           *next = s+1;\r
+       }\r
+    }\r
+    return c;\r
+}\r
+\r
+prompts_t *new_prompts(void *frontend)\r
+{\r
+    prompts_t *p = snew(prompts_t);\r
+    p->prompts = NULL;\r
+    p->n_prompts = 0;\r
+    p->frontend = frontend;\r
+    p->data = NULL;\r
+    p->to_server = TRUE; /* to be on the safe side */\r
+    p->name = p->instruction = NULL;\r
+    p->name_reqd = p->instr_reqd = FALSE;\r
+    return p;\r
+}\r
+void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len)\r
+{\r
+    prompt_t *pr = snew(prompt_t);\r
+    char *result = snewn(len, char);\r
+    pr->prompt = promptstr;\r
+    pr->echo = echo;\r
+    pr->result = result;\r
+    pr->result_len = len;\r
+    p->n_prompts++;\r
+    p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *);\r
+    p->prompts[p->n_prompts-1] = pr;\r
+}\r
+void free_prompts(prompts_t *p)\r
+{\r
+    size_t i;\r
+    for (i=0; i < p->n_prompts; i++) {\r
+       prompt_t *pr = p->prompts[i];\r
+       memset(pr->result, 0, pr->result_len); /* burn the evidence */\r
+       sfree(pr->result);\r
+       sfree(pr->prompt);\r
+       sfree(pr);\r
+    }\r
+    sfree(p->prompts);\r
+    sfree(p->name);\r
+    sfree(p->instruction);\r
+    sfree(p);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * String handling routines.\r
+ */\r
+\r
+char *dupstr(const char *s)\r
+{\r
+    char *p = NULL;\r
+    if (s) {\r
+        int len = strlen(s);\r
+        p = snewn(len + 1, char);\r
+        strcpy(p, s);\r
+    }\r
+    return p;\r
+}\r
+\r
+/* Allocate the concatenation of N strings. Terminate arg list with NULL. */\r
+char *dupcat(const char *s1, ...)\r
+{\r
+    int len;\r
+    char *p, *q, *sn;\r
+    va_list ap;\r
+\r
+    len = strlen(s1);\r
+    va_start(ap, s1);\r
+    while (1) {\r
+       sn = va_arg(ap, char *);\r
+       if (!sn)\r
+           break;\r
+       len += strlen(sn);\r
+    }\r
+    va_end(ap);\r
+\r
+    p = snewn(len + 1, char);\r
+    strcpy(p, s1);\r
+    q = p + strlen(p);\r
+\r
+    va_start(ap, s1);\r
+    while (1) {\r
+       sn = va_arg(ap, char *);\r
+       if (!sn)\r
+           break;\r
+       strcpy(q, sn);\r
+       q += strlen(q);\r
+    }\r
+    va_end(ap);\r
+\r
+    return p;\r
+}\r
+\r
+/*\r
+ * Do an sprintf(), but into a custom-allocated buffer.\r
+ * \r
+ * Currently I'm doing this via vsnprintf. This has worked so far,\r
+ * but it's not good, because vsnprintf is not available on all\r
+ * platforms. There's an ifdef to use `_vsnprintf', which seems\r
+ * to be the local name for it on Windows. Other platforms may\r
+ * lack it completely, in which case it'll be time to rewrite\r
+ * this function in a totally different way.\r
+ * \r
+ * The only `properly' portable solution I can think of is to\r
+ * implement my own format string scanner, which figures out an\r
+ * upper bound for the length of each formatting directive,\r
+ * allocates the buffer as it goes along, and calls sprintf() to\r
+ * actually process each directive. If I ever need to actually do\r
+ * this, some caveats:\r
+ * \r
+ *  - It's very hard to find a reliable upper bound for\r
+ *    floating-point values. %f, in particular, when supplied with\r
+ *    a number near to the upper or lower limit of representable\r
+ *    numbers, could easily take several hundred characters. It's\r
+ *    probably feasible to predict this statically using the\r
+ *    constants in <float.h>, or even to predict it dynamically by\r
+ *    looking at the exponent of the specific float provided, but\r
+ *    it won't be fun.\r
+ * \r
+ *  - Don't forget to _check_, after calling sprintf, that it's\r
+ *    used at most the amount of space we had available.\r
+ * \r
+ *  - Fault any formatting directive we don't fully understand. The\r
+ *    aim here is to _guarantee_ that we never overflow the buffer,\r
+ *    because this is a security-critical function. If we see a\r
+ *    directive we don't know about, we should panic and die rather\r
+ *    than run any risk.\r
+ */\r
+char *dupprintf(const char *fmt, ...)\r
+{\r
+    char *ret;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    ret = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    return ret;\r
+}\r
+char *dupvprintf(const char *fmt, va_list ap)\r
+{\r
+    char *buf;\r
+    int len, size;\r
+\r
+    buf = snewn(512, char);\r
+    size = 512;\r
+\r
+    while (1) {\r
+#ifdef _WINDOWS\r
+#define vsnprintf _vsnprintf\r
+#endif\r
+#ifdef va_copy\r
+       /* Use the `va_copy' macro mandated by C99, if present.\r
+        * XXX some environments may have this as __va_copy() */\r
+       va_list aq;\r
+       va_copy(aq, ap);\r
+       len = vsnprintf(buf, size, fmt, aq);\r
+       va_end(aq);\r
+#else\r
+       /* Ugh. No va_copy macro, so do something nasty.\r
+        * Technically, you can't reuse a va_list like this: it is left\r
+        * unspecified whether advancing a va_list pointer modifies its\r
+        * value or something it points to, so on some platforms calling\r
+        * vsnprintf twice on the same va_list might fail hideously\r
+        * (indeed, it has been observed to).\r
+        * XXX the autoconf manual suggests that using memcpy() will give\r
+        *     "maximum portability". */\r
+       len = vsnprintf(buf, size, fmt, ap);\r
+#endif\r
+       if (len >= 0 && len < size) {\r
+           /* This is the C99-specified criterion for snprintf to have\r
+            * been completely successful. */\r
+           return buf;\r
+       } else if (len > 0) {\r
+           /* This is the C99 error condition: the returned length is\r
+            * the required buffer size not counting the NUL. */\r
+           size = len + 1;\r
+       } else {\r
+           /* This is the pre-C99 glibc error condition: <0 means the\r
+            * buffer wasn't big enough, so we enlarge it a bit and hope. */\r
+           size += 512;\r
+       }\r
+       buf = sresize(buf, size, char);\r
+    }\r
+}\r
+\r
+/*\r
+ * Read an entire line of text from a file. Return a buffer\r
+ * malloced to be as big as necessary (caller must free).\r
+ */\r
+char *fgetline(FILE *fp)\r
+{\r
+    char *ret = snewn(512, char);\r
+    int size = 512, len = 0;\r
+    while (fgets(ret + len, size - len, fp)) {\r
+       len += strlen(ret + len);\r
+       if (ret[len-1] == '\n')\r
+           break;                     /* got a newline, we're done */\r
+       size = len + 512;\r
+       ret = sresize(ret, size, char);\r
+    }\r
+    if (len == 0) {                   /* first fgets returned NULL */\r
+       sfree(ret);\r
+       return NULL;\r
+    }\r
+    ret[len] = '\0';\r
+    return ret;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Base64 encoding routine. This is required in public-key writing\r
+ * but also in HTTP proxy handling, so it's centralised here.\r
+ */\r
+\r
+void base64_encode_atom(unsigned char *data, int n, char *out)\r
+{\r
+    static const char base64_chars[] =\r
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+\r
+    unsigned word;\r
+\r
+    word = data[0] << 16;\r
+    if (n > 1)\r
+       word |= data[1] << 8;\r
+    if (n > 2)\r
+       word |= data[2];\r
+    out[0] = base64_chars[(word >> 18) & 0x3F];\r
+    out[1] = base64_chars[(word >> 12) & 0x3F];\r
+    if (n > 1)\r
+       out[2] = base64_chars[(word >> 6) & 0x3F];\r
+    else\r
+       out[2] = '=';\r
+    if (n > 2)\r
+       out[3] = base64_chars[word & 0x3F];\r
+    else\r
+       out[3] = '=';\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Generic routines to deal with send buffers: a linked list of\r
+ * smallish blocks, with the operations\r
+ * \r
+ *  - add an arbitrary amount of data to the end of the list\r
+ *  - remove the first N bytes from the list\r
+ *  - return a (pointer,length) pair giving some initial data in\r
+ *    the list, suitable for passing to a send or write system\r
+ *    call\r
+ *  - retrieve a larger amount of initial data from the list\r
+ *  - return the current size of the buffer chain in bytes\r
+ */\r
+\r
+#define BUFFER_GRANULE  512\r
+\r
+struct bufchain_granule {\r
+    struct bufchain_granule *next;\r
+    int buflen, bufpos;\r
+    char buf[BUFFER_GRANULE];\r
+};\r
+\r
+void bufchain_init(bufchain *ch)\r
+{\r
+    ch->head = ch->tail = NULL;\r
+    ch->buffersize = 0;\r
+}\r
+\r
+void bufchain_clear(bufchain *ch)\r
+{\r
+    struct bufchain_granule *b;\r
+    while (ch->head) {\r
+       b = ch->head;\r
+       ch->head = ch->head->next;\r
+       sfree(b);\r
+    }\r
+    ch->tail = NULL;\r
+    ch->buffersize = 0;\r
+}\r
+\r
+int bufchain_size(bufchain *ch)\r
+{\r
+    return ch->buffersize;\r
+}\r
+\r
+void bufchain_add(bufchain *ch, const void *data, int len)\r
+{\r
+    const char *buf = (const char *)data;\r
+\r
+    if (len == 0) return;\r
+\r
+    ch->buffersize += len;\r
+\r
+    if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {\r
+       int copylen = min(len, BUFFER_GRANULE - ch->tail->buflen);\r
+       memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);\r
+       buf += copylen;\r
+       len -= copylen;\r
+       ch->tail->buflen += copylen;\r
+    }\r
+    while (len > 0) {\r
+       int grainlen = min(len, BUFFER_GRANULE);\r
+       struct bufchain_granule *newbuf;\r
+       newbuf = snew(struct bufchain_granule);\r
+       newbuf->bufpos = 0;\r
+       newbuf->buflen = grainlen;\r
+       memcpy(newbuf->buf, buf, grainlen);\r
+       buf += grainlen;\r
+       len -= grainlen;\r
+       if (ch->tail)\r
+           ch->tail->next = newbuf;\r
+       else\r
+           ch->head = ch->tail = newbuf;\r
+       newbuf->next = NULL;\r
+       ch->tail = newbuf;\r
+    }\r
+}\r
+\r
+void bufchain_consume(bufchain *ch, int len)\r
+{\r
+    struct bufchain_granule *tmp;\r
+\r
+    assert(ch->buffersize >= len);\r
+    while (len > 0) {\r
+       int remlen = len;\r
+       assert(ch->head != NULL);\r
+       if (remlen >= ch->head->buflen - ch->head->bufpos) {\r
+           remlen = ch->head->buflen - ch->head->bufpos;\r
+           tmp = ch->head;\r
+           ch->head = tmp->next;\r
+           sfree(tmp);\r
+           if (!ch->head)\r
+               ch->tail = NULL;\r
+       } else\r
+           ch->head->bufpos += remlen;\r
+       ch->buffersize -= remlen;\r
+       len -= remlen;\r
+    }\r
+}\r
+\r
+void bufchain_prefix(bufchain *ch, void **data, int *len)\r
+{\r
+    *len = ch->head->buflen - ch->head->bufpos;\r
+    *data = ch->head->buf + ch->head->bufpos;\r
+}\r
+\r
+void bufchain_fetch(bufchain *ch, void *data, int len)\r
+{\r
+    struct bufchain_granule *tmp;\r
+    char *data_c = (char *)data;\r
+\r
+    tmp = ch->head;\r
+\r
+    assert(ch->buffersize >= len);\r
+    while (len > 0) {\r
+       int remlen = len;\r
+\r
+       assert(tmp != NULL);\r
+       if (remlen >= tmp->buflen - tmp->bufpos)\r
+           remlen = tmp->buflen - tmp->bufpos;\r
+       memcpy(data_c, tmp->buf + tmp->bufpos, remlen);\r
+\r
+       tmp = tmp->next;\r
+       len -= remlen;\r
+       data_c += remlen;\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * My own versions of malloc, realloc and free. Because I want\r
+ * malloc and realloc to bomb out and exit the program if they run\r
+ * out of memory, realloc to reliably call malloc if passed a NULL\r
+ * pointer, and free to reliably do nothing if passed a NULL\r
+ * pointer. We can also put trace printouts in, if we need to; and\r
+ * we can also replace the allocator with an ElectricFence-like\r
+ * one.\r
+ */\r
+\r
+#ifdef MINEFIELD\r
+void *minefield_c_malloc(size_t size);\r
+void minefield_c_free(void *p);\r
+void *minefield_c_realloc(void *p, size_t size);\r
+#endif\r
+\r
+#ifdef MALLOC_LOG\r
+static FILE *fp = NULL;\r
+\r
+static char *mlog_file = NULL;\r
+static int mlog_line = 0;\r
+\r
+void mlog(char *file, int line)\r
+{\r
+    mlog_file = file;\r
+    mlog_line = line;\r
+    if (!fp) {\r
+       fp = fopen("putty_mem.log", "w");\r
+       setvbuf(fp, NULL, _IONBF, BUFSIZ);\r
+    }\r
+    if (fp)\r
+       fprintf(fp, "%s:%d: ", file, line);\r
+}\r
+#endif\r
+\r
+void *safemalloc(size_t n, size_t size)\r
+{\r
+    void *p;\r
+\r
+    if (n > INT_MAX / size) {\r
+       p = NULL;\r
+    } else {\r
+       size *= n;\r
+       if (size == 0) size = 1;\r
+#ifdef MINEFIELD\r
+       p = minefield_c_malloc(size);\r
+#else\r
+       p = malloc(size);\r
+#endif\r
+    }\r
+\r
+    if (!p) {\r
+       char str[200];\r
+#ifdef MALLOC_LOG\r
+       sprintf(str, "Out of memory! (%s:%d, size=%d)",\r
+               mlog_file, mlog_line, size);\r
+       fprintf(fp, "*** %s\n", str);\r
+       fclose(fp);\r
+#else\r
+       strcpy(str, "Out of memory!");\r
+#endif\r
+       modalfatalbox(str);\r
+    }\r
+#ifdef MALLOC_LOG\r
+    if (fp)\r
+       fprintf(fp, "malloc(%d) returns %p\n", size, p);\r
+#endif\r
+    return p;\r
+}\r
+\r
+void *saferealloc(void *ptr, size_t n, size_t size)\r
+{\r
+    void *p;\r
+\r
+    if (n > INT_MAX / size) {\r
+       p = NULL;\r
+    } else {\r
+       size *= n;\r
+       if (!ptr) {\r
+#ifdef MINEFIELD\r
+           p = minefield_c_malloc(size);\r
+#else\r
+           p = malloc(size);\r
+#endif\r
+       } else {\r
+#ifdef MINEFIELD\r
+           p = minefield_c_realloc(ptr, size);\r
+#else\r
+           p = realloc(ptr, size);\r
+#endif\r
+       }\r
+    }\r
+\r
+    if (!p) {\r
+       char str[200];\r
+#ifdef MALLOC_LOG\r
+       sprintf(str, "Out of memory! (%s:%d, size=%d)",\r
+               mlog_file, mlog_line, size);\r
+       fprintf(fp, "*** %s\n", str);\r
+       fclose(fp);\r
+#else\r
+       strcpy(str, "Out of memory!");\r
+#endif\r
+       modalfatalbox(str);\r
+    }\r
+#ifdef MALLOC_LOG\r
+    if (fp)\r
+       fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p);\r
+#endif\r
+    return p;\r
+}\r
+\r
+void safefree(void *ptr)\r
+{\r
+    if (ptr) {\r
+#ifdef MALLOC_LOG\r
+       if (fp)\r
+           fprintf(fp, "free(%p)\n", ptr);\r
+#endif\r
+#ifdef MINEFIELD\r
+       minefield_c_free(ptr);\r
+#else\r
+       free(ptr);\r
+#endif\r
+    }\r
+#ifdef MALLOC_LOG\r
+    else if (fp)\r
+       fprintf(fp, "freeing null pointer - no action taken\n");\r
+#endif\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Debugging routines.\r
+ */\r
+\r
+#ifdef DEBUG\r
+extern void dputs(char *);             /* defined in per-platform *misc.c */\r
+\r
+void debug_printf(char *fmt, ...)\r
+{\r
+    char *buf;\r
+    va_list ap;\r
+\r
+    va_start(ap, fmt);\r
+    buf = dupvprintf(fmt, ap);\r
+    dputs(buf);\r
+    sfree(buf);\r
+    va_end(ap);\r
+}\r
+\r
+\r
+void debug_memdump(void *buf, int len, int L)\r
+{\r
+    int i;\r
+    unsigned char *p = buf;\r
+    char foo[17];\r
+    if (L) {\r
+       int delta;\r
+       debug_printf("\t%d (0x%x) bytes:\n", len, len);\r
+       delta = 15 & (unsigned long int) p;\r
+       p -= delta;\r
+       len += delta;\r
+    }\r
+    for (; 0 < len; p += 16, len -= 16) {\r
+       dputs("  ");\r
+       if (L)\r
+           debug_printf("%p: ", p);\r
+       strcpy(foo, "................");        /* sixteen dots */\r
+       for (i = 0; i < 16 && i < len; ++i) {\r
+           if (&p[i] < (unsigned char *) buf) {\r
+               dputs("   ");          /* 3 spaces */\r
+               foo[i] = ' ';\r
+           } else {\r
+               debug_printf("%c%02.2x",\r
+                       &p[i] != (unsigned char *) buf\r
+                       && i % 4 ? '.' : ' ', p[i]\r
+                   );\r
+               if (p[i] >= ' ' && p[i] <= '~')\r
+                   foo[i] = (char) p[i];\r
+           }\r
+       }\r
+       foo[i] = '\0';\r
+       debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);\r
+    }\r
+}\r
+\r
+#endif                         /* def DEBUG */\r
+\r
+/*\r
+ * Determine whether or not a Config structure represents a session\r
+ * which can sensibly be launched right now.\r
+ */\r
+int cfg_launchable(const Config *cfg)\r
+{\r
+    if (cfg->protocol == PROT_SERIAL)\r
+       return cfg->serline[0] != 0;\r
+    else\r
+       return cfg->host[0] != 0;\r
+}\r
+\r
+char const *cfg_dest(const Config *cfg)\r
+{\r
+    if (cfg->protocol == PROT_SERIAL)\r
+       return cfg->serline;\r
+    else\r
+       return cfg->host;\r
+}\r
diff --git a/putty/MISC.H b/putty/MISC.H
new file mode 100644 (file)
index 0000000..1123314
--- /dev/null
@@ -0,0 +1,132 @@
+/*\r
+ * Header for misc.c.\r
+ */\r
+\r
+#ifndef PUTTY_MISC_H\r
+#define PUTTY_MISC_H\r
+\r
+#include "puttymem.h"\r
+\r
+#include <stdio.h>                    /* for FILE * */\r
+#include <stdarg.h>                   /* for va_list */\r
+#include <time.h>                      /* for struct tm */\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+typedef struct Filename Filename;\r
+typedef struct FontSpec FontSpec;\r
+\r
+unsigned long parse_blocksize(const char *bs);\r
+char ctrlparse(char *s, char **next);\r
+\r
+char *dupstr(const char *s);\r
+char *dupcat(const char *s1, ...);\r
+char *dupprintf(const char *fmt, ...);\r
+char *dupvprintf(const char *fmt, va_list ap);\r
+\r
+char *fgetline(FILE *fp);\r
+\r
+void base64_encode_atom(unsigned char *data, int n, char *out);\r
+\r
+struct bufchain_granule;\r
+typedef struct bufchain_tag {\r
+    struct bufchain_granule *head, *tail;\r
+    int buffersize;                   /* current amount of buffered data */\r
+} bufchain;\r
+\r
+void bufchain_init(bufchain *ch);\r
+void bufchain_clear(bufchain *ch);\r
+int bufchain_size(bufchain *ch);\r
+void bufchain_add(bufchain *ch, const void *data, int len);\r
+void bufchain_prefix(bufchain *ch, void **data, int *len);\r
+void bufchain_consume(bufchain *ch, int len);\r
+void bufchain_fetch(bufchain *ch, void *data, int len);\r
+\r
+struct tm ltime(void);\r
+\r
+/*\r
+ * Debugging functions.\r
+ *\r
+ * Output goes to debug.log\r
+ *\r
+ * debug(()) (note the double brackets) is like printf().\r
+ *\r
+ * dmemdump() and dmemdumpl() both do memory dumps.  The difference\r
+ * is that dmemdumpl() is more suited for when the memory address is\r
+ * important (say because you'll be recording pointer values later\r
+ * on).  dmemdump() is more concise.\r
+ */\r
+\r
+#ifdef DEBUG\r
+void debug_printf(char *fmt, ...);\r
+void debug_memdump(void *buf, int len, int L);\r
+#define debug(x) (debug_printf x)\r
+#define dmemdump(buf,len) debug_memdump (buf, len, 0);\r
+#define dmemdumpl(buf,len) debug_memdump (buf, len, 1);\r
+#else\r
+#define debug(x)\r
+#define dmemdump(buf,len)\r
+#define dmemdumpl(buf,len)\r
+#endif\r
+\r
+#ifndef lenof\r
+#define lenof(x) ( (sizeof((x))) / (sizeof(*(x))))\r
+#endif\r
+\r
+#ifndef min\r
+#define min(x,y) ( (x) < (y) ? (x) : (y) )\r
+#endif\r
+#ifndef max\r
+#define max(x,y) ( (x) > (y) ? (x) : (y) )\r
+#endif\r
+\r
+#define GET_32BIT_LSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0]) | \\r
+  ((unsigned long)(unsigned char)(cp)[1] << 8) | \\r
+  ((unsigned long)(unsigned char)(cp)[2] << 16) | \\r
+  ((unsigned long)(unsigned char)(cp)[3] << 24))\r
+\r
+#define PUT_32BIT_LSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)(value), \\r
+  (cp)[1] = (unsigned char)((value) >> 8), \\r
+  (cp)[2] = (unsigned char)((value) >> 16), \\r
+  (cp)[3] = (unsigned char)((value) >> 24) )\r
+\r
+#define GET_16BIT_LSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0]) | \\r
+  ((unsigned long)(unsigned char)(cp)[1] << 8))\r
+\r
+#define PUT_16BIT_LSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)(value), \\r
+  (cp)[1] = (unsigned char)((value) >> 8) )\r
+\r
+#define GET_32BIT_MSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0] << 24) | \\r
+  ((unsigned long)(unsigned char)(cp)[1] << 16) | \\r
+  ((unsigned long)(unsigned char)(cp)[2] << 8) | \\r
+  ((unsigned long)(unsigned char)(cp)[3]))\r
+\r
+#define GET_32BIT(cp) GET_32BIT_MSB_FIRST(cp)\r
+\r
+#define PUT_32BIT_MSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)((value) >> 24), \\r
+  (cp)[1] = (unsigned char)((value) >> 16), \\r
+  (cp)[2] = (unsigned char)((value) >> 8), \\r
+  (cp)[3] = (unsigned char)(value) )\r
+\r
+#define PUT_32BIT(cp, value) PUT_32BIT_MSB_FIRST(cp, value)\r
+\r
+#define GET_16BIT_MSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0] << 8) | \\r
+  ((unsigned long)(unsigned char)(cp)[1]))\r
+\r
+#define PUT_16BIT_MSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)((value) >> 8), \\r
+  (cp)[1] = (unsigned char)(value) )\r
+\r
+#endif\r
diff --git a/putty/MKAUTO.SH b/putty/MKAUTO.SH
new file mode 100644 (file)
index 0000000..6548951
--- /dev/null
@@ -0,0 +1,47 @@
+#! /bin/sh\r
+# This script makes the autoconf mechanism for the Unix port work.\r
+# It's separate from mkfiles.pl because it won't work (and isn't needed)\r
+# on a non-Unix system.\r
+\r
+# It's nice to be able to run this from inside the unix subdir as\r
+# well as from outside.\r
+test -f unix.h && cd ..\r
+\r
+# Persuade automake to give us a copy of its install-sh. This is a\r
+# pain because I don't actually want to have to _use_ automake.\r
+# Instead, I construct a trivial unrelated automake project in a\r
+# temporary subdirectory, run automake so that it'll copy\r
+# install-sh into that directory, then copy it back out again.\r
+# Hideous, but it should work.\r
+\r
+mkdir automake-grievous-hack\r
+cat > automake-grievous-hack/hello.c << EOF\r
+#include <stdio.h>\r
+int main(int argc, char **argv)\r
+{\r
+    printf("hello, world\n");\r
+    return 0;\r
+}\r
+EOF\r
+cat > automake-grievous-hack/Makefile.am << EOF\r
+bin_PROGRAMS = hello\r
+hello_SOURCES = hello.c\r
+EOF\r
+cat > automake-grievous-hack/configure.ac << EOF\r
+AC_INIT\r
+AM_INIT_AUTOMAKE(hello, 1.0)\r
+AC_CONFIG_FILES([Makefile])\r
+AC_PROG_CC\r
+AC_OUTPUT\r
+EOF\r
+echo Some news > automake-grievous-hack/NEWS\r
+echo Some text > automake-grievous-hack/README\r
+echo Some people > automake-grievous-hack/AUTHORS\r
+echo Some changes > automake-grievous-hack/ChangeLog\r
+rm -f install-sh # this won't work if we accidentally have one _here_\r
+(cd automake-grievous-hack && autoreconf -i && \\r
+  cp install-sh ../unix/install-sh)\r
+rm -rf automake-grievous-hack\r
+\r
+# That was the hard bit. Now run autoconf on our real configure.in.\r
+(cd unix && autoreconf && rm -rf aclocal.m4 autom4te.cache)\r
diff --git a/putty/MKFILES.PL b/putty/MKFILES.PL
new file mode 100644 (file)
index 0000000..8075ddb
--- /dev/null
@@ -0,0 +1,1493 @@
+#!/usr/bin/env perl\r
+#\r
+# Cross-platform Makefile generator.\r
+#\r
+# Reads the file `Recipe' to determine the list of generated\r
+# executables and their component objects. Then reads the source\r
+# files to compute #include dependencies. Finally, writes out the\r
+# various target Makefiles.\r
+\r
+# PuTTY specifics which could still do with removing:\r
+#  - Mac makefile is not portabilised at all. Include directories\r
+#    are hardwired, and also the libraries are fixed. This is\r
+#    mainly because I was too scared to go anywhere near it.\r
+#  - sbcsgen.pl is still run at startup.\r
+#\r
+# FIXME: no attempt made to handle !forceobj in the project files.\r
+\r
+use warnings;\r
+use FileHandle;\r
+use Cwd;\r
+\r
+open IN, "Recipe" or do {\r
+    # We want to deal correctly with being run from one of the\r
+    # subdirs in the source tree. So if we can't find Recipe here,\r
+    # try one level up.\r
+    chdir "..";\r
+    open IN, "Recipe" or die "unable to open Recipe file\n";\r
+};\r
+\r
+# HACK: One of the source files in `charset' is auto-generated by\r
+# sbcsgen.pl. We need to generate that _now_, before attempting\r
+# dependency analysis.\r
+eval 'chdir "charset"; require "sbcsgen.pl"; chdir ".."';\r
+\r
+@srcdirs = ("./");\r
+\r
+$divert = undef; # ref to scalar in which text is currently being put\r
+$help = ""; # list of newline-free lines of help text\r
+$project_name = "project"; # this is a good enough default\r
+%makefiles = (); # maps makefile types to output makefile pathnames\r
+%makefile_extra = (); # maps makefile types to extra Makefile text\r
+%programs = (); # maps prog name + type letter to listref of objects/resources\r
+%groups = (); # maps group name to listref of objects/resources\r
+\r
+while (<IN>) {\r
+  chomp;\r
+  @_ = split;\r
+\r
+  # If we're gathering help text, keep doing so.\r
+  if (defined $divert) {\r
+      if ((defined $_[0]) && $_[0] eq "!end") {\r
+         $divert = undef;\r
+      } else {\r
+         ${$divert} .= "$_\n";\r
+      }\r
+      next;\r
+  }\r
+  # Skip comments and blank lines.\r
+  next if /^\s*#/ or scalar @_ == 0;\r
+\r
+  if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = \$help; next; }\r
+  if ($_[0] eq "!end") { $divert = undef; next; }\r
+  if ($_[0] eq "!name") { $project_name = $_[1]; next; }\r
+  if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; }\r
+  if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;}\r
+  if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;}\r
+  if ($_[0] eq "!forceobj") { $forceobj{$_[1]} = 1; next; }\r
+  if ($_[0] eq "!begin") {\r
+      if (&mfval($_[1])) {\r
+          $sect = $_[2] ? $_[2] : "end";\r
+         $divert = \($makefile_extra{$_[1]}->{$sect});\r
+      } else {\r
+         $dummy = '';\r
+         $divert = \$dummy;\r
+      }\r
+      next;\r
+  }\r
+  # If we're gathering help/verbatim text, keep doing so.\r
+  if (defined $divert) { ${$divert} .= "$_\n"; next; }\r
+  # Ignore blank lines.\r
+  next if scalar @_ == 0;\r
+\r
+  # Now we have an ordinary line. See if it's an = line, a : line\r
+  # or a + line.\r
+  @objs = @_;\r
+\r
+  if ($_[0] eq "+") {\r
+    $listref = $lastlistref;\r
+    $prog = undef;\r
+    die "$.: unexpected + line\n" if !defined $lastlistref;\r
+  } elsif ($_[1] eq "=") {\r
+    $groups{$_[0]} = [] if !defined $groups{$_[0]};\r
+    $listref = $groups{$_[0]};\r
+    $prog = undef;\r
+    shift @objs; # eat the group name\r
+  } elsif ($_[1] eq ":") {\r
+    $listref = [];\r
+    $prog = $_[0];\r
+    shift @objs; # eat the program name\r
+  } else {\r
+    die "$.: unrecognised line type\n";\r
+  }\r
+  shift @objs; # eat the +, the = or the :\r
+\r
+  while (scalar @objs > 0) {\r
+    $i = shift @objs;\r
+    if ($groups{$i}) {\r
+      foreach $j (@{$groups{$i}}) { unshift @objs, $j; }\r
+    } elsif (($i eq "[G]" or $i eq "[C]" or $i eq "[M]" or\r
+              $i eq "[X]" or $i eq "[U]" or $i eq "[MX]") and defined $prog) {\r
+      $type = substr($i,1,(length $i)-2);\r
+    } else {\r
+      push @$listref, $i;\r
+    }\r
+  }\r
+  if ($prog and $type) {\r
+    die "multiple program entries for $prog [$type]\n"\r
+        if defined $programs{$prog . "," . $type};\r
+    $programs{$prog . "," . $type} = $listref;\r
+  }\r
+  $lastlistref = $listref;\r
+}\r
+\r
+close IN;\r
+\r
+# Now retrieve the complete list of objects and resource files, and\r
+# construct dependency data for them. While we're here, expand the\r
+# object list for each program, and complain if its type isn't set.\r
+@prognames = sort keys %programs;\r
+%depends = ();\r
+@scanlist = ();\r
+foreach $i (@prognames) {\r
+  ($prog, $type) = split ",", $i;\r
+  # Strip duplicate object names.\r
+  $prev = '';\r
+  @list = grep { $status = ($prev ne $_); $prev=$_; $status }\r
+          sort @{$programs{$i}};\r
+  $programs{$i} = [@list];\r
+  foreach $j (@list) {\r
+    # Dependencies for "x" start with "x.c" or "x.m" (depending on\r
+    # which one exists).\r
+    # Dependencies for "x.res" start with "x.rc".\r
+    # Dependencies for "x.rsrc" start with "x.r".\r
+    # Both types of file are pushed on the list of files to scan.\r
+    # Libraries (.lib) don't have dependencies at all.\r
+    if ($j =~ /^(.*)\.res$/) {\r
+      $file = "$1.rc";\r
+      $depends{$j} = [$file];\r
+      push @scanlist, $file;\r
+    } elsif ($j =~ /^(.*)\.rsrc$/) {\r
+      $file = "$1.r";\r
+      $depends{$j} = [$file];\r
+      push @scanlist, $file;\r
+    } elsif ($j !~ /\./) {\r
+      $file = "$j.c";\r
+      $file = "$j.m" unless &findfile($file);\r
+      $depends{$j} = [$file];\r
+      push @scanlist, $file;\r
+    }\r
+  }\r
+}\r
+\r
+# Scan each file on @scanlist and find further inclusions.\r
+# Inclusions are given by lines of the form `#include "otherfile"'\r
+# (system headers are automatically ignored by this because they'll\r
+# be given in angle brackets). Files included by this method are\r
+# added back on to @scanlist to be scanned in turn (if not already\r
+# done).\r
+#\r
+# Resource scripts (.rc) can also include a file by means of:\r
+#  - a line # ending `ICON "filename"';\r
+#  - a line ending `RT_MANIFEST "filename"'.\r
+# Files included by this method are not added to @scanlist because\r
+# they can never include further files.\r
+#\r
+# In this pass we write out a hash %further which maps a source\r
+# file name into a listref containing further source file names.\r
+\r
+%further = ();\r
+while (scalar @scanlist > 0) {\r
+  $file = shift @scanlist;\r
+  next if defined $further{$file}; # skip if we've already done it\r
+  $further{$file} = [];\r
+  $dirfile = &findfile($file);\r
+  open IN, "$dirfile" or die "unable to open source file $file\n";\r
+  while (<IN>) {\r
+    chomp;\r
+    /^\s*#include\s+\"([^\"]+)\"/ and do {\r
+      push @{$further{$file}}, $1;\r
+      push @scanlist, $1;\r
+      next;\r
+    };\r
+    /(RT_MANIFEST|ICON)\s+\"([^\"]+)\"\s*$/ and do {\r
+      push @{$further{$file}}, $2;\r
+      next;\r
+    }\r
+  }\r
+  close IN;\r
+}\r
+\r
+# Now we're ready to generate the final dependencies section. For\r
+# each key in %depends, we must expand the dependencies list by\r
+# iteratively adding entries from %further.\r
+foreach $i (keys %depends) {\r
+  %dep = ();\r
+  @scanlist = @{$depends{$i}};\r
+  foreach $i (@scanlist) { $dep{$i} = 1; }\r
+  while (scalar @scanlist > 0) {\r
+    $file = shift @scanlist;\r
+    foreach $j (@{$further{$file}}) {\r
+      if (!$dep{$j}) {\r
+        $dep{$j} = 1;\r
+        push @{$depends{$i}}, $j;\r
+        push @scanlist, $j;\r
+      }\r
+    }\r
+  }\r
+#  printf "%s: %s\n", $i, join ' ',@{$depends{$i}};\r
+}\r
+\r
+# Validation of input.\r
+\r
+sub mfval($) {\r
+    my ($type) = @_;\r
+    # Returns true if the argument is a known makefile type. Otherwise,\r
+    # prints a warning and returns false;\r
+    if (grep { $type eq $_ }\r
+       ("vc","vcproj","cygwin","borland","lcc","devcppproj","gtk","unix",\r
+        "ac","osx",)) {\r
+           return 1;\r
+       }\r
+    warn "$.:unknown makefile type '$type'\n";\r
+    return 0;\r
+}\r
+\r
+# Utility routines while writing out the Makefiles.\r
+\r
+sub def {\r
+    my ($x) = shift @_;\r
+    return (defined $x) ? $x : "";\r
+}\r
+\r
+sub dirpfx {\r
+    my ($path) = shift @_;\r
+    my ($sep) = shift @_;\r
+    my $ret = "";\r
+    my $i;\r
+\r
+    while (($i = index $path, $sep) >= 0 ||\r
+          ($j = index $path, "/") >= 0) {\r
+        if ($i >= 0 and ($j < 0 or $i < $j)) {\r
+           $path = substr $path, ($i + length $sep);\r
+       } else {\r
+           $path = substr $path, ($j + 1);\r
+       }\r
+       $ret .= "..$sep";\r
+    }\r
+    return $ret;\r
+}\r
+\r
+sub findfile {\r
+  my ($name) = @_;\r
+  my $dir = '';\r
+  my $i;\r
+  my $outdir = undef;\r
+  unless (defined $findfilecache{$name}) {\r
+    $i = 0;\r
+    foreach $dir (@srcdirs) {\r
+      if (-f "$dir$name") {\r
+        $outdir = $dir;\r
+        $i++;\r
+        $outdir =~ s/^\.\///;\r
+      }\r
+    }\r
+    die "multiple instances of source file $name\n" if $i > 1;\r
+    $findfilecache{$name} = (defined $outdir ? $outdir . $name : undef);\r
+  }\r
+  return $findfilecache{$name};\r
+}\r
+\r
+sub objects {\r
+  my ($prog, $otmpl, $rtmpl, $ltmpl, $prefix, $dirsep) = @_;\r
+  my @ret;\r
+  my ($i, $x, $y);\r
+  ($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl);\r
+  @ret = ();\r
+  foreach $i (@{$programs{$prog}}) {\r
+    $x = "";\r
+    if ($i =~ /^(.*)\.(res|rsrc)/) {\r
+      $y = $1;\r
+      ($x = $rtmpl) =~ s/X/$y/;\r
+    } elsif ($i =~ /^(.*)\.lib/) {\r
+      $y = $1;\r
+      ($x = $ltmpl) =~ s/X/$y/;\r
+    } elsif ($i !~ /\./) {\r
+      ($x = $otmpl) =~ s/X/$i/;\r
+    }\r
+    push @ret, $x if $x ne "";\r
+  }\r
+  return join " ", @ret;\r
+}\r
+\r
+sub special {\r
+  my ($prog, $suffix) = @_;\r
+  my @ret;\r
+  my ($i, $x, $y);\r
+  ($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl);\r
+  @ret = ();\r
+  foreach $i (@{$programs{$prog}}) {\r
+    if (substr($i, (length $i) - (length $suffix)) eq $suffix) {\r
+      push @ret, $i;\r
+    }\r
+  }\r
+  return (scalar @ret) ? (join " ", @ret) : undef;\r
+}\r
+\r
+sub splitline {\r
+  my ($line, $width, $splitchar) = @_;\r
+  my $result = "";\r
+  my $len;\r
+  $len = (defined $width ? $width : 76);\r
+  $splitchar = (defined $splitchar ? $splitchar : '\\');\r
+  while (length $line > $len) {\r
+    $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,}?\s(.*)$/;\r
+    $result .= $1;\r
+    $result .= " ${splitchar}\n\t\t" if $2 ne '';\r
+    $line = $2;\r
+    $len = 60;\r
+  }\r
+  return $result . $line;\r
+}\r
+\r
+sub deps {\r
+  my ($otmpl, $rtmpl, $prefix, $dirsep, $mftyp, $depchar, $splitchar) = @_;\r
+  my ($i, $x, $y);\r
+  my @deps;\r
+  my @ret;\r
+  @ret = ();\r
+  $depchar ||= ':';\r
+  foreach $i (sort keys %depends) {\r
+    next if $specialobj{$mftyp}->{$i};\r
+    if ($i =~ /^(.*)\.(res|rsrc)/) {\r
+      next if !defined $rtmpl;\r
+      $y = $1;\r
+      ($x = $rtmpl) =~ s/X/$y/;\r
+    } else {\r
+      ($x = $otmpl) =~ s/X/$i/;\r
+    }\r
+    @deps = @{$depends{$i}};\r
+    @deps = map {\r
+      $_ = &findfile($_);\r
+      s/\//$dirsep/g;\r
+      $_ = $prefix . $_;\r
+    } @deps;\r
+    push @ret, {obj => $x, obj_orig => $i, deps => [@deps]};\r
+  }\r
+  return @ret;\r
+}\r
+\r
+sub prognames {\r
+  my ($types) = @_;\r
+  my ($n, $prog, $type);\r
+  my @ret;\r
+  @ret = ();\r
+  foreach $n (@prognames) {\r
+    ($prog, $type) = split ",", $n;\r
+    push @ret, $n if index(":$types:", ":$type:") >= 0;\r
+  }\r
+  return @ret;\r
+}\r
+\r
+sub progrealnames {\r
+  my ($types) = @_;\r
+  my ($n, $prog, $type);\r
+  my @ret;\r
+  @ret = ();\r
+  foreach $n (@prognames) {\r
+    ($prog, $type) = split ",", $n;\r
+    push @ret, $prog if index(":$types:", ":$type:") >= 0;\r
+  }\r
+  return @ret;\r
+}\r
+\r
+sub manpages {\r
+  my ($types,$suffix) = @_;\r
+\r
+  # assume that all UNIX programs have a man page\r
+  if($suffix eq "1" && $types =~ /:X:/) {\r
+    return map("$_.1", &progrealnames($types));\r
+  }\r
+  return ();\r
+}\r
+\r
+# Now we're ready to output the actual Makefiles.\r
+\r
+if (defined $makefiles{'cygwin'}) {\r
+    $dirpfx = &dirpfx($makefiles{'cygwin'}, "/");\r
+\r
+    ##-- CygWin makefile\r
+    open OUT, ">$makefiles{'cygwin'}"; select OUT;\r
+    print\r
+    "# Makefile for $project_name under cygwin.\n".\r
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    # gcc command line option is -D not /D\r
+    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;\r
+    print $_;\r
+    print\r
+    "\n".\r
+    "# You can define this path to point at your tools if you need to\n".\r
+    "# TOOLPATH = c:\\cygwin\\bin\\ # or similar, if you're running Windows\n".\r
+    "# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/\n".\r
+    "CC = \$(TOOLPATH)gcc\n".\r
+    "RC = \$(TOOLPATH)windres\n".\r
+    "# Uncomment the following two lines to compile under Winelib\n".\r
+    "# CC = winegcc\n".\r
+    "# RC = wrc\n".\r
+    "# You may also need to tell windres where to find include files:\n".\r
+    "# RCINC = --include-dir c:\\cygwin\\include\\\n".\r
+    "\n".\r
+    &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT".\r
+      " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP " .\r
+              (join " ", map {"-I$dirpfx$_"} @srcdirs)) .\r
+              "\n".\r
+    "LDFLAGS = -mno-cygwin -s\n".\r
+    &splitline("RCFLAGS = \$(RCINC) --define WIN32=1 --define _WIN32=1".\r
+      " --define WINVER=0x0400")."\n".\r
+    "\n".\r
+    $makefile_extra{'cygwin'}->{'vars'} .\r
+    "\n".\r
+    ".SUFFIXES:\n".\r
+    "\n";\r
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));\r
+    print "\n\n";\r
+    foreach $p (&prognames("G:C")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr = &objects($p, "X.o", "X.res.o", undef);\r
+      print &splitline($prog . ".exe: " . $objstr), "\n";\r
+      my $mw = $type eq "G" ? " -mwindows" : "";\r
+      $libstr = &objects($p, undef, undef, "-lX");\r
+      print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " .\r
+                       "-Wl,-Map,$prog.map " .\r
+                       $objstr . " $libstr", 69), "\n\n";\r
+    }\r
+    foreach $d (&deps("X.o", "X.res.o", $dirpfx, "/", "cygwin")) {\r
+      if ($forceobj{$d->{obj_orig}}) {\r
+        printf ("%s: FORCE\n", $d->{obj});\r
+      } else {\r
+        print &splitline(sprintf("%s: %s", $d->{obj},\r
+                         join " ", @{$d->{deps}})), "\n";\r
+      }\r
+      if ($d->{obj} =~ /\.res\.o$/) {\r
+         print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." ".$d->{obj}."\n\n";\r
+      } else {\r
+         print "\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c ".$d->{deps}->[0]."\n\n";\r
+      }\r
+    }\r
+    print "\n";\r
+    print $makefile_extra{'cygwin'}->{'end'};\r
+    print "\nclean:\n".\r
+    "\trm -f *.o *.exe *.res.o *.map\n".\r
+    "\n".\r
+    "FORCE:\n";\r
+    select STDOUT; close OUT;\r
+\r
+}\r
+\r
+##-- Borland makefile\r
+if (defined $makefiles{'borland'}) {\r
+    $dirpfx = &dirpfx($makefiles{'borland'}, "\\");\r
+\r
+    %stdlibs = (  # Borland provides many Win32 API libraries intrinsically\r
+      "advapi32" => 1,\r
+      "comctl32" => 1,\r
+      "comdlg32" => 1,\r
+      "gdi32" => 1,\r
+      "imm32" => 1,\r
+      "shell32" => 1,\r
+      "user32" => 1,\r
+      "winmm" => 1,\r
+      "winspool" => 1,\r
+      "wsock32" => 1,\r
+    );\r
+    open OUT, ">$makefiles{'borland'}"; select OUT;\r
+    print\r
+    "# Makefile for $project_name under Borland C.\n".\r
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    # bcc32 command line option is -D not /D\r
+    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;\r
+    print $_;\r
+    print\r
+    "\n".\r
+    "# If you rename this file to `Makefile', you should change this line,\n".\r
+    "# so that the .rsp files still depend on the correct makefile.\n".\r
+    "MAKEFILE = Makefile.bor\n".\r
+    "\n".\r
+    "# C compilation flags\n".\r
+    "CFLAGS = -D_WINDOWS -DWINVER=0x0500\n".\r
+    "# Resource compilation flags\n".\r
+    "RCFLAGS = -DNO_WINRESRC_H -DWIN32 -D_WIN32 -DWINVER=0x0401\n".\r
+    "\n".\r
+    "# Get include directory for resource compiler\n".\r
+    "!if !\$d(BCB)\n".\r
+    "BCB = \$(MAKEDIR)\\..\n".\r
+    "!endif\n".\r
+    "\n".\r
+    $makefile_extra{'borland'}->{'vars'} .\r
+    "\n".\r
+    ".c.obj:\n".\r
+    &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT)".\r
+              " \$(CFLAGS) \$(XFLAGS) ".\r
+              (join " ", map {"-I$dirpfx$_"} @srcdirs) .\r
+              " /c \$*.c",69)."\n".\r
+    ".rc.res:\n".\r
+    &splitline("\tbrcc32 \$(RCFL) -i \$(BCB)\\include -r".\r
+      " \$(RCFLAGS) \$*.rc",69)."\n".\r
+    "\n";\r
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));\r
+    print "\n\n";\r
+    foreach $p (&prognames("G:C")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr =  &objects($p, "X.obj", "X.res", undef);\r
+      print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";\r
+      my $ap = ($type eq "G") ? "-aa" : "-ap";\r
+      print "\tilink32 $ap -Gn -L\$(BCB)\\lib \@$prog.rsp\n\n";\r
+    }\r
+    foreach $p (&prognames("G:C")) {\r
+      ($prog, $type) = split ",", $p;\r
+      print $prog, ".rsp: \$(MAKEFILE)\n";\r
+      $objstr = &objects($p, "X.obj", undef, undef);\r
+      @objlist = split " ", $objstr;\r
+      @objlines = ("");\r
+      foreach $i (@objlist) {\r
+        if (length($objlines[$#objlines] . " $i") > 50) {\r
+          push @objlines, "";\r
+        }\r
+        $objlines[$#objlines] .= " $i";\r
+      }\r
+      $c0w = ($type eq "G") ? "c0w32" : "c0x32";\r
+      print "\techo $c0w + > $prog.rsp\n";\r
+      for ($i=0; $i<=$#objlines; $i++) {\r
+        $plus = ($i < $#objlines ? " +" : "");\r
+        print "\techo$objlines[$i]$plus >> $prog.rsp\n";\r
+      }\r
+      print "\techo $prog.exe >> $prog.rsp\n";\r
+      $objstr = &objects($p, "X.obj", "X.res", undef);\r
+      @libs = split " ", &objects($p, undef, undef, "X");\r
+      @libs = grep { !$stdlibs{$_} } @libs;\r
+      unshift @libs, "cw32", "import32";\r
+      $libstr = join ' ', @libs;\r
+      print "\techo nul,$libstr, >> $prog.rsp\n";\r
+      print "\techo " . &objects($p, undef, "X.res", undef) . " >> $prog.rsp\n";\r
+      print "\n";\r
+    }\r
+    foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\", "borland")) {\r
+      if ($forceobj{$d->{obj_orig}}) {\r
+        printf("%s: FORCE\n", $d->{obj});\r
+      } else {\r
+        print &splitline(sprintf("%s: %s", $d->{obj},\r
+                                 join " ", @{$d->{deps}})), "\n";\r
+      }\r
+    }\r
+    print "\n";\r
+    print $makefile_extra{'borland'}->{'end'};\r
+    print "\nclean:\n".\r
+    "\t-del *.obj\n".\r
+    "\t-del *.exe\n".\r
+    "\t-del *.res\n".\r
+    "\t-del *.pch\n".\r
+    "\t-del *.aps\n".\r
+    "\t-del *.il*\n".\r
+    "\t-del *.pdb\n".\r
+    "\t-del *.rsp\n".\r
+    "\t-del *.tds\n".\r
+    "\t-del *.\$\$\$\$\$\$\n".\r
+    "\n".\r
+    "FORCE:\n".\r
+    "\t-rem dummy command\n";\r
+    select STDOUT; close OUT;\r
+}\r
+\r
+if (defined $makefiles{'vc'}) {\r
+    $dirpfx = &dirpfx($makefiles{'vc'}, "\\");\r
+\r
+    ##-- Visual C++ makefile\r
+    open OUT, ">$makefiles{'vc'}"; select OUT;\r
+    print\r
+      "# Makefile for $project_name under Visual C.\n".\r
+      "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+      "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    print $help;\r
+    print\r
+      "\n".\r
+      "# If you rename this file to `Makefile', you should change this line,\n".\r
+      "# so that the .rsp files still depend on the correct makefile.\n".\r
+      "MAKEFILE = Makefile.vc\n".\r
+      "\n".\r
+      "# C compilation flags\n".\r
+      "CFLAGS = /nologo /W3 /O1 " .\r
+      (join " ", map {"-I$dirpfx$_"} @srcdirs) .\r
+      " /D_WINDOWS /D_WIN32_WINDOWS=0x500 /DWINVER=0x500\n".\r
+      "LFLAGS = /incremental:no /fixed\n".\r
+      "RCFLAGS = -DWIN32 -D_WIN32 -DWINVER=0x0400\n".\r
+      "\n".\r
+      $makefile_extra{'vc'}->{'vars'} .\r
+      "\n".\r
+      "\n";\r
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));\r
+    print "\n\n";\r
+    foreach $p (&prognames("G:C")) {\r
+       ($prog, $type) = split ",", $p;\r
+       $objstr = &objects($p, "X.obj", "X.res", undef);\r
+       print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";\r
+       print "\tlink \$(LFLAGS) \$(XLFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n";\r
+    }\r
+    foreach $p (&prognames("G:C")) {\r
+       ($prog, $type) = split ",", $p;\r
+       print $prog, ".rsp: \$(MAKEFILE)\n";\r
+       $objstr = &objects($p, "X.obj", "X.res", "X.lib");\r
+       @objlist = split " ", $objstr;\r
+       @objlines = ("");\r
+       foreach $i (@objlist) {\r
+           if (length($objlines[$#objlines] . " $i") > 50) {\r
+               push @objlines, "";\r
+           }\r
+           $objlines[$#objlines] .= " $i";\r
+       }\r
+       $subsys = ($type eq "G") ? "windows" : "console";\r
+       print "\techo /nologo /subsystem:$subsys > $prog.rsp\n";\r
+       for ($i=0; $i<=$#objlines; $i++) {\r
+           print "\techo$objlines[$i] >> $prog.rsp\n";\r
+       }\r
+       print "\n";\r
+    }\r
+    foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\", "vc")) {\r
+        $extradeps = $forceobj{$d->{obj_orig}} ? ["*.c","*.h","*.rc"] : [];\r
+        print &splitline(sprintf("%s: %s", $d->{obj},\r
+                                 join " ", @$extradeps, @{$d->{deps}})), "\n";\r
+        if ($d->{obj} =~ /.obj$/) {\r
+           print "\tcl \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c ".$d->{deps}->[0],"\n\n";\r
+       } else {\r
+           print "\trc \$(RCFL) -r \$(RCFLAGS) ".$d->{deps}->[0],"\n\n";\r
+       }\r
+    }\r
+    print "\n";\r
+    print $makefile_extra{'vc'}->{'end'};\r
+    print "\nclean: tidy\n".\r
+      "\t-del *.exe\n\n".\r
+      "tidy:\n".\r
+      "\t-del *.obj\n".\r
+      "\t-del *.res\n".\r
+      "\t-del *.pch\n".\r
+      "\t-del *.aps\n".\r
+      "\t-del *.ilk\n".\r
+      "\t-del *.pdb\n".\r
+      "\t-del *.rsp\n".\r
+      "\t-del *.dsp\n".\r
+      "\t-del *.dsw\n".\r
+      "\t-del *.ncb\n".\r
+      "\t-del *.opt\n".\r
+      "\t-del *.plg\n".\r
+      "\t-del *.map\n".\r
+      "\t-del *.idb\n".\r
+      "\t-del debug.log\n";\r
+    select STDOUT; close OUT;\r
+}\r
+\r
+if (defined $makefiles{'vcproj'}) {\r
+    $dirpfx = &dirpfx($makefiles{'vcproj'}, "\\");\r
+\r
+    $orig_dir = cwd;\r
+\r
+    ##-- MSVC 6 Workspace and projects\r
+    #\r
+    # Note: All files created in this section are written in binary\r
+    # mode, because although MSVC's command-line make can deal with\r
+    # LF-only line endings, MSVC project files really _need_ to be\r
+    # CRLF. Hence, in order for mkfiles.pl to generate usable project\r
+    # files even when run from Unix, I make sure all files are binary\r
+    # and explicitly write the CRLFs.\r
+    #\r
+    # Create directories if necessary\r
+    mkdir $makefiles{'vcproj'}\r
+        if(! -d $makefiles{'vcproj'});\r
+    chdir $makefiles{'vcproj'};\r
+    @deps = &deps("X.obj", "X.res", $dirpfx, "\\", "vcproj");\r
+    %all_object_deps = map {$_->{obj} => $_->{deps}} @deps;\r
+    # Create the project files\r
+    # Get names of all Windows projects (GUI and console)\r
+    my @prognames = &prognames("G:C");\r
+    foreach $progname (@prognames) {\r
+      create_vc_project(\%all_object_deps, $progname);\r
+    }\r
+    # Create the workspace file\r
+    open OUT, ">$project_name.dsw"; binmode OUT; select OUT;\r
+    print\r
+    "Microsoft Developer Studio Workspace File, Format Version 6.00\r\n".\r
+    "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r\n".\r
+    "\r\n".\r
+    "###############################################################################\r\n".\r
+    "\r\n";\r
+    # List projects\r
+    foreach $progname (@prognames) {\r
+      ($windows_project, $type) = split ",", $progname;\r
+       print "Project: \"$windows_project\"=\".\\$windows_project\\$windows_project.dsp\" - Package Owner=<4>\r\n";\r
+    }\r
+    print\r
+    "\r\n".\r
+    "Package=<5>\r\n".\r
+    "{{{\r\n".\r
+    "}}}\r\n".\r
+    "\r\n".\r
+    "Package=<4>\r\n".\r
+    "{{{\r\n".\r
+    "}}}\r\n".\r
+    "\r\n".\r
+    "###############################################################################\r\n".\r
+    "\r\n".\r
+    "Global:\r\n".\r
+    "\r\n".\r
+    "Package=<5>\r\n".\r
+    "{{{\r\n".\r
+    "}}}\r\n".\r
+    "\r\n".\r
+    "Package=<3>\r\n".\r
+    "{{{\r\n".\r
+    "}}}\r\n".\r
+    "\r\n".\r
+    "###############################################################################\r\n".\r
+    "\r\n";\r
+    select STDOUT; close OUT;\r
+    chdir $orig_dir;\r
+\r
+    sub create_vc_project {\r
+       my ($all_object_deps, $progname) = @_;\r
+       # Construct program's dependency info\r
+       %seen_objects = ();\r
+       %lib_files = ();\r
+       %source_files = ();\r
+       %header_files = ();\r
+       %resource_files = ();\r
+       @object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib");\r
+       foreach $object_file (@object_files) {\r
+           next if defined $seen_objects{$object_file};\r
+           $seen_objects{$object_file} = 1;\r
+           if($object_file =~ /\.lib$/io) {\r
+               $lib_files{$object_file} = 1;\r
+               next;\r
+           }\r
+           $object_deps = $all_object_deps{$object_file};\r
+           foreach $object_dep (@$object_deps) {\r
+               if($object_dep =~ /\.c$/io) {\r
+                   $source_files{$object_dep} = 1;\r
+                   next;\r
+               }\r
+               if($object_dep =~ /\.h$/io) {\r
+                   $header_files{$object_dep} = 1;\r
+                   next;\r
+               }\r
+               if($object_dep =~ /\.(rc|ico)$/io) {\r
+                   $resource_files{$object_dep} = 1;\r
+                   next;\r
+               }\r
+           }\r
+       }\r
+       $libs = join " ", sort keys %lib_files;\r
+       @source_files = sort keys %source_files;\r
+       @header_files = sort keys %header_files;\r
+       @resources = sort keys %resource_files;\r
+       ($windows_project, $type) = split ",", $progname;\r
+       mkdir $windows_project\r
+           if(! -d $windows_project);\r
+       chdir $windows_project;\r
+       $subsys = ($type eq "G") ? "windows" : "console";\r
+       open OUT, ">$windows_project.dsp"; binmode OUT; select OUT;\r
+       print\r
+       "# Microsoft Developer Studio Project File - Name=\"$windows_project\" - Package Owner=<4>\r\n".\r
+       "# Microsoft Developer Studio Generated Build File, Format Version 6.00\r\n".\r
+       "# ** DO NOT EDIT **\r\n".\r
+       "\r\n".\r
+       "# TARGTYPE \"Win32 (x86) Application\" 0x0101\r\n".\r
+       "\r\n".\r
+       "CFG=$windows_project - Win32 Debug\r\n".\r
+       "!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r\n".\r
+       "!MESSAGE use the Export Makefile command and run\r\n".\r
+       "!MESSAGE \r\n".\r
+       "!MESSAGE NMAKE /f \"$windows_project.mak\".\r\n".\r
+       "!MESSAGE \r\n".\r
+       "!MESSAGE You can specify a configuration when running NMAKE\r\n".\r
+       "!MESSAGE by defining the macro CFG on the command line. For example:\r\n".\r
+       "!MESSAGE \r\n".\r
+       "!MESSAGE NMAKE /f \"$windows_project.mak\" CFG=\"$windows_project - Win32 Debug\"\r\n".\r
+       "!MESSAGE \r\n".\r
+       "!MESSAGE Possible choices for configuration are:\r\n".\r
+       "!MESSAGE \r\n".\r
+       "!MESSAGE \"$windows_project - Win32 Release\" (based on \"Win32 (x86) Application\")\r\n".\r
+       "!MESSAGE \"$windows_project - Win32 Debug\" (based on \"Win32 (x86) Application\")\r\n".\r
+       "!MESSAGE \r\n".\r
+       "\r\n".\r
+       "# Begin Project\r\n".\r
+       "# PROP AllowPerConfigDependencies 0\r\n".\r
+       "# PROP Scc_ProjName \"\"\r\n".\r
+       "# PROP Scc_LocalPath \"\"\r\n".\r
+       "CPP=cl.exe\r\n".\r
+       "MTL=midl.exe\r\n".\r
+       "RSC=rc.exe\r\n".\r
+       "\r\n".\r
+       "!IF  \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".\r
+       "\r\n".\r
+       "# PROP BASE Use_MFC 0\r\n".\r
+       "# PROP BASE Use_Debug_Libraries 0\r\n".\r
+       "# PROP BASE Output_Dir \"Release\"\r\n".\r
+       "# PROP BASE Intermediate_Dir \"Release\"\r\n".\r
+       "# PROP BASE Target_Dir \"\"\r\n".\r
+       "# PROP Use_MFC 0\r\n".\r
+       "# PROP Use_Debug_Libraries 0\r\n".\r
+       "# PROP Output_Dir \"Release\"\r\n".\r
+       "# PROP Intermediate_Dir \"Release\"\r\n".\r
+       "# PROP Ignore_Export_Lib 0\r\n".\r
+       "# PROP Target_Dir \"\"\r\n".\r
+       "# ADD BASE CPP /nologo /W3 /GX /O2 ".\r
+         (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .\r
+         " /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".\r
+       "# ADD CPP /nologo /W3 /GX /O2 ".\r
+         (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .\r
+         " /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".\r
+       "# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".\r
+       "# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".\r
+       "# ADD BASE RSC /l 0x809 /d \"NDEBUG\"\r\n".\r
+       "# ADD RSC /l 0x809 /d \"NDEBUG\"\r\n".\r
+       "BSC32=bscmake.exe\r\n".\r
+       "# ADD BASE BSC32 /nologo\r\n".\r
+       "# ADD BSC32 /nologo\r\n".\r
+       "LINK32=link.exe\r\n".\r
+       "# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /machine:I386\r\n".\r
+       "# ADD LINK32 $libs /nologo /subsystem:$subsys /machine:I386\r\n".\r
+       "# SUBTRACT LINK32 /pdb:none\r\n".\r
+       "\r\n".\r
+       "!ELSEIF  \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n".\r
+       "\r\n".\r
+       "# PROP BASE Use_MFC 0\r\n".\r
+       "# PROP BASE Use_Debug_Libraries 1\r\n".\r
+       "# PROP BASE Output_Dir \"Debug\"\r\n".\r
+       "# PROP BASE Intermediate_Dir \"Debug\"\r\n".\r
+       "# PROP BASE Target_Dir \"\"\r\n".\r
+       "# PROP Use_MFC 0\r\n".\r
+       "# PROP Use_Debug_Libraries 1\r\n".\r
+       "# PROP Output_Dir \"Debug\"\r\n".\r
+       "# PROP Intermediate_Dir \"Debug\"\r\n".\r
+       "# PROP Ignore_Export_Lib 0\r\n".\r
+       "# PROP Target_Dir \"\"\r\n".\r
+       "# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od ".\r
+         (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .\r
+         " /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".\r
+       "# ADD CPP /nologo /W3 /Gm /GX /ZI /Od ".\r
+         (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .\r
+         " /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".\r
+       "# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".\r
+       "# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".\r
+       "# ADD BASE RSC /l 0x809 /d \"_DEBUG\"\r\n".\r
+       "# ADD RSC /l 0x809 /d \"_DEBUG\"\r\n".\r
+       "BSC32=bscmake.exe\r\n".\r
+       "# ADD BASE BSC32 /nologo\r\n".\r
+       "# ADD BSC32 /nologo\r\n".\r
+       "LINK32=link.exe\r\n".\r
+       "# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".\r
+       "# ADD LINK32 $libs /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".\r
+       "# SUBTRACT LINK32 /pdb:none\r\n".\r
+       "\r\n".\r
+       "!ENDIF \r\n".\r
+       "\r\n".\r
+       "# Begin Target\r\n".\r
+       "\r\n".\r
+       "# Name \"$windows_project - Win32 Release\"\r\n".\r
+       "# Name \"$windows_project - Win32 Debug\"\r\n".\r
+       "# Begin Group \"Source Files\"\r\n".\r
+       "\r\n".\r
+       "# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\r\n";\r
+       foreach $source_file (@source_files) {\r
+           print\r
+             "# Begin Source File\r\n".\r
+             "\r\n".\r
+             "SOURCE=..\\..\\$source_file\r\n";\r
+           if($source_file =~ /ssh\.c/io) {\r
+               # Disable 'Edit and continue' as Visual Studio can't handle the macros\r
+               print\r
+                 "\r\n".\r
+                 "!IF  \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".\r
+                 "\r\n".\r
+                 "!ELSEIF  \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n".\r
+                 "\r\n".\r
+                 "# ADD CPP /Zi\r\n".\r
+                 "\r\n".\r
+                 "!ENDIF \r\n".\r
+                 "\r\n";\r
+           }\r
+           print "# End Source File\r\n";\r
+       }\r
+       print\r
+       "# End Group\r\n".\r
+       "# Begin Group \"Header Files\"\r\n".\r
+       "\r\n".\r
+       "# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\r\n";\r
+       foreach $header_file (@header_files) {\r
+           print\r
+             "# Begin Source File\r\n".\r
+             "\r\n".\r
+             "SOURCE=..\\..\\$header_file\r\n".\r
+             "# End Source File\r\n";\r
+       }\r
+       print\r
+       "# End Group\r\n".\r
+       "# Begin Group \"Resource Files\"\r\n".\r
+       "\r\n".\r
+       "# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe\"\r\n";\r
+       foreach $resource_file (@resources) {\r
+           print\r
+             "# Begin Source File\r\n".\r
+             "\r\n".\r
+             "SOURCE=..\\..\\$resource_file\r\n".\r
+             "# End Source File\r\n";\r
+       }\r
+       print\r
+       "# End Group\r\n".\r
+       "# End Target\r\n".\r
+       "# End Project\r\n";\r
+       select STDOUT; close OUT;\r
+       chdir "..";\r
+    }\r
+}\r
+\r
+if (defined $makefiles{'gtk'}) {\r
+    $dirpfx = &dirpfx($makefiles{'gtk'}, "/");\r
+\r
+    ##-- X/GTK/Unix makefile\r
+    open OUT, ">$makefiles{'gtk'}"; select OUT;\r
+    print\r
+    "# Makefile for $project_name under X/GTK and Unix.\n".\r
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    # gcc command line option is -D not /D\r
+    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;\r
+    print $_;\r
+    print\r
+    "\n".\r
+    "# You can define this path to point at your tools if you need to\n".\r
+    "# TOOLPATH = /opt/gcc/bin\n".\r
+    "CC = \$(TOOLPATH)cc\n".\r
+    "# If necessary set the path to krb5-config here\n".\r
+    "KRB5CONFIG=krb5-config\n".\r
+    "# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'\n".\r
+    "# (depending on what works on your system) if you want to enforce\n".\r
+    "# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0 x11'\n".\r
+    "# if you want to enforce 2.0. The default is to try 2.0 and fall back\n".\r
+    "# to 1.2 if it isn't found.\n".\r
+    "GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 x11 \$\$0 2>/dev/null || gtk-config \$\$0'\n".\r
+    "\n".\r
+    "-include Makefile.local\n".\r
+    "\n".\r
+    "unexport CFLAGS # work around a weird issue with krb5-config\n".\r
+    "\n".\r
+    &splitline("CFLAGS = -O2 -Wall -Werror -g " .\r
+              (join " ", map {"-I$dirpfx$_"} @srcdirs) .\r
+              " \$(shell \$(GTK_CONFIG) --cflags)").\r
+                " -D _FILE_OFFSET_BITS=64\n".\r
+    "XLDFLAGS = \$(LDFLAGS) \$(shell \$(GTK_CONFIG) --libs)\n".\r
+    "ULDFLAGS = \$(LDFLAGS)\n".\r
+    "ifeq (,\$(findstring NO_GSSAPI,\$(COMPAT)))\n".\r
+    "ifeq (,\$(findstring STATIC_GSSAPI,\$(COMPAT)))\n".\r
+    "XLDFLAGS+= -ldl\n".\r
+    "ULDFLAGS+= -ldl\n".\r
+    "else\n".\r
+    "CFLAGS+= -DNO_LIBDL \$(shell \$(KRB5CONFIG) --cflags gssapi)\n".\r
+    "XLDFLAGS+= \$(shell \$(KRB5CONFIG) --libs gssapi)\n".\r
+    "ULDFLAGS+= \$(shell \$(KRB5CONFIG) --libs gssapi)\n".\r
+    "endif\n".\r
+    "endif\n".\r
+    "INSTALL=install\n".\r
+    "INSTALL_PROGRAM=\$(INSTALL)\n".\r
+    "INSTALL_DATA=\$(INSTALL)\n".\r
+    "prefix=/usr/local\n".\r
+    "exec_prefix=\$(prefix)\n".\r
+    "bindir=\$(exec_prefix)/bin\n".\r
+    "mandir=\$(prefix)/man\n".\r
+    "man1dir=\$(mandir)/man1\n".\r
+    "\n".\r
+    &def($makefile_extra{'gtk'}->{'vars'}) .\r
+    "\n".\r
+    ".SUFFIXES:\n".\r
+    "\n".\r
+    "\n";\r
+    print &splitline("all:" . join "", map { " $_" } &progrealnames("X:U"));\r
+    print "\n\n";\r
+    foreach $p (&prognames("X:U")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr = &objects($p, "X.o", undef, undef);\r
+      print &splitline($prog . ": " . $objstr), "\n";\r
+      $libstr = &objects($p, undef, undef, "-lX");\r
+      print &splitline("\t\$(CC) -o \$@ " .\r
+                       $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n";\r
+    }\r
+    foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) {\r
+      if ($forceobj{$d->{obj_orig}}) {\r
+        printf("%s: FORCE\n", $d->{obj});\r
+      } else {\r
+        print &splitline(sprintf("%s: %s", $d->{obj},\r
+                                 join " ", @{$d->{deps}})), "\n";\r
+      }\r
+      print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n");\r
+    }\r
+    print "\n";\r
+    print $makefile_extra{'gtk'}->{'end'};\r
+    print "\nclean:\n".\r
+    "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n";\r
+    print "\nFORCE:\n";\r
+    select STDOUT; close OUT;\r
+}\r
+\r
+if (defined $makefiles{'unix'}) {\r
+    $dirpfx = &dirpfx($makefiles{'unix'}, "/");\r
+\r
+    ##-- GTK-free pure-Unix makefile for non-GUI apps only\r
+    open OUT, ">$makefiles{'unix'}"; select OUT;\r
+    print\r
+    "# Makefile for $project_name under Unix.\n".\r
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    # gcc command line option is -D not /D\r
+    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;\r
+    print $_;\r
+    print\r
+    "\n".\r
+    "# You can define this path to point at your tools if you need to\n".\r
+    "# TOOLPATH = /opt/gcc/bin\n".\r
+    "CC = \$(TOOLPATH)cc\n".\r
+    "\n".\r
+    "-include Makefile.local\n".\r
+    "\n".\r
+    "unexport CFLAGS # work around a weird issue with krb5-config\n".\r
+    "\n".\r
+    &splitline("CFLAGS = -O2 -Wall -Werror -g " .\r
+              (join " ", map {"-I$dirpfx$_"} @srcdirs)).\r
+                " -D _FILE_OFFSET_BITS=64\n".\r
+    "ULDFLAGS = \$(LDFLAGS)\n".\r
+    "INSTALL=install\n".\r
+    "INSTALL_PROGRAM=\$(INSTALL)\n".\r
+    "INSTALL_DATA=\$(INSTALL)\n".\r
+    "prefix=/usr/local\n".\r
+    "exec_prefix=\$(prefix)\n".\r
+    "bindir=\$(exec_prefix)/bin\n".\r
+    "mandir=\$(prefix)/man\n".\r
+    "man1dir=\$(mandir)/man1\n".\r
+    "\n".\r
+    &def($makefile_extra{'unix'}->{'vars'}) .\r
+    "\n".\r
+    ".SUFFIXES:\n".\r
+    "\n".\r
+    "\n";\r
+    print &splitline("all:" . join "", map { " $_" } &progrealnames("U"));\r
+    print "\n\n";\r
+    foreach $p (&prognames("U")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr = &objects($p, "X.o", undef, undef);\r
+      print &splitline($prog . ": " . $objstr), "\n";\r
+      $libstr = &objects($p, undef, undef, "-lX");\r
+      print &splitline("\t\$(CC) -o \$@ " .\r
+                       $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n";\r
+    }\r
+    foreach $d (&deps("X.o", undef, $dirpfx, "/", "unix")) {\r
+      if ($forceobj{$d->{obj_orig}}) {\r
+        printf("%s: FORCE\n", $d->{obj});\r
+      } else {\r
+        print &splitline(sprintf("%s: %s", $d->{obj},\r
+                                 join " ", @{$d->{deps}})), "\n";\r
+      }\r
+      print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n");\r
+    }\r
+    print "\n";\r
+    print &def($makefile_extra{'unix'}->{'end'});\r
+    print "\nclean:\n".\r
+    "\trm -f *.o". (join "", map { " $_" } &progrealnames("U")) . "\n";\r
+    print "\nFORCE:\n";\r
+    select STDOUT; close OUT;\r
+}\r
+\r
+if (defined $makefiles{'ac'}) {\r
+    $dirpfx = &dirpfx($makefiles{'ac'}, "/");\r
+\r
+    ##-- Unix/autoconf makefile\r
+    open OUT, ">$makefiles{'ac'}"; select OUT;\r
+    print\r
+    "# Makefile.in for $project_name under Unix with Autoconf.\n".\r
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    # gcc command line option is -D not /D\r
+    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;\r
+    print $_;\r
+    print\r
+    "\n".\r
+    "CC = \@CC\@\n".\r
+    "\n".\r
+    &splitline("CFLAGS = \@CFLAGS\@ \@PUTTYCFLAGS\@ \@CPPFLAGS\@ " .\r
+               "\@DEFS\@ \@GTK_CFLAGS\@ " .\r
+              (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".\r
+    "XLDFLAGS = \@LDFLAGS\@ \@LIBS\@ \@GTK_LIBS\@\n".\r
+    "ULDFLAGS = \@LDFLAGS\@ \@LIBS\@\n".\r
+    "INSTALL=\@INSTALL\@\n".\r
+    "INSTALL_PROGRAM=\$(INSTALL)\n".\r
+    "INSTALL_DATA=\$(INSTALL)\n".\r
+    "prefix=\@prefix\@\n".\r
+    "exec_prefix=\@exec_prefix\@\n".\r
+    "bindir=\@bindir\@\n".\r
+    "datarootdir=\@datarootdir\@\n".\r
+    "mandir=\@mandir\@\n".\r
+    "man1dir=\$(mandir)/man1\n".\r
+    "\n".\r
+    &def($makefile_extra{'gtk'}->{'vars'}) .\r
+    "\n".\r
+    ".SUFFIXES:\n".\r
+    "\n".\r
+    "\n".\r
+    "all: \@all_targets\@\n".\r
+    &splitline("all-cli:" . join "", map { " $_" } &progrealnames("U"))."\n".\r
+    &splitline("all-gtk:" . join "", map { " $_" } &progrealnames("X"))."\n";\r
+    print "\n";\r
+    foreach $p (&prognames("X:U")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr = &objects($p, "X.o", undef, undef);\r
+      print &splitline($prog . ": " . $objstr), "\n";\r
+      $libstr = &objects($p, undef, undef, "-lX");\r
+      print &splitline("\t\$(CC) -o \$@ " .\r
+                       $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n";\r
+    }\r
+    foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) {\r
+      if ($forceobj{$d->{obj_orig}}) {\r
+        printf("%s: FORCE\n", $d->{obj});\r
+      } else {\r
+        print &splitline(sprintf("%s: %s", $d->{obj},\r
+                                 join " ", @{$d->{deps}})), "\n";\r
+      }\r
+      print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n");\r
+    }\r
+    print "\n";\r
+    print $makefile_extra{'gtk'}->{'end'};\r
+    print "\nclean:\n".\r
+    "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n";\r
+    print "\ndistclean: clean\n".\r
+    "\t". &splitline("rm -f config.status config.cache config.log ".\r
+                    "configure.lineno config.status.lineno Makefile") . "\n";\r
+    print "\nFORCE:\n";\r
+    select STDOUT; close OUT;\r
+}\r
+\r
+if (defined $makefiles{'lcc'}) {\r
+    $dirpfx = &dirpfx($makefiles{'lcc'}, "\\");\r
+\r
+    ##-- lcc makefile\r
+    open OUT, ">$makefiles{'lcc'}"; select OUT;\r
+    print\r
+    "# Makefile for $project_name under lcc.\n".\r
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    # lcc command line option is -D not /D\r
+    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;\r
+    print $_;\r
+    print\r
+    "\n".\r
+    "# If you rename this file to `Makefile', you should change this line,\n".\r
+    "# so that the .rsp files still depend on the correct makefile.\n".\r
+    "MAKEFILE = Makefile.lcc\n".\r
+    "\n".\r
+    "# C compilation flags\n".\r
+    "CFLAGS = -D_WINDOWS " .\r
+      (join " ", map {"-I$dirpfx$_"} @srcdirs) .\r
+      "\n".\r
+    "# Resource compilation flags\n".\r
+    "RCFLAGS = \n".\r
+    "\n".\r
+    "# Get include directory for resource compiler\n".\r
+    "\n".\r
+    $makefile_extra{'lcc'}->{'vars'} .\r
+    "\n";\r
+    print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));\r
+    print "\n\n";\r
+    foreach $p (&prognames("G:C")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr = &objects($p, "X.obj", "X.res", undef);\r
+      print &splitline("$prog.exe: " . $objstr ), "\n";\r
+      $subsystemtype = '';\r
+      if ($type eq "G") { $subsystemtype = "-subsystem  windows"; }\r
+      my $libss = "shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib";\r
+      print &splitline("\tlcclnk $subsystemtype -o $prog.exe $objstr $libss");\r
+      print "\n\n";\r
+    }\r
+\r
+    foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\", "lcc")) {\r
+      if ($forceobj{$d->{obj_orig}}) {\r
+         printf("%s: FORCE\n", $d->{obj});\r
+      } else {\r
+         print &splitline(sprintf("%s: %s", $d->{obj},\r
+                          join " ", @{$d->{deps}})), "\n";\r
+      }\r
+      if ($d->{obj} =~ /\.obj$/) {\r
+         print &splitline("\tlcc -O -p6 \$(COMPAT)".\r
+                          " \$(CFLAGS) \$(XFLAGS) ".$d->{deps}->[0],69)."\n";\r
+      } else {\r
+          print &splitline("\tlrc \$(RCFL) -r \$(RCFLAGS) ".\r
+                           $d->{deps}->[0],69)."\n";\r
+      }\r
+    }\r
+    print "\n";\r
+    print $makefile_extra{'lcc'}->{'end'};\r
+    print "\nclean:\n".\r
+    "\t-del *.obj\n".\r
+    "\t-del *.exe\n".\r
+    "\t-del *.res\n".\r
+    "\n".\r
+    "FORCE:\n";\r
+\r
+    select STDOUT; close OUT;\r
+}\r
+\r
+if (defined $makefiles{'osx'}) {\r
+    $dirpfx = &dirpfx($makefiles{'osx'}, "/");\r
+\r
+    ##-- Mac OS X makefile\r
+    open OUT, ">$makefiles{'osx'}"; select OUT;\r
+    print\r
+    "# Makefile for $project_name under Mac OS X.\n".\r
+    "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".\r
+    "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";\r
+    # gcc command line option is -D not /D\r
+    ($_ = $help) =~ s/([=" ])\/D/$1-D/gs;\r
+    print $_;\r
+    print\r
+    "CC = \$(TOOLPATH)gcc\n".\r
+    "\n".\r
+    &splitline("CFLAGS = -O2 -Wall -Werror -g " .\r
+              (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".\r
+    "MLDFLAGS = -framework Cocoa\n".\r
+    "ULDFLAGS =\n".\r
+    "\n" .\r
+    $makefile_extra{'osx'}->{'vars'} .\r
+    "\n" .\r
+    &splitline("all:" . join "", map { " $_" } &progrealnames("MX:U")) .\r
+    "\n";\r
+    foreach $p (&prognames("MX")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr = &objects($p, "X.o", undef, undef);\r
+      $icon = &special($p, ".icns");\r
+      $infoplist = &special($p, "info.plist");\r
+      print "${prog}.app:\n\tmkdir -p \$\@\n";\r
+      print "${prog}.app/Contents: ${prog}.app\n\tmkdir -p \$\@\n";\r
+      print "${prog}.app/Contents/MacOS: ${prog}.app/Contents\n\tmkdir -p \$\@\n";\r
+      $targets = "${prog}.app/Contents/MacOS/$prog";\r
+      if (defined $icon) {\r
+       print "${prog}.app/Contents/Resources: ${prog}.app/Contents\n\tmkdir -p \$\@\n";\r
+       print "${prog}.app/Contents/Resources/${prog}.icns: ${prog}.app/Contents/Resources $icon\n\tcp $icon \$\@\n";\r
+       $targets .= " ${prog}.app/Contents/Resources/${prog}.icns";\r
+      }\r
+      if (defined $infoplist) {\r
+       print "${prog}.app/Contents/Info.plist: ${prog}.app/Contents/Resources $infoplist\n\tcp $infoplist \$\@\n";\r
+       $targets .= " ${prog}.app/Contents/Info.plist";\r
+      }\r
+      $targets .= " \$(${prog}_extra)";\r
+      print &splitline("${prog}: $targets", 69) . "\n\n";\r
+      print &splitline("${prog}.app/Contents/MacOS/$prog: ".\r
+                      "${prog}.app/Contents/MacOS " . $objstr), "\n";\r
+      $libstr = &objects($p, undef, undef, "-lX");\r
+      print &splitline("\t\$(CC) \$(MLDFLAGS) -o \$@ " .\r
+                       $objstr . " $libstr", 69), "\n\n";\r
+    }\r
+    foreach $p (&prognames("U")) {\r
+      ($prog, $type) = split ",", $p;\r
+      $objstr = &objects($p, "X.o", undef, undef);\r
+      print &splitline($prog . ": " . $objstr), "\n";\r
+      $libstr = &objects($p, undef, undef, "-lX");\r
+      print &splitline("\t\$(CC) \$(ULDFLAGS) -o \$@ " .\r
+                       $objstr . " $libstr", 69), "\n\n";\r
+    }\r
+    foreach $d (&deps("X.o", undef, $dirpfx, "/", "osx")) {\r
+      if ($forceobj{$d->{obj_orig}}) {\r
+         printf("%s: FORCE\n", $d->{obj});\r
+      } else {\r
+         print &splitline(sprintf("%s: %s", $d->{obj},\r
+                                  join " ", @{$d->{deps}})), "\n";\r
+      }\r
+      $firstdep = $d->{deps}->[0];\r
+      if ($firstdep =~ /\.c$/) {\r
+         print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n";\r
+      } elsif ($firstdep =~ /\.m$/) {\r
+         print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n";\r
+      }\r
+    }\r
+    print "\n".&def($makefile_extra{'osx'}->{'end'});\r
+    print "\nclean:\n".\r
+    "\trm -f *.o *.dmg". (join "", map { " $_" } &progrealnames("U")) . "\n".\r
+    "\trm -rf *.app\n".\r
+    "\n".\r
+    "FORCE:\n";\r
+    select STDOUT; close OUT;\r
+}\r
+\r
+if (defined $makefiles{'devcppproj'}) {\r
+    $dirpfx = &dirpfx($makefiles{'devcppproj'}, "\\");\r
+    $orig_dir = cwd;\r
+\r
+    ##-- Dev-C++ 5 projects\r
+    #\r
+    # Note: All files created in this section are written in binary\r
+    # mode to prevent any posibility of misinterpreted line endings.\r
+    # I don't know if Dev-C++ is as touchy as MSVC with LF-only line\r
+    # endings. But however, CRLF line endings are the common way on\r
+    # Win32 machines where Dev-C++ is running.\r
+    # Hence, in order for mkfiles.pl to generate CRLF project files\r
+    # even when run from Unix, I make sure all files are binary and\r
+    # explicitly write the CRLFs.\r
+    #\r
+    # Create directories if necessary\r
+    mkdir $makefiles{'devcppproj'}\r
+        if(! -d $makefiles{'devcppproj'});\r
+    chdir $makefiles{'devcppproj'};\r
+    @deps = &deps("X.obj", "X.res", $dirpfx, "\\", "devcppproj");\r
+    %all_object_deps = map {$_->{obj} => $_->{deps}} @deps;\r
+    # Make dir names FAT/NTFS compatible\r
+    my @srcdirs = @srcdirs;\r
+    for ($i=0; $i<@srcdirs; $i++) {\r
+      $srcdirs[$i] =~ s/\//\\/g;\r
+      $srcdirs[$i] =~ s/\\$//;\r
+    }\r
+    # Create the project files\r
+    # Get names of all Windows projects (GUI and console)\r
+    my @prognames = &prognames("G:C");\r
+    foreach $progname (@prognames) {\r
+      create_devcpp_project(\%all_object_deps, $progname);\r
+    }\r
+\r
+    sub create_devcpp_project {\r
+      my ($all_object_deps, $progname) = @_;\r
+      # Construct program's dependency info (Taken from 'vcproj', seems to work right here, too.)\r
+      %seen_objects = ();\r
+      %lib_files = ();\r
+      %source_files = ();\r
+      %header_files = ();\r
+      %resource_files = ();\r
+      @object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib");\r
+      foreach $object_file (@object_files) {\r
+      next if defined $seen_objects{$object_file};\r
+      $seen_objects{$object_file} = 1;\r
+      if($object_file =~ /\.lib$/io) {\r
+    $lib_files{$object_file} = 1;\r
+    next;\r
+      }\r
+      $object_deps = $all_object_deps{$object_file};\r
+      foreach $object_dep (@$object_deps) {\r
+    if($object_dep =~ /\.c$/io) {\r
+        $source_files{$object_dep} = 1;\r
+        next;\r
+    }\r
+    if($object_dep =~ /\.h$/io) {\r
+        $header_files{$object_dep} = 1;\r
+        next;\r
+    }\r
+    if($object_dep =~ /\.(rc|ico)$/io) {\r
+        $resource_files{$object_dep} = 1;\r
+        next;\r
+    }\r
+      }\r
+      }\r
+      $libs = join " ", sort keys %lib_files;\r
+      @source_files = sort keys %source_files;\r
+      @header_files = sort keys %header_files;\r
+      @resources = sort keys %resource_files;\r
+  ($windows_project, $type) = split ",", $progname;\r
+      mkdir $windows_project\r
+      if(! -d $windows_project);\r
+      chdir $windows_project;\r
+\r
+  $subsys = ($type eq "G") ? "0" : "1";  # 0 = Win32 GUI, 1 = Win32 Console\r
+      open OUT, ">$windows_project.dev"; binmode OUT; select OUT;\r
+      print\r
+      "# DEV-C++ 5 Project File - $windows_project.dev\r\n".\r
+      "# ** DO NOT EDIT **\r\n".\r
+      "\r\n".\r
+      # No difference between DEBUG and RELEASE here as in 'vcproj', because\r
+      # Dev-C++ does not support mutiple compilation profiles in one single project.\r
+      # (At least I can say this for Dev-C++ 5 Beta)\r
+      "[Project]\r\n".\r
+      "FileName=$windows_project.dev\r\n".\r
+      "Name=$windows_project\r\n".\r
+      "Ver=1\r\n".\r
+      "IsCpp=1\r\n".\r
+      "Type=$subsys\r\n".\r
+      # Multimon is disabled here, as Dev-C++ (Version 5 Beta) does not have multimon.h\r
+      "Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_\@\@_\r\n".\r
+      "CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_\@\@_\r\n".\r
+      "Includes=" . (join ";", map {"..\\..\\$dirpfx$_"} @srcdirs) . "\r\n".\r
+      "Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_\@\@_\r\n".\r
+      "Libs=\r\n".\r
+      "UnitCount=" . (@source_files + @header_files + @resources) . "\r\n".\r
+      "Folders=\"Header Files\",\"Resource Files\",\"Source Files\"\r\n".\r
+      "ObjFiles=\r\n".\r
+      "PrivateResource=${windows_project}_private.rc\r\n".\r
+      "ResourceIncludes=..\\..\\..\\WINDOWS\r\n".\r
+      "MakeIncludes=\r\n".\r
+      "Icon=\r\n". # It's ok to leave this blank.\r
+      "ExeOutput=\r\n".\r
+      "ObjectOutput=\r\n".\r
+      "OverrideOutput=0\r\n".\r
+      "OverrideOutputName=$windows_project.exe\r\n".\r
+      "HostApplication=\r\n".\r
+      "CommandLine=\r\n".\r
+      "UseCustomMakefile=0\r\n".\r
+      "CustomMakefile=\r\n".\r
+      "IncludeVersionInfo=0\r\n".\r
+      "SupportXPThemes=0\r\n".\r
+      "CompilerSet=0\r\n".\r
+      "CompilerSettings=0000000000000000000000\r\n".\r
+      "\r\n";\r
+      $unit_count = 1;\r
+      foreach $source_file (@source_files) {\r
+      print\r
+        "[Unit$unit_count]\r\n".\r
+        "FileName=..\\..\\$source_file\r\n".\r
+        "Folder=Source Files\r\n".\r
+        "Compile=1\r\n".\r
+        "CompileCpp=0\r\n".\r
+        "Link=1\r\n".\r
+        "Priority=1000\r\n".\r
+        "OverrideBuildCmd=0\r\n".\r
+        "BuildCmd=\r\n".\r
+        "\r\n";\r
+      $unit_count++;\r
+  }\r
+      foreach $header_file (@header_files) {\r
+      print\r
+        "[Unit$unit_count]\r\n".\r
+        "FileName=..\\..\\$header_file\r\n".\r
+        "Folder=Header Files\r\n".\r
+        "Compile=1\r\n".\r
+        "CompileCpp=1\r\n". # Dev-C++ want's to compile all header files with both compilers C and C++. It does not hurt.\r
+        "Link=1\r\n".\r
+        "Priority=1000\r\n".\r
+        "OverrideBuildCmd=0\r\n".\r
+        "BuildCmd=\r\n".\r
+        "\r\n";\r
+      $unit_count++;\r
+  }\r
+      foreach $resource_file (@resources) {\r
+      if ($resource_file =~ /.*\.(ico|cur|bmp|dlg|rc2|rct|bin|rgs|gif|jpg|jpeg|jpe)/io) { # Default filter as in 'vcproj'\r
+        $Compile = "0";    # Don't compile images and other binary resource files\r
+        $CompileCpp = "0";\r
+      } else {\r
+        $Compile = "1";\r
+        $CompileCpp = "1"; # Dev-C++ want's to compile all .rc files with both compilers C and C++. It does not hurt.\r
+      }\r
+      print\r
+        "[Unit$unit_count]\r\n".\r
+        "FileName=..\\..\\$resource_file\r\n".\r
+        "Folder=Resource Files\r\n".\r
+        "Compile=$Compile\r\n".\r
+        "CompileCpp=$CompileCpp\r\n".\r
+        "Link=0\r\n".\r
+        "Priority=1000\r\n".\r
+        "OverrideBuildCmd=0\r\n".\r
+        "BuildCmd=\r\n".\r
+        "\r\n";\r
+      $unit_count++;\r
+  }\r
+      #Note: By default, [VersionInfo] is not used.\r
+      print\r
+      "[VersionInfo]\r\n".\r
+      "Major=0\r\n".\r
+      "Minor=0\r\n".\r
+      "Release=1\r\n".\r
+      "Build=1\r\n".\r
+      "LanguageID=1033\r\n".\r
+      "CharsetID=1252\r\n".\r
+      "CompanyName=\r\n".\r
+      "FileVersion=0.1\r\n".\r
+      "FileDescription=\r\n".\r
+      "InternalName=\r\n".\r
+      "LegalCopyright=\r\n".\r
+      "LegalTrademarks=\r\n".\r
+      "OriginalFilename=$windows_project.exe\r\n".\r
+      "ProductName=$windows_project\r\n".\r
+      "ProductVersion=0.1\r\n".\r
+      "AutoIncBuildNr=0\r\n";\r
+      select STDOUT; close OUT;\r
+      chdir "..";\r
+    }\r
+}\r
diff --git a/putty/MKUNXARC.SH b/putty/MKUNXARC.SH
new file mode 100644 (file)
index 0000000..8047850
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/sh \r
+\r
+# Build a Unix source distribution from the PuTTY CVS area.\r
+#\r
+# Pass an argument of the form `2004-02-08' to have the archive\r
+# tagged as a development snapshot; of the form `0.54' to have it\r
+# tagged as a release; of the form `r1234' to have it tagged as a\r
+# custom build. Otherwise it'll be tagged as unidentified.\r
+\r
+case "$1" in\r
+  ????-??-??)\r
+    case "$1" in *[!-0-9]*) echo "Malformed snapshot ID '$1'" >&2;exit 1;;esac\r
+    arcsuffix="-`cat LATEST.VER`-$1"\r
+    ver="-DSNAPSHOT=$1"\r
+    docver=\r
+    ;;\r
+  r*)\r
+    arcsuffix="-$1"\r
+    ver="-DSVN_REV=$1"\r
+    docver=\r
+    ;;\r
+  '')\r
+    arcsuffix=\r
+    ver=\r
+    docver=\r
+    ;;\r
+  *)\r
+    case "$1" in *[!.0-9a-z]*) echo "Malformed release ID '$1'">&2;exit 1;;esac\r
+    arcsuffix="-$1"\r
+    ver="-DRELEASE=$1"\r
+    docver="VERSION=\"PuTTY release $1\""\r
+    ;;\r
+esac\r
+\r
+perl mkfiles.pl\r
+(cd doc && make -s ${docver:+"$docver"})\r
+sh mkauto.sh 2>/dev/null\r
+\r
+relver=`cat LATEST.VER`\r
+arcname="putty$arcsuffix"\r
+mkdir uxarc\r
+mkdir uxarc/$arcname\r
+find . -name uxarc -prune -o \\r
+       -name CVS -prune -o \\r
+       -name .svn -prune -o \\r
+       -name . -o \\r
+       -type d -exec mkdir uxarc/$arcname/{} \;\r
+find . -name uxarc -prune -o \\r
+       -name CVS -prune -o \\r
+       -name .cvsignore -prune -o \\r
+       -name .svn -prune -o \\r
+       -name '*.zip' -prune -o \\r
+       -name '*.tar.gz' -prune -o \\r
+       -type f -exec ln -s $PWD/{} uxarc/$arcname/{} \;\r
+if test "x$ver" != "x"; then\r
+  (cd uxarc/$arcname;\r
+   md5sum `find . -name '*.[ch]' -print` > manifest;\r
+   echo "$ver" > version.def)\r
+fi\r
+tar -C uxarc -chzof $arcname.tar.gz $arcname\r
+rm -rf uxarc\r
diff --git a/putty/NETWORK.H b/putty/NETWORK.H
new file mode 100644 (file)
index 0000000..b1b5590
--- /dev/null
@@ -0,0 +1,247 @@
+/*\r
+ * Networking abstraction in PuTTY.\r
+ *\r
+ * The way this works is: a back end can choose to open any number\r
+ * of sockets - including zero, which might be necessary in some.\r
+ * It can register a bunch of callbacks (most notably for when \r
+ * data is received) for each socket, and it can call the networking\r
+ * abstraction to send data without having to worry about blocking.\r
+ * The stuff behind the abstraction takes care of selects and\r
+ * nonblocking writes and all that sort of painful gubbins.\r
+ */\r
+\r
+#ifndef PUTTY_NETWORK_H\r
+#define PUTTY_NETWORK_H\r
+\r
+#ifndef DONE_TYPEDEFS\r
+#define DONE_TYPEDEFS\r
+typedef struct config_tag Config;\r
+typedef struct backend_tag Backend;\r
+typedef struct terminal_tag Terminal;\r
+#endif\r
+\r
+typedef struct SockAddr_tag *SockAddr;\r
+/* pay attention to levels of indirection */\r
+typedef struct socket_function_table **Socket;\r
+typedef struct plug_function_table **Plug;\r
+\r
+#ifndef OSSOCKET_DEFINED\r
+typedef void *OSSocket;\r
+#endif\r
+\r
+struct socket_function_table {\r
+    Plug(*plug) (Socket s, Plug p);\r
+    /* use a different plug (return the old one) */\r
+    /* if p is NULL, it doesn't change the plug */\r
+    /* but it does return the one it's using */\r
+    void (*close) (Socket s);\r
+    int (*write) (Socket s, const char *data, int len);\r
+    int (*write_oob) (Socket s, const char *data, int len);\r
+    void (*flush) (Socket s);\r
+    void (*set_private_ptr) (Socket s, void *ptr);\r
+    void *(*get_private_ptr) (Socket s);\r
+    void (*set_frozen) (Socket s, int is_frozen);\r
+    /* ignored by tcp, but vital for ssl */\r
+    const char *(*socket_error) (Socket s);\r
+};\r
+\r
+struct plug_function_table {\r
+    void (*log)(Plug p, int type, SockAddr addr, int port,\r
+               const char *error_msg, int error_code);\r
+    /*\r
+     * Passes the client progress reports on the process of setting\r
+     * up the connection.\r
+     * \r
+     *         - type==0 means we are about to try to connect to address\r
+     *           `addr' (error_msg and error_code are ignored)\r
+     *         - type==1 means we have failed to connect to address `addr'\r
+     *           (error_msg and error_code are supplied). This is not a\r
+     *           fatal error - we may well have other candidate addresses\r
+     *           to fall back to. When it _is_ fatal, the closing()\r
+     *           function will be called.\r
+     */\r
+    int (*closing)\r
+     (Plug p, const char *error_msg, int error_code, int calling_back);\r
+    /* error_msg is NULL iff it is not an error (ie it closed normally) */\r
+    /* calling_back != 0 iff there is a Plug function */\r
+    /* currently running (would cure the fixme in try_send()) */\r
+    int (*receive) (Plug p, int urgent, char *data, int len);\r
+    /*\r
+     *  - urgent==0. `data' points to `len' bytes of perfectly\r
+     *    ordinary data.\r
+     * \r
+     *  - urgent==1. `data' points to `len' bytes of data,\r
+     *    which were read from before an Urgent pointer.\r
+     * \r
+     *  - urgent==2. `data' points to `len' bytes of data,\r
+     *    the first of which was the one at the Urgent mark.\r
+     */\r
+    void (*sent) (Plug p, int bufsize);\r
+    /*\r
+     * The `sent' function is called when the pending send backlog\r
+     * on a socket is cleared or partially cleared. The new backlog\r
+     * size is passed in the `bufsize' parameter.\r
+     */\r
+    int (*accepting)(Plug p, OSSocket sock);\r
+    /*\r
+     * returns 0 if the host at address addr is a valid host for connecting or error\r
+     */\r
+};\r
+\r
+/* proxy indirection layer */\r
+/* NB, control of 'addr' is passed via new_connection, which takes\r
+ * responsibility for freeing it */\r
+Socket new_connection(SockAddr addr, char *hostname,\r
+                     int port, int privport,\r
+                     int oobinline, int nodelay, int keepalive,\r
+                     Plug plug, const Config *cfg);\r
+Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,\r
+                   const Config *cfg, int addressfamily);\r
+SockAddr name_lookup(char *host, int port, char **canonicalname,\r
+                    const Config *cfg, int addressfamily);\r
+\r
+/* platform-dependent callback from new_connection() */\r
+/* (same caveat about addr as new_connection()) */\r
+Socket platform_new_connection(SockAddr addr, char *hostname,\r
+                              int port, int privport,\r
+                              int oobinline, int nodelay, int keepalive,\r
+                              Plug plug, const Config *cfg);\r
+\r
+/* socket functions */\r
+\r
+void sk_init(void);                   /* called once at program startup */\r
+void sk_cleanup(void);                /* called just before program exit */\r
+\r
+SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family);\r
+SockAddr sk_nonamelookup(const char *host);\r
+void sk_getaddr(SockAddr addr, char *buf, int buflen);\r
+int sk_hostname_is_local(char *name);\r
+int sk_address_is_local(SockAddr addr);\r
+int sk_addrtype(SockAddr addr);\r
+void sk_addrcopy(SockAddr addr, char *buf);\r
+void sk_addr_free(SockAddr addr);\r
+/* sk_addr_dup generates another SockAddr which contains the same data\r
+ * as the original one and can be freed independently. May not actually\r
+ * physically _duplicate_ it: incrementing a reference count so that\r
+ * one more free is required before it disappears is an acceptable\r
+ * implementation. */\r
+SockAddr sk_addr_dup(SockAddr addr);\r
+\r
+/* NB, control of 'addr' is passed via sk_new, which takes responsibility\r
+ * for freeing it, as for new_connection() */\r
+Socket sk_new(SockAddr addr, int port, int privport, int oobinline,\r
+             int nodelay, int keepalive, Plug p);\r
+\r
+Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int address_family);\r
+\r
+Socket sk_register(OSSocket sock, Plug plug);\r
+\r
+#define sk_plug(s,p) (((*s)->plug) (s, p))\r
+#define sk_close(s) (((*s)->close) (s))\r
+#define sk_write(s,buf,len) (((*s)->write) (s, buf, len))\r
+#define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len))\r
+#define sk_flush(s) (((*s)->flush) (s))\r
+\r
+#ifdef DEFINE_PLUG_METHOD_MACROS\r
+#define plug_log(p,type,addr,port,msg,code) (((*p)->log) (p, type, addr, port, msg, code))\r
+#define plug_closing(p,msg,code,callback) (((*p)->closing) (p, msg, code, callback))\r
+#define plug_receive(p,urgent,buf,len) (((*p)->receive) (p, urgent, buf, len))\r
+#define plug_sent(p,bufsize) (((*p)->sent) (p, bufsize))\r
+#define plug_accepting(p, sock) (((*p)->accepting)(p, sock))\r
+#endif\r
+\r
+/*\r
+ * Each socket abstraction contains a `void *' private field in\r
+ * which the client can keep state.\r
+ *\r
+ * This is perhaps unnecessary now that we have the notion of a plug,\r
+ * but there is some existing code that uses it, so it stays.\r
+ */\r
+#define sk_set_private_ptr(s, ptr) (((*s)->set_private_ptr) (s, ptr))\r
+#define sk_get_private_ptr(s) (((*s)->get_private_ptr) (s))\r
+\r
+/*\r
+ * Special error values are returned from sk_namelookup and sk_new\r
+ * if there's a problem. These functions extract an error message,\r
+ * or return NULL if there's no problem.\r
+ */\r
+const char *sk_addr_error(SockAddr addr);\r
+#define sk_socket_error(s) (((*s)->socket_error) (s))\r
+\r
+/*\r
+ * Set the `frozen' flag on a socket. A frozen socket is one in\r
+ * which all READABLE notifications are ignored, so that data is\r
+ * not accepted from the peer until the socket is unfrozen. This\r
+ * exists for two purposes:\r
+ * \r
+ *  - Port forwarding: when a local listening port receives a\r
+ *    connection, we do not want to receive data from the new\r
+ *    socket until we have somewhere to send it. Hence, we freeze\r
+ *    the socket until its associated SSH channel is ready; then we\r
+ *    unfreeze it and pending data is delivered.\r
+ * \r
+ *  - Socket buffering: if an SSH channel (or the whole connection)\r
+ *    backs up or presents a zero window, we must freeze the\r
+ *    associated local socket in order to avoid unbounded buffer\r
+ *    growth.\r
+ */\r
+#define sk_set_frozen(s, is_frozen) (((*s)->set_frozen) (s, is_frozen))\r
+\r
+/*\r
+ * Call this after an operation that might have tried to send on a\r
+ * socket, to clean up any pending network errors.\r
+ */\r
+void net_pending_errors(void);\r
+\r
+/*\r
+ * Simple wrapper on getservbyname(), needed by ssh.c. Returns the\r
+ * port number, in host byte order (suitable for printf and so on).\r
+ * Returns 0 on failure. Any platform not supporting getservbyname\r
+ * can just return 0 - this function is not required to handle\r
+ * numeric port specifications.\r
+ */\r
+int net_service_lookup(char *service);\r
+\r
+/*\r
+ * Look up the local hostname; return value needs freeing.\r
+ * May return NULL.\r
+ */\r
+char *get_hostname(void);\r
+\r
+/********** SSL stuff **********/\r
+\r
+/*\r
+ * This section is subject to change, but you get the general idea\r
+ * of what it will eventually look like.\r
+ */\r
+\r
+typedef struct certificate *Certificate;\r
+typedef struct our_certificate *Our_Certificate;\r
+    /* to be defined somewhere else, somehow */\r
+\r
+typedef struct ssl_client_socket_function_table **SSL_Client_Socket;\r
+typedef struct ssl_client_plug_function_table **SSL_Client_Plug;\r
+\r
+struct ssl_client_socket_function_table {\r
+    struct socket_function_table base;\r
+    void (*renegotiate) (SSL_Client_Socket s);\r
+    /* renegotiate the cipher spec */\r
+};\r
+\r
+struct ssl_client_plug_function_table {\r
+    struct plug_function_table base;\r
+    int (*refuse_cert) (SSL_Client_Plug p, Certificate cert[]);\r
+    /* do we accept this certificate chain?  If not, why not? */\r
+    /* cert[0] is the server's certificate, cert[] is NULL-terminated */\r
+    /* the last certificate may or may not be the root certificate */\r
+     Our_Certificate(*client_cert) (SSL_Client_Plug p);\r
+    /* the server wants us to identify ourselves */\r
+    /* may return NULL if we want anonymity */\r
+};\r
+\r
+SSL_Client_Socket sk_ssl_client_over(Socket s, /* pre-existing (tcp) connection */\r
+                                    SSL_Client_Plug p);\r
+\r
+#define sk_renegotiate(s) (((*s)->renegotiate) (s))\r
+\r
+#endif\r
diff --git a/putty/NOCPROXY.C b/putty/NOCPROXY.C
new file mode 100644 (file)
index 0000000..ed75def
--- /dev/null
@@ -0,0 +1,36 @@
+/*\r
+ * Routines to refuse to do cryptographic interaction with proxies\r
+ * in PuTTY. This is a stub implementation of the same interfaces\r
+ * provided by cproxy.c, for use in PuTTYtel.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <ctype.h>\r
+#include <string.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+void proxy_socks5_offerencryptedauth(char * command, int * len)\r
+{\r
+    /* For telnet, don't add any new encrypted authentication routines */\r
+}\r
+\r
+int proxy_socks5_handlechap (Proxy_Socket p)\r
+{\r
+\r
+    plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request"\r
+                " in telnet-only build",\r
+                PROXY_ERROR_GENERAL, 0);\r
+    return 1;\r
+}\r
+\r
+int proxy_socks5_selectchap(Proxy_Socket p)\r
+{\r
+    plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request"\r
+                " in telnet-only build",\r
+                PROXY_ERROR_GENERAL, 0);\r
+    return 1;\r
+}\r
diff --git a/putty/NOGSS.C b/putty/NOGSS.C
new file mode 100644 (file)
index 0000000..0f1e254
--- /dev/null
@@ -0,0 +1,11 @@
+/*\r
+ * Stub definitions of the GSSAPI library list, for Unix pterm and\r
+ * any other application that needs the symbols defined but has no\r
+ * use for them.\r
+ */\r
+\r
+#include "putty.h"\r
+\r
+const int ngsslibs = 0;\r
+const char *const gsslibnames[1] = { "dummy" };\r
+const struct keyvalwhere gsslibkeywords[1] = { { "dummy", 0, -1, -1 } };\r
diff --git a/putty/NOPRINT.C b/putty/NOPRINT.C
new file mode 100644 (file)
index 0000000..627d798
--- /dev/null
@@ -0,0 +1,38 @@
+/*\r
+ * Stub implementation of the printing interface for PuTTY, for the\r
+ * benefit of non-printing terminal applications.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+struct printer_job_tag {\r
+    int dummy;\r
+};\r
+\r
+printer_job *printer_start_job(char *printer)\r
+{\r
+    return NULL;\r
+}\r
+\r
+void printer_job_data(printer_job *pj, void *data, int len)\r
+{\r
+}\r
+\r
+void printer_finish_job(printer_job *pj)\r
+{\r
+}\r
+\r
+printer_enum *printer_start_enum(int *nprinters_ptr)\r
+{\r
+    *nprinters_ptr = 0;\r
+    return NULL;\r
+}\r
+char *printer_get_name(printer_enum *pe, int i)\r
+{\r
+    return NULL;\r
+}\r
+void printer_finish_enum(printer_enum *pe)\r
+{\r
+}\r
diff --git a/putty/NOTIMING.C b/putty/NOTIMING.C
new file mode 100644 (file)
index 0000000..aed3e45
--- /dev/null
@@ -0,0 +1,21 @@
+/*\r
+ * notiming.c: stub version of timing API.\r
+ * \r
+ * Used in any tool which needs a subsystem linked against the\r
+ * timing API but doesn't want to actually provide timing. For\r
+ * example, key generation tools need the random number generator,\r
+ * but they don't want the hassle of calling noise_regular() at\r
+ * regular intervals - and they don't _need_ it either, since they\r
+ * have their own rigorous and different means of noise collection.\r
+ */\r
+\r
+#include "putty.h"\r
+\r
+long schedule_timer(int ticks, timer_fn_t fn, void *ctx)\r
+{\r
+    return 0;\r
+}\r
+\r
+void expire_timer_context(void *ctx)\r
+{\r
+}\r
diff --git a/putty/PGSSAPI.C b/putty/PGSSAPI.C
new file mode 100644 (file)
index 0000000..d3c768c
--- /dev/null
@@ -0,0 +1,105 @@
+/* This file actually defines the GSSAPI function pointers for\r
+ * functions we plan to import from a GSSAPI library.\r
+ */\r
+#include "putty.h"\r
+\r
+#ifndef NO_GSSAPI\r
+\r
+#include "pgssapi.h"\r
+\r
+#ifndef NO_LIBDL\r
+\r
+/* Reserved static storage for GSS_oids.  Comments are quotes from RFC 2744. */\r
+static const gss_OID_desc oids[] = {\r
+    /* The implementation must reserve static storage for a\r
+     * gss_OID_desc object containing the value */\r
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"},\r
+    /* corresponding to an object-identifier value of\r
+     * {iso(1) member-body(2) United States(840) mit(113554)\r
+     * infosys(1) gssapi(2) generic(1) user_name(1)}.  The constant\r
+     * GSS_C_NT_USER_NAME should be initialized to point\r
+     * to that gss_OID_desc.\r
+\r
+     * The implementation must reserve static storage for a\r
+     * gss_OID_desc object containing the value */\r
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"},\r
+    /* corresponding to an object-identifier value of\r
+     * {iso(1) member-body(2) United States(840) mit(113554)\r
+     * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.\r
+     * The constant GSS_C_NT_MACHINE_UID_NAME should be\r
+     * initialized to point to that gss_OID_desc.\r
+\r
+     * The implementation must reserve static storage for a\r
+     * gss_OID_desc object containing the value */\r
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"},\r
+    /* corresponding to an object-identifier value of\r
+     * {iso(1) member-body(2) United States(840) mit(113554)\r
+     * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.\r
+     * The constant GSS_C_NT_STRING_UID_NAME should be\r
+     * initialized to point to that gss_OID_desc.\r
+     *\r
+     * The implementation must reserve static storage for a\r
+     * gss_OID_desc object containing the value */\r
+    {6, (void *)"\x2b\x06\x01\x05\x06\x02"},\r
+    /* corresponding to an object-identifier value of\r
+     * {iso(1) org(3) dod(6) internet(1) security(5)\r
+     * nametypes(6) gss-host-based-services(2)).  The constant\r
+     * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point\r
+     * to that gss_OID_desc.  This is a deprecated OID value, and\r
+     * implementations wishing to support hostbased-service names\r
+     * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,\r
+     * defined below, to identify such names;\r
+     * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym\r
+     * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input\r
+     * parameter, but should not be emitted by GSS-API\r
+     * implementations\r
+     *\r
+     * The implementation must reserve static storage for a\r
+     * gss_OID_desc object containing the value */\r
+     {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"},\r
+    /* corresponding to an object-identifier value of {iso(1)\r
+     * member-body(2) Unites States(840) mit(113554) infosys(1)\r
+     * gssapi(2) generic(1) service_name(4)}.  The constant\r
+     * GSS_C_NT_HOSTBASED_SERVICE should be initialized\r
+     * to point to that gss_OID_desc.\r
+     *\r
+     * The implementation must reserve static storage for a\r
+     * gss_OID_desc object containing the value */\r
+    {6, (void *)"\x2b\x06\01\x05\x06\x03"},\r
+    /* corresponding to an object identifier value of\r
+     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),\r
+     * 6(nametypes), 3(gss-anonymous-name)}.  The constant\r
+     * and GSS_C_NT_ANONYMOUS should be initialized to point\r
+     * to that gss_OID_desc.\r
+     *\r
+     * The implementation must reserve static storage for a\r
+     * gss_OID_desc object containing the value */\r
+    {6, (void *)"\x2b\x06\x01\x05\x06\x04"},\r
+     /* corresponding to an object-identifier value of\r
+     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),\r
+     * 6(nametypes), 4(gss-api-exported-name)}.  The constant\r
+     * GSS_C_NT_EXPORT_NAME should be initialized to point\r
+     * to that gss_OID_desc.\r
+     */\r
+};\r
+\r
+/* Here are the constants which point to the static structure above.\r
+ *\r
+ * Constants of the form GSS_C_NT_* are specified by rfc 2744.\r
+ */\r
+const_gss_OID GSS_C_NT_USER_NAME           = oids+0;\r
+const_gss_OID GSS_C_NT_MACHINE_UID_NAME    = oids+1;\r
+const_gss_OID GSS_C_NT_STRING_UID_NAME     = oids+2;\r
+const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3;\r
+const_gss_OID GSS_C_NT_HOSTBASED_SERVICE   = oids+4;\r
+const_gss_OID GSS_C_NT_ANONYMOUS           = oids+5;\r
+const_gss_OID GSS_C_NT_EXPORT_NAME         = oids+6;\r
+\r
+#endif /* NO_LIBDL */\r
+\r
+static gss_OID_desc gss_mech_krb5_desc =\r
+{ 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };\r
+/* iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) krb5(2)*/\r
+const gss_OID GSS_MECH_KRB5 = &gss_mech_krb5_desc;\r
+\r
+#endif /* NO_GSSAPI */\r
diff --git a/putty/PGSSAPI.H b/putty/PGSSAPI.H
new file mode 100644 (file)
index 0000000..4cf9fc4
--- /dev/null
@@ -0,0 +1,296 @@
+#ifndef PUTTY_PGSSAPI_H\r
+#define PUTTY_PGSSAPI_H\r
+\r
+#include "putty.h"\r
+\r
+#ifndef NO_GSSAPI\r
+\r
+/*\r
+ * On Unix, if we're statically linking against GSSAPI, we leave the\r
+ * declaration of all this lot to the official header. If we're\r
+ * dynamically linking, we declare it ourselves, because that avoids\r
+ * us needing the official header at compile time.\r
+ *\r
+ * However, we still need the function pointer types, because even\r
+ * with statically linked GSSAPI we use the ssh_gss_library wrapper.\r
+ */\r
+#ifdef STATIC_GSSAPI\r
+#include <gssapi/gssapi.h>\r
+typedef gss_OID const_gss_OID;        /* for our prototypes below */\r
+#else /* STATIC_GSSAPI */\r
+\r
+/*******************************************************************************\r
+ *  GSSAPI Definitions, taken from RFC 2744\r
+ ******************************************************************************/\r
+\r
+/* GSSAPI Type Definitions */\r
+typedef uint32 OM_uint32;\r
+\r
+typedef struct gss_OID_desc_struct {\r
+    OM_uint32 length;\r
+    void *elements;\r
+} gss_OID_desc;\r
+typedef const gss_OID_desc *const_gss_OID;\r
+typedef gss_OID_desc *gss_OID;\r
+\r
+typedef struct gss_OID_set_desc_struct  {\r
+    size_t  count;\r
+    gss_OID elements;\r
+} gss_OID_set_desc;\r
+typedef const gss_OID_set_desc *const_gss_OID_set;\r
+typedef gss_OID_set_desc *gss_OID_set;\r
+\r
+typedef struct gss_buffer_desc_struct {\r
+    size_t length;\r
+    void *value;\r
+} gss_buffer_desc, *gss_buffer_t;\r
+\r
+typedef struct gss_channel_bindings_struct {\r
+    OM_uint32 initiator_addrtype;\r
+    gss_buffer_desc initiator_address;\r
+    OM_uint32 acceptor_addrtype;\r
+    gss_buffer_desc acceptor_address;\r
+    gss_buffer_desc application_data;\r
+} *gss_channel_bindings_t;\r
+\r
+typedef void * gss_ctx_id_t;\r
+typedef void * gss_name_t;\r
+typedef void * gss_cred_id_t;\r
+\r
+typedef OM_uint32 gss_qop_t;\r
+\r
+/* Flag bits for context-level services. */\r
+\r
+#define GSS_C_DELEG_FLAG      1\r
+#define GSS_C_MUTUAL_FLAG     2\r
+#define GSS_C_REPLAY_FLAG     4\r
+#define GSS_C_SEQUENCE_FLAG   8\r
+#define GSS_C_CONF_FLAG       16\r
+#define GSS_C_INTEG_FLAG      32\r
+#define GSS_C_ANON_FLAG       64\r
+#define GSS_C_PROT_READY_FLAG 128\r
+#define GSS_C_TRANS_FLAG      256\r
+\r
+/* Credential usage options */\r
+#define GSS_C_BOTH     0\r
+#define GSS_C_INITIATE 1\r
+#define GSS_C_ACCEPT   2\r
+\r
+/* Status code types for gss_display_status */\r
+#define GSS_C_GSS_CODE  1\r
+#define GSS_C_MECH_CODE 2\r
+\r
+/* The constant definitions for channel-bindings address families */\r
+#define GSS_C_AF_UNSPEC     0\r
+#define GSS_C_AF_LOCAL      1\r
+#define GSS_C_AF_INET       2\r
+#define GSS_C_AF_IMPLINK    3\r
+#define GSS_C_AF_PUP        4\r
+#define GSS_C_AF_CHAOS      5\r
+#define GSS_C_AF_NS         6\r
+#define GSS_C_AF_NBS        7\r
+#define GSS_C_AF_ECMA       8\r
+#define GSS_C_AF_DATAKIT    9\r
+#define GSS_C_AF_CCITT      10\r
+#define GSS_C_AF_SNA        11\r
+#define GSS_C_AF_DECnet     12\r
+#define GSS_C_AF_DLI        13\r
+#define GSS_C_AF_LAT        14\r
+#define GSS_C_AF_HYLINK     15\r
+#define GSS_C_AF_APPLETALK  16\r
+#define GSS_C_AF_BSC        17\r
+#define GSS_C_AF_DSS        18\r
+#define GSS_C_AF_OSI        19\r
+#define GSS_C_AF_X25        21\r
+\r
+#define GSS_C_AF_NULLADDR   255\r
+\r
+/* Various Null values */\r
+#define GSS_C_NO_NAME ((gss_name_t) 0)\r
+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)\r
+#define GSS_C_NO_OID ((gss_OID) 0)\r
+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)\r
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)\r
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)\r
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)\r
+#define GSS_C_EMPTY_BUFFER {0, NULL}\r
+\r
+/* Major status codes */\r
+#define GSS_S_COMPLETE 0\r
+\r
+/* Some "helper" definitions to make the status code macros obvious. */\r
+#define GSS_C_CALLING_ERROR_OFFSET 24\r
+#define GSS_C_ROUTINE_ERROR_OFFSET 16\r
+\r
+#define GSS_C_SUPPLEMENTARY_OFFSET 0\r
+#define GSS_C_CALLING_ERROR_MASK 0377ul\r
+#define GSS_C_ROUTINE_ERROR_MASK 0377ul\r
+#define GSS_C_SUPPLEMENTARY_MASK 0177777ul\r
+\r
+/*\r
+ * The macros that test status codes for error conditions.\r
+ * Note that the GSS_ERROR() macro has changed slightly from\r
+ * the V1 GSS-API so that it now evaluates its argument\r
+ * only once.\r
+ */\r
+#define GSS_CALLING_ERROR(x)                                            \\r
+    (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))\r
+#define GSS_ROUTINE_ERROR(x)                                            \\r
+    (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))\r
+#define GSS_SUPPLEMENTARY_INFO(x)                                       \\r
+    (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))\r
+#define GSS_ERROR(x)                                                    \\r
+    (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) |    \\r
+          (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))\r
+\r
+/* Now the actual status code definitions */\r
+\r
+/* Calling errors: */\r
+#define GSS_S_CALL_INACCESSIBLE_READ            \\r
+    (1ul << GSS_C_CALLING_ERROR_OFFSET)\r
+#define GSS_S_CALL_INACCESSIBLE_WRITE           \\r
+    (2ul << GSS_C_CALLING_ERROR_OFFSET)\r
+#define GSS_S_CALL_BAD_STRUCTURE                \\r
+    (3ul << GSS_C_CALLING_ERROR_OFFSET)\r
+\r
+/* Routine errors: */\r
+#define GSS_S_BAD_MECH             (1ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_BAD_NAME             (2ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_BAD_NAMETYPE         (3ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_BAD_BINDINGS         (4ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_BAD_STATUS           (5ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_BAD_SIG              (6ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_BAD_MIC GSS_S_BAD_SIG\r
+#define GSS_S_NO_CRED              (7ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_NO_CONTEXT           (8ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_DEFECTIVE_TOKEN      (9ul <<                      \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_DEFECTIVE_CREDENTIAL (10ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_CREDENTIALS_EXPIRED  (11ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_CONTEXT_EXPIRED      (12ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_FAILURE              (13ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_BAD_QOP              (14ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_UNAUTHORIZED         (15ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_UNAVAILABLE          (16ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_DUPLICATE_ELEMENT    (17ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+#define GSS_S_NAME_NOT_MN          (18ul <<                     \\r
+                                    GSS_C_ROUTINE_ERROR_OFFSET)\r
+\r
+/* Supplementary info bits: */\r
+#define GSS_S_CONTINUE_NEEDED                                           \\r
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))\r
+#define GSS_S_DUPLICATE_TOKEN                                           \\r
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))\r
+#define GSS_S_OLD_TOKEN                                                 \\r
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))\r
+#define GSS_S_UNSEQ_TOKEN                                               \\r
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))\r
+#define GSS_S_GAP_TOKEN                                                 \\r
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))\r
+\r
+extern const_gss_OID GSS_C_NT_USER_NAME;\r
+extern const_gss_OID GSS_C_NT_MACHINE_UID_NAME;\r
+extern const_gss_OID GSS_C_NT_STRING_UID_NAME;\r
+extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;\r
+extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE;\r
+extern const_gss_OID GSS_C_NT_ANONYMOUS;\r
+extern const_gss_OID GSS_C_NT_EXPORT_NAME;\r
+\r
+#endif /* STATIC_GSSAPI */\r
+\r
+extern const gss_OID GSS_MECH_KRB5;\r
+\r
+/* GSSAPI functions we use.\r
+ * TODO: Replace with all GSSAPI functions from RFC?\r
+ */\r
+\r
+/* Calling convention, just in case we need one. */\r
+#ifndef GSS_CC\r
+#define GSS_CC\r
+#endif /*GSS_CC*/\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_release_cred)\r
+            (OM_uint32                    * /*minor_status*/,\r
+             gss_cred_id_t                * /*cred_handle*/);\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_init_sec_context)\r
+            (OM_uint32                    * /*minor_status*/,\r
+             const gss_cred_id_t            /*initiator_cred_handle*/,\r
+             gss_ctx_id_t                 * /*context_handle*/,\r
+             const gss_name_t               /*target_name*/,\r
+             const gss_OID                  /*mech_type*/,\r
+             OM_uint32                      /*req_flags*/,\r
+             OM_uint32                      /*time_req*/,\r
+             const gss_channel_bindings_t   /*input_chan_bindings*/,\r
+             const gss_buffer_t             /*input_token*/,\r
+             gss_OID                      * /*actual_mech_type*/,\r
+             gss_buffer_t                   /*output_token*/,\r
+             OM_uint32                    * /*ret_flags*/,\r
+             OM_uint32                    * /*time_rec*/);\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_delete_sec_context)\r
+            (OM_uint32                    * /*minor_status*/,\r
+             gss_ctx_id_t                 * /*context_handle*/,\r
+             gss_buffer_t                   /*output_token*/);\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_get_mic)\r
+            (OM_uint32                    * /*minor_status*/,\r
+             const gss_ctx_id_t             /*context_handle*/,\r
+             gss_qop_t                      /*qop_req*/,\r
+             const gss_buffer_t             /*message_buffer*/,\r
+             gss_buffer_t                   /*msg_token*/);\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_display_status)\r
+            (OM_uint32                   * /*minor_status*/,\r
+             OM_uint32                     /*status_value*/,\r
+             int                           /*status_type*/,\r
+             const gss_OID                 /*mech_type*/,\r
+             OM_uint32                   * /*message_context*/,\r
+             gss_buffer_t                  /*status_string*/);\r
+\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_import_name)\r
+            (OM_uint32                   * /*minor_status*/,\r
+             const gss_buffer_t            /*input_name_buffer*/,\r
+             const_gss_OID                 /*input_name_type*/,\r
+             gss_name_t                  * /*output_name*/);\r
+\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_release_name)\r
+            (OM_uint32                   * /*minor_status*/,\r
+             gss_name_t                  * /*name*/);\r
+\r
+typedef OM_uint32 (GSS_CC *t_gss_release_buffer)\r
+            (OM_uint32                   * /*minor_status*/,\r
+             gss_buffer_t                  /*buffer*/);\r
+\r
+struct gssapi_functions {\r
+    t_gss_delete_sec_context delete_sec_context;\r
+    t_gss_display_status display_status;\r
+    t_gss_get_mic get_mic;\r
+    t_gss_import_name import_name;\r
+    t_gss_init_sec_context init_sec_context;\r
+    t_gss_release_buffer release_buffer;\r
+    t_gss_release_cred release_cred;\r
+    t_gss_release_name release_name;\r
+};\r
+\r
+#endif /* NO_GSSAPI */\r
+\r
+#endif /* PUTTY_PGSSAPI_H */\r
diff --git a/putty/PINGER.C b/putty/PINGER.C
new file mode 100644 (file)
index 0000000..b6fde24
--- /dev/null
@@ -0,0 +1,71 @@
+/*\r
+ * pinger.c: centralised module that deals with sending TS_PING\r
+ * keepalives, to avoid replicating this code in multiple backends.\r
+ */\r
+\r
+#include "putty.h"\r
+\r
+struct pinger_tag {\r
+    int interval;\r
+    int pending;\r
+    long next;\r
+    Backend *back;\r
+    void *backhandle;\r
+};\r
+\r
+static void pinger_schedule(Pinger pinger);\r
+\r
+static void pinger_timer(void *ctx, long now)\r
+{\r
+    Pinger pinger = (Pinger)ctx;\r
+\r
+    if (pinger->pending && now - pinger->next >= 0) {\r
+       pinger->back->special(pinger->backhandle, TS_PING);\r
+       pinger->pending = FALSE;\r
+       pinger_schedule(pinger);\r
+    }\r
+}\r
+\r
+static void pinger_schedule(Pinger pinger)\r
+{\r
+    int next;\r
+\r
+    if (!pinger->interval) {\r
+       pinger->pending = FALSE;       /* cancel any pending ping */\r
+       return;\r
+    }\r
+\r
+    next = schedule_timer(pinger->interval * TICKSPERSEC,\r
+                         pinger_timer, pinger);\r
+    if (!pinger->pending || next < pinger->next) {\r
+       pinger->next = next;\r
+       pinger->pending = TRUE;\r
+    }\r
+}\r
+\r
+Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)\r
+{\r
+    Pinger pinger = snew(struct pinger_tag);\r
+\r
+    pinger->interval = cfg->ping_interval;\r
+    pinger->pending = FALSE;\r
+    pinger->back = back;\r
+    pinger->backhandle = backhandle;\r
+    pinger_schedule(pinger);\r
+\r
+    return pinger;\r
+}\r
+\r
+void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg)\r
+{\r
+    if (oldcfg->ping_interval != newcfg->ping_interval) {\r
+       pinger->interval = newcfg->ping_interval;\r
+       pinger_schedule(pinger);\r
+    }\r
+}\r
+\r
+void pinger_free(Pinger pinger)\r
+{\r
+    expire_timer_context(pinger);\r
+    sfree(pinger);\r
+}\r
diff --git a/putty/PORTFWD.C b/putty/PORTFWD.C
new file mode 100644 (file)
index 0000000..e5874a6
--- /dev/null
@@ -0,0 +1,556 @@
+/*\r
+ * SSH port forwarding.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+struct PFwdPrivate {\r
+    const struct plug_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+    void *c;                          /* (channel) data used by ssh.c */\r
+    void *backhandle;                 /* instance of SSH backend itself */\r
+    /* Note that backhandle need not be filled in if c is non-NULL */\r
+    Socket s;\r
+    int throttled, throttle_override;\r
+    int ready;\r
+    /*\r
+     * `dynamic' does double duty. It's set to 0 for an ordinary\r
+     * forwarded port, and nonzero for SOCKS-style dynamic port\r
+     * forwarding; but it also represents the state of the SOCKS\r
+     * exchange.\r
+     */\r
+    int dynamic;\r
+    /*\r
+     * `hostname' and `port' are the real hostname and port, once\r
+     * we know what we're connecting to; they're unused for this\r
+     * purpose while conducting a local SOCKS exchange, which means\r
+     * we can also use them as a buffer and pointer for reading\r
+     * data from the SOCKS client.\r
+     */\r
+    char hostname[256+8];\r
+    int port;\r
+    /*\r
+     * When doing dynamic port forwarding, we can receive\r
+     * connection data before we are actually able to send it; so\r
+     * we may have to temporarily hold some in a dynamically\r
+     * allocated buffer here.\r
+     */\r
+    void *buffer;\r
+    int buflen;\r
+};\r
+\r
+static void pfd_log(Plug plug, int type, SockAddr addr, int port,\r
+                   const char *error_msg, int error_code)\r
+{\r
+    /* we have to dump these since we have no interface to logging.c */\r
+}\r
+\r
+static int pfd_closing(Plug plug, const char *error_msg, int error_code,\r
+                      int calling_back)\r
+{\r
+    struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
+\r
+    /*\r
+     * We have no way to communicate down the forwarded connection,\r
+     * so if an error occurred on the socket, we just ignore it\r
+     * and treat it like a proper close.\r
+     */\r
+    if (pr->c)\r
+       sshfwd_close(pr->c);\r
+    pfd_close(pr->s);\r
+    return 1;\r
+}\r
+\r
+static int pfd_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
+    if (pr->dynamic) {\r
+       while (len--) {\r
+           /*\r
+            * Throughout SOCKS negotiation, "hostname" is re-used as a\r
+            * random protocol buffer with "port" storing the length.\r
+            */ \r
+           if (pr->port >= lenof(pr->hostname)) {\r
+               /* Request too long. */\r
+               if ((pr->dynamic >> 12) == 4) {\r
+                   /* Send back a SOCKS 4 error before closing. */\r
+                   char data[8];\r
+                   memset(data, 0, sizeof(data));\r
+                   data[1] = 91;      /* generic `request rejected' */\r
+                   sk_write(pr->s, data, 8);\r
+               }\r
+               pfd_close(pr->s);\r
+               return 1;\r
+           }\r
+           pr->hostname[pr->port++] = *data++;\r
+\r
+           /*\r
+            * Now check what's in the buffer to see if it's a\r
+            * valid and complete message in the SOCKS exchange.\r
+            */\r
+           if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) &&\r
+               pr->hostname[0] == 4) {\r
+               /*\r
+                * SOCKS 4.\r
+                */\r
+               if (pr->dynamic == 1)\r
+                   pr->dynamic = 0x4000;\r
+               if (pr->port < 2) continue;/* don't have command code yet */\r
+               if (pr->hostname[1] != 1) {\r
+                   /* Not CONNECT. */\r
+                   /* Send back a SOCKS 4 error before closing. */\r
+                   char data[8];\r
+                   memset(data, 0, sizeof(data));\r
+                   data[1] = 91;      /* generic `request rejected' */\r
+                   sk_write(pr->s, data, 8);\r
+                   pfd_close(pr->s);\r
+                   return 1;\r
+               }\r
+               if (pr->port <= 8) continue; /* haven't started user/hostname */\r
+               if (pr->hostname[pr->port-1] != 0)\r
+                   continue;          /* haven't _finished_ user/hostname */\r
+               /*\r
+                * Now we have a full SOCKS 4 request. Check it to\r
+                * see if it's a SOCKS 4A request.\r
+                */\r
+               if (pr->hostname[4] == 0 && pr->hostname[5] == 0 &&\r
+                   pr->hostname[6] == 0 && pr->hostname[7] != 0) {\r
+                   /*\r
+                    * It's SOCKS 4A. So if we haven't yet\r
+                    * collected the host name, we should continue\r
+                    * waiting for data in order to do so; if we\r
+                    * have, we can go ahead.\r
+                    */\r
+                   int len;\r
+                   if (pr->dynamic == 0x4000) {\r
+                       pr->dynamic = 0x4001;\r
+                       pr->port = 8;      /* reset buffer to overwrite name */\r
+                       continue;\r
+                   }\r
+                   pr->hostname[0] = 0;   /* reply version code */\r
+                   pr->hostname[1] = 90;   /* request granted */\r
+                   sk_write(pr->s, pr->hostname, 8);\r
+                   len= pr->port - 8;\r
+                   pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2);\r
+                   memmove(pr->hostname, pr->hostname + 8, len);\r
+                   goto connect;\r
+               } else {\r
+                   /*\r
+                    * It's SOCKS 4, which means we should format\r
+                    * the IP address into the hostname string and\r
+                    * then just go.\r
+                    */\r
+                   pr->hostname[0] = 0;   /* reply version code */\r
+                   pr->hostname[1] = 90;   /* request granted */\r
+                   sk_write(pr->s, pr->hostname, 8);\r
+                   pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2);\r
+                   sprintf(pr->hostname, "%d.%d.%d.%d",\r
+                           (unsigned char)pr->hostname[4],\r
+                           (unsigned char)pr->hostname[5],\r
+                           (unsigned char)pr->hostname[6],\r
+                           (unsigned char)pr->hostname[7]);\r
+                   goto connect;\r
+               }\r
+           }\r
+\r
+           if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) &&\r
+               pr->hostname[0] == 5) {\r
+               /*\r
+                * SOCKS 5.\r
+                */\r
+               if (pr->dynamic == 1)\r
+                   pr->dynamic = 0x5000;\r
+\r
+               if (pr->dynamic == 0x5000) {\r
+                   int i, method;\r
+                   char data[2];\r
+                   /*\r
+                    * We're receiving a set of method identifiers.\r
+                    */\r
+                   if (pr->port < 2) continue;/* no method count yet */\r
+                   if (pr->port < 2 + (unsigned char)pr->hostname[1])\r
+                       continue;      /* no methods yet */\r
+                   method = 0xFF;     /* invalid */\r
+                   for (i = 0; i < (unsigned char)pr->hostname[1]; i++)\r
+                       if (pr->hostname[2+i] == 0) {\r
+                           method = 0;/* no auth */\r
+                           break;\r
+                       }\r
+                   data[0] = 5;\r
+                   data[1] = method;\r
+                   sk_write(pr->s, data, 2);\r
+                   pr->dynamic = 0x5001;\r
+                   pr->port = 0;      /* re-empty the buffer */\r
+                   continue;\r
+               }\r
+\r
+               if (pr->dynamic == 0x5001) {\r
+                   /*\r
+                    * We're receiving a SOCKS request.\r
+                    */\r
+                   unsigned char reply[10]; /* SOCKS5 atyp=1 reply */\r
+                   int atype, alen = 0;\r
+\r
+                   /*\r
+                    * Pre-fill reply packet.\r
+                    * In all cases, we set BND.{HOST,ADDR} to 0.0.0.0:0\r
+                    * (atyp=1) in the reply; if we succeed, we don't know\r
+                    * the right answers, and if we fail, they should be\r
+                    * ignored.\r
+                    */\r
+                   memset(reply, 0, lenof(reply));\r
+                   reply[0] = 5; /* VER */\r
+                   reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */\r
+\r
+                   if (pr->port < 6) continue;\r
+                   atype = (unsigned char)pr->hostname[3];\r
+                   if (atype == 1)    /* IPv4 address */\r
+                       alen = 4;\r
+                   if (atype == 4)    /* IPv6 address */\r
+                       alen = 16;\r
+                   if (atype == 3)    /* domain name has leading length */\r
+                       alen = 1 + (unsigned char)pr->hostname[4];\r
+                   if (pr->port < 6 + alen) continue;\r
+                   if (pr->hostname[1] != 1 || pr->hostname[2] != 0) {\r
+                       /* Not CONNECT or reserved field nonzero - error */\r
+                       reply[1] = 1;   /* generic failure */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       pfd_close(pr->s);\r
+                       return 1;\r
+                   }\r
+                   /*\r
+                    * Now we have a viable connect request. Switch\r
+                    * on atype.\r
+                    */\r
+                   pr->port = GET_16BIT_MSB_FIRST(pr->hostname+4+alen);\r
+                   if (atype == 1) {\r
+                       /* REP=0 (success) already */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       sprintf(pr->hostname, "%d.%d.%d.%d",\r
+                               (unsigned char)pr->hostname[4],\r
+                               (unsigned char)pr->hostname[5],\r
+                               (unsigned char)pr->hostname[6],\r
+                               (unsigned char)pr->hostname[7]);\r
+                       goto connect;\r
+                   } else if (atype == 3) {\r
+                       /* REP=0 (success) already */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       memmove(pr->hostname, pr->hostname + 5, alen-1);\r
+                       pr->hostname[alen-1] = '\0';\r
+                       goto connect;\r
+                   } else {\r
+                       /*\r
+                        * Unknown address type. (FIXME: support IPv6!)\r
+                        */\r
+                       reply[1] = 8;   /* atype not supported */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       pfd_close(pr->s);\r
+                       return 1;\r
+                   }\r
+               }\r
+           }\r
+\r
+           /*\r
+            * If we get here without either having done `continue'\r
+            * or `goto connect', it must be because there is no\r
+            * sensible interpretation of what's in our buffer. So\r
+            * close the connection rudely.\r
+            */\r
+           pfd_close(pr->s);\r
+           return 1;\r
+       }\r
+       return 1;\r
+\r
+       /*\r
+        * We come here when we're ready to make an actual\r
+        * connection.\r
+        */\r
+       connect:\r
+\r
+       /*\r
+        * Freeze the socket until the SSH server confirms the\r
+        * connection.\r
+        */\r
+       sk_set_frozen(pr->s, 1);\r
+\r
+       pr->c = new_sock_channel(pr->backhandle, pr->s);\r
+       if (pr->c == NULL) {\r
+           pfd_close(pr->s);\r
+           return 1;\r
+       } else {\r
+           /* asks to forward to the specified host/port for this */\r
+           ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");\r
+       }\r
+       pr->dynamic = 0;\r
+\r
+       /*\r
+        * If there's any data remaining in our current buffer,\r
+        * save it to be sent on pfd_confirm().\r
+        */\r
+       if (len > 0) {\r
+           pr->buffer = snewn(len, char);\r
+           memcpy(pr->buffer, data, len);\r
+           pr->buflen = len;\r
+       }\r
+    }\r
+    if (pr->ready) {\r
+       if (sshfwd_write(pr->c, data, len) > 0) {\r
+           pr->throttled = 1;\r
+           sk_set_frozen(pr->s, 1);\r
+       }\r
+    }\r
+    return 1;\r
+}\r
+\r
+static void pfd_sent(Plug plug, int bufsize)\r
+{\r
+    struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
+\r
+    if (pr->c)\r
+       sshfwd_unthrottle(pr->c, bufsize);\r
+}\r
+\r
+/*\r
+ * Called when receiving a PORT OPEN from the server\r
+ */\r
+const char *pfd_newconnect(Socket *s, char *hostname, int port,\r
+                          void *c, const Config *cfg, int addressfamily)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       pfd_log,\r
+       pfd_closing,\r
+       pfd_receive,\r
+       pfd_sent,\r
+       NULL\r
+    };\r
+\r
+    SockAddr addr;\r
+    const char *err;\r
+    char *dummy_realhost;\r
+    struct PFwdPrivate *pr;\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    pr = snew(struct PFwdPrivate);\r
+    pr->buffer = NULL;\r
+    pr->fn = &fn_table;\r
+    pr->throttled = pr->throttle_override = 0;\r
+    pr->ready = 1;\r
+    pr->c = c;\r
+    pr->backhandle = NULL;            /* we shouldn't need this */\r
+    pr->dynamic = 0;\r
+\r
+    pr->s = *s = new_connection(addr, dummy_realhost, port,\r
+                               0, 1, 0, 0, (Plug) pr, cfg);\r
+    if ((err = sk_socket_error(*s)) != NULL) {\r
+       sfree(pr);\r
+       return err;\r
+    }\r
+\r
+    sk_set_private_ptr(*s, pr);\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ called when someone connects to the local port\r
+ */\r
+\r
+static int pfd_accepting(Plug p, OSSocket sock)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       pfd_log,\r
+       pfd_closing,\r
+       pfd_receive,\r
+       pfd_sent,\r
+       NULL\r
+    };\r
+    struct PFwdPrivate *pr, *org;\r
+    Socket s;\r
+    const char *err;\r
+\r
+    org = (struct PFwdPrivate *)p;\r
+    pr = snew(struct PFwdPrivate);\r
+    pr->buffer = NULL;\r
+    pr->fn = &fn_table;\r
+\r
+    pr->c = NULL;\r
+    pr->backhandle = org->backhandle;\r
+\r
+    pr->s = s = sk_register(sock, (Plug) pr);\r
+    if ((err = sk_socket_error(s)) != NULL) {\r
+       sfree(pr);\r
+       return err != NULL;\r
+    }\r
+\r
+    sk_set_private_ptr(s, pr);\r
+\r
+    pr->throttled = pr->throttle_override = 0;\r
+    pr->ready = 0;\r
+\r
+    if (org->dynamic) {\r
+       pr->dynamic = 1;\r
+       pr->port = 0;                  /* "hostname" buffer is so far empty */\r
+       sk_set_frozen(s, 0);           /* we want to receive SOCKS _now_! */\r
+    } else {\r
+       pr->dynamic = 0;\r
+       strcpy(pr->hostname, org->hostname);\r
+       pr->port = org->port;   \r
+       pr->c = new_sock_channel(org->backhandle, s);\r
+\r
+       if (pr->c == NULL) {\r
+           sfree(pr);\r
+           return 1;\r
+       } else {\r
+           /* asks to forward to the specified host/port for this */\r
+           ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");\r
+       }\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+\r
+/* Add a new forwarding from port -> desthost:destport\r
+ sets up a listener on the local machine on (srcaddr:)port\r
+ */\r
+const char *pfd_addforward(char *desthost, int destport, char *srcaddr,\r
+                          int port, void *backhandle, const Config *cfg,\r
+                          void **sockdata, int address_family)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       pfd_log,\r
+       pfd_closing,\r
+       pfd_receive,                   /* should not happen... */\r
+       pfd_sent,                      /* also should not happen */\r
+       pfd_accepting\r
+    };\r
+\r
+    const char *err;\r
+    struct PFwdPrivate *pr;\r
+    Socket s;\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    pr = snew(struct PFwdPrivate);\r
+    pr->buffer = NULL;\r
+    pr->fn = &fn_table;\r
+    pr->c = NULL;\r
+    if (desthost) {\r
+       strcpy(pr->hostname, desthost);\r
+       pr->port = destport;\r
+       pr->dynamic = 0;\r
+    } else\r
+       pr->dynamic = 1;\r
+    pr->throttled = pr->throttle_override = 0;\r
+    pr->ready = 0;\r
+    pr->backhandle = backhandle;\r
+\r
+    pr->s = s = new_listener(srcaddr, port, (Plug) pr,\r
+                            !cfg->lport_acceptall, cfg, address_family);\r
+    if ((err = sk_socket_error(s)) != NULL) {\r
+       sfree(pr);\r
+       return err;\r
+    }\r
+\r
+    sk_set_private_ptr(s, pr);\r
+\r
+    *sockdata = (void *)s;\r
+\r
+    return NULL;\r
+}\r
+\r
+void pfd_close(Socket s)\r
+{\r
+    struct PFwdPrivate *pr;\r
+\r
+    if (!s)\r
+       return;\r
+\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+\r
+    sfree(pr->buffer);\r
+    sfree(pr);\r
+\r
+    sk_close(s);\r
+}\r
+\r
+/*\r
+ * Terminate a listener.\r
+ */\r
+void pfd_terminate(void *sv)\r
+{\r
+    pfd_close((Socket)sv);\r
+}\r
+\r
+void pfd_unthrottle(Socket s)\r
+{\r
+    struct PFwdPrivate *pr;\r
+    if (!s)\r
+       return;\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+\r
+    pr->throttled = 0;\r
+    sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
+}\r
+\r
+void pfd_override_throttle(Socket s, int enable)\r
+{\r
+    struct PFwdPrivate *pr;\r
+    if (!s)\r
+       return;\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+\r
+    pr->throttle_override = enable;\r
+    sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
+}\r
+\r
+/*\r
+ * Called to send data down the raw connection.\r
+ */\r
+int pfd_send(Socket s, char *data, int len)\r
+{\r
+    if (s == NULL)\r
+       return 0;\r
+    return sk_write(s, data, len);\r
+}\r
+\r
+\r
+void pfd_confirm(Socket s)\r
+{\r
+    struct PFwdPrivate *pr;\r
+\r
+    if (s == NULL)\r
+       return;\r
+\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+    pr->ready = 1;\r
+    sk_set_frozen(s, 0);\r
+    sk_write(s, NULL, 0);\r
+    if (pr->buffer) {\r
+       sshfwd_write(pr->c, pr->buffer, pr->buflen);\r
+       sfree(pr->buffer);\r
+       pr->buffer = NULL;\r
+    }\r
+}\r
diff --git a/putty/PPROXY.C b/putty/PPROXY.C
new file mode 100644 (file)
index 0000000..5ab31b2
--- /dev/null
@@ -0,0 +1,17 @@
+/*\r
+ * pproxy.c: dummy implementation of platform_new_connection(), to\r
+ * be supplanted on any platform which has its own local proxy\r
+ * method.\r
+ */\r
+\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+Socket platform_new_connection(SockAddr addr, char *hostname,\r
+                              int port, int privport,\r
+                              int oobinline, int nodelay, int keepalive,\r
+                              Plug plug, const Config *cfg)\r
+{\r
+    return NULL;\r
+}\r
diff --git a/putty/PROXY.C b/putty/PROXY.C
new file mode 100644 (file)
index 0000000..1f42999
--- /dev/null
@@ -0,0 +1,1478 @@
+/*\r
+ * Network proxy abstraction in PuTTY\r
+ *\r
+ * A proxy layer, if necessary, wedges itself between the network\r
+ * code and the higher level backend.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <ctype.h>\r
+#include <string.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+#define do_proxy_dns(cfg) \\r
+    (cfg->proxy_dns == FORCE_ON || \\r
+        (cfg->proxy_dns == AUTO && cfg->proxy_type != PROXY_SOCKS4))\r
+\r
+/*\r
+ * Call this when proxy negotiation is complete, so that this\r
+ * socket can begin working normally.\r
+ */\r
+void proxy_activate (Proxy_Socket p)\r
+{\r
+    void *data;\r
+    int len;\r
+    long output_before, output_after;\r
+    \r
+    p->state = PROXY_STATE_ACTIVE;\r
+\r
+    /* we want to ignore new receive events until we have sent\r
+     * all of our buffered receive data.\r
+     */\r
+    sk_set_frozen(p->sub_socket, 1);\r
+\r
+    /* how many bytes of output have we buffered? */\r
+    output_before = bufchain_size(&p->pending_oob_output_data) +\r
+       bufchain_size(&p->pending_output_data);\r
+    /* and keep track of how many bytes do not get sent. */\r
+    output_after = 0;\r
+    \r
+    /* send buffered OOB writes */\r
+    while (bufchain_size(&p->pending_oob_output_data) > 0) {\r
+       bufchain_prefix(&p->pending_oob_output_data, &data, &len);\r
+       output_after += sk_write_oob(p->sub_socket, data, len);\r
+       bufchain_consume(&p->pending_oob_output_data, len);\r
+    }\r
+\r
+    /* send buffered normal writes */\r
+    while (bufchain_size(&p->pending_output_data) > 0) {\r
+       bufchain_prefix(&p->pending_output_data, &data, &len);\r
+       output_after += sk_write(p->sub_socket, data, len);\r
+       bufchain_consume(&p->pending_output_data, len);\r
+    }\r
+\r
+    /* if we managed to send any data, let the higher levels know. */\r
+    if (output_after < output_before)\r
+       plug_sent(p->plug, output_after);\r
+\r
+    /* if we were asked to flush the output during\r
+     * the proxy negotiation process, do so now.\r
+     */\r
+    if (p->pending_flush) sk_flush(p->sub_socket);\r
+\r
+    /* if the backend wanted the socket unfrozen, try to unfreeze.\r
+     * our set_frozen handler will flush buffered receive data before\r
+     * unfreezing the actual underlying socket.\r
+     */\r
+    if (!p->freeze)\r
+       sk_set_frozen((Socket)p, 0);\r
+}\r
+\r
+/* basic proxy socket functions */\r
+\r
+static Plug sk_proxy_plug (Socket s, Plug p)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    Plug ret = ps->plug;\r
+    if (p)\r
+       ps->plug = p;\r
+    return ret;\r
+}\r
+\r
+static void sk_proxy_close (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    sk_close(ps->sub_socket);\r
+    sk_addr_free(ps->remote_addr);\r
+    sfree(ps);\r
+}\r
+\r
+static int sk_proxy_write (Socket s, const char *data, int len)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       bufchain_add(&ps->pending_output_data, data, len);\r
+       return bufchain_size(&ps->pending_output_data);\r
+    }\r
+    return sk_write(ps->sub_socket, data, len);\r
+}\r
+\r
+static int sk_proxy_write_oob (Socket s, const char *data, int len)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       bufchain_clear(&ps->pending_output_data);\r
+       bufchain_clear(&ps->pending_oob_output_data);\r
+       bufchain_add(&ps->pending_oob_output_data, data, len);\r
+       return len;\r
+    }\r
+    return sk_write_oob(ps->sub_socket, data, len);\r
+}\r
+\r
+static void sk_proxy_flush (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->pending_flush = 1;\r
+       return;\r
+    }\r
+    sk_flush(ps->sub_socket);\r
+}\r
+\r
+static void sk_proxy_set_private_ptr (Socket s, void *ptr)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    sk_set_private_ptr(ps->sub_socket, ptr);\r
+}\r
+\r
+static void * sk_proxy_get_private_ptr (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    return sk_get_private_ptr(ps->sub_socket);\r
+}\r
+\r
+static void sk_proxy_set_frozen (Socket s, int is_frozen)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->freeze = is_frozen;\r
+       return;\r
+    }\r
+    \r
+    /* handle any remaining buffered recv data first */\r
+    if (bufchain_size(&ps->pending_input_data) > 0) {\r
+       ps->freeze = is_frozen;\r
+\r
+       /* loop while we still have buffered data, and while we are\r
+        * unfrozen. the plug_receive call in the loop could result \r
+        * in a call back into this function refreezing the socket, \r
+        * so we have to check each time.\r
+        */\r
+        while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) {\r
+           void *data;\r
+           char databuf[512];\r
+           int len;\r
+           bufchain_prefix(&ps->pending_input_data, &data, &len);\r
+           if (len > lenof(databuf))\r
+               len = lenof(databuf);\r
+           memcpy(databuf, data, len);\r
+           bufchain_consume(&ps->pending_input_data, len);\r
+           plug_receive(ps->plug, 0, databuf, len);\r
+       }\r
+\r
+       /* if we're still frozen, we'll have to wait for another\r
+        * call from the backend to finish unbuffering the data.\r
+        */\r
+       if (ps->freeze) return;\r
+    }\r
+    \r
+    sk_set_frozen(ps->sub_socket, is_frozen);\r
+}\r
+\r
+static const char * sk_proxy_socket_error (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    if (ps->error != NULL || ps->sub_socket == NULL) {\r
+       return ps->error;\r
+    }\r
+    return sk_socket_error(ps->sub_socket);\r
+}\r
+\r
+/* basic proxy plug functions */\r
+\r
+static void plug_proxy_log(Plug plug, int type, SockAddr addr, int port,\r
+                          const char *error_msg, int error_code)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) plug;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    plug_log(ps->plug, type, addr, port, error_msg, error_code);\r
+}\r
+\r
+static int plug_proxy_closing (Plug p, const char *error_msg,\r
+                              int error_code, int calling_back)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->closing_error_msg = error_msg;\r
+       ps->closing_error_code = error_code;\r
+       ps->closing_calling_back = calling_back;\r
+       return ps->negotiate(ps, PROXY_CHANGE_CLOSING);\r
+    }\r
+    return plug_closing(ps->plug, error_msg,\r
+                       error_code, calling_back);\r
+}\r
+\r
+static int plug_proxy_receive (Plug p, int urgent, char *data, int len)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       /* we will lose the urgentness of this data, but since most,\r
+        * if not all, of this data will be consumed by the negotiation\r
+        * process, hopefully it won't affect the protocol above us\r
+        */\r
+       bufchain_add(&ps->pending_input_data, data, len);\r
+       ps->receive_urgent = urgent;\r
+       ps->receive_data = data;\r
+       ps->receive_len = len;\r
+       return ps->negotiate(ps, PROXY_CHANGE_RECEIVE);\r
+    }\r
+    return plug_receive(ps->plug, urgent, data, len);\r
+}\r
+\r
+static void plug_proxy_sent (Plug p, int bufsize)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->sent_bufsize = bufsize;\r
+       ps->negotiate(ps, PROXY_CHANGE_SENT);\r
+       return;\r
+    }\r
+    plug_sent(ps->plug, bufsize);\r
+}\r
+\r
+static int plug_proxy_accepting (Plug p, OSSocket sock)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->accepting_sock = sock;\r
+       return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);\r
+    }\r
+    return plug_accepting(ps->plug, sock);\r
+}\r
+\r
+/*\r
+ * This function can accept a NULL pointer as `addr', in which case\r
+ * it will only check the host name.\r
+ */\r
+static int proxy_for_destination (SockAddr addr, char *hostname, int port,\r
+                                 const Config *cfg)\r
+{\r
+    int s = 0, e = 0;\r
+    char hostip[64];\r
+    int hostip_len, hostname_len;\r
+    const char *exclude_list;\r
+\r
+    /*\r
+     * Check the host name and IP against the hard-coded\r
+     * representations of `localhost'.\r
+     */\r
+    if (!cfg->even_proxy_localhost &&\r
+       (sk_hostname_is_local(hostname) ||\r
+        (addr && sk_address_is_local(addr))))\r
+       return 0;                      /* do not proxy */\r
+\r
+    /* we want a string representation of the IP address for comparisons */\r
+    if (addr) {\r
+       sk_getaddr(addr, hostip, 64);\r
+       hostip_len = strlen(hostip);\r
+    } else\r
+       hostip_len = 0;                /* placate gcc; shouldn't be required */\r
+\r
+    hostname_len = strlen(hostname);\r
+\r
+    exclude_list = cfg->proxy_exclude_list;\r
+\r
+    /* now parse the exclude list, and see if either our IP\r
+     * or hostname matches anything in it.\r
+     */\r
+\r
+    while (exclude_list[s]) {\r
+       while (exclude_list[s] &&\r
+              (isspace((unsigned char)exclude_list[s]) ||\r
+               exclude_list[s] == ',')) s++;\r
+\r
+       if (!exclude_list[s]) break;\r
+\r
+       e = s;\r
+\r
+       while (exclude_list[e] &&\r
+              (isalnum((unsigned char)exclude_list[e]) ||\r
+               exclude_list[e] == '-' ||\r
+               exclude_list[e] == '.' ||\r
+               exclude_list[e] == '*')) e++;\r
+\r
+       if (exclude_list[s] == '*') {\r
+           /* wildcard at beginning of entry */\r
+\r
+           if ((addr && strnicmp(hostip + hostip_len - (e - s - 1),\r
+                                 exclude_list + s + 1, e - s - 1) == 0) ||\r
+               strnicmp(hostname + hostname_len - (e - s - 1),\r
+                        exclude_list + s + 1, e - s - 1) == 0)\r
+               return 0; /* IP/hostname range excluded. do not use proxy. */\r
+\r
+       } else if (exclude_list[e-1] == '*') {\r
+           /* wildcard at end of entry */\r
+\r
+           if ((addr && strnicmp(hostip, exclude_list + s, e - s - 1) == 0) ||\r
+               strnicmp(hostname, exclude_list + s, e - s - 1) == 0)\r
+               return 0; /* IP/hostname range excluded. do not use proxy. */\r
+\r
+       } else {\r
+           /* no wildcard at either end, so let's try an absolute\r
+            * match (ie. a specific IP)\r
+            */\r
+\r
+           if (addr && strnicmp(hostip, exclude_list + s, e - s) == 0)\r
+               return 0; /* IP/hostname excluded. do not use proxy. */\r
+           if (strnicmp(hostname, exclude_list + s, e - s) == 0)\r
+               return 0; /* IP/hostname excluded. do not use proxy. */\r
+       }\r
+\r
+       s = e;\r
+\r
+       /* Make sure we really have reached the next comma or end-of-string */\r
+       while (exclude_list[s] &&\r
+              !isspace((unsigned char)exclude_list[s]) &&\r
+              exclude_list[s] != ',') s++;\r
+    }\r
+\r
+    /* no matches in the exclude list, so use the proxy */\r
+    return 1;\r
+}\r
+\r
+SockAddr name_lookup(char *host, int port, char **canonicalname,\r
+                    const Config *cfg, int addressfamily)\r
+{\r
+    if (cfg->proxy_type != PROXY_NONE &&\r
+       do_proxy_dns(cfg) &&\r
+       proxy_for_destination(NULL, host, port, cfg)) {\r
+       *canonicalname = dupstr(host);\r
+       return sk_nonamelookup(host);\r
+    }\r
+\r
+    return sk_namelookup(host, canonicalname, addressfamily);\r
+}\r
+\r
+Socket new_connection(SockAddr addr, char *hostname,\r
+                     int port, int privport,\r
+                     int oobinline, int nodelay, int keepalive,\r
+                     Plug plug, const Config *cfg)\r
+{\r
+    static const struct socket_function_table socket_fn_table = {\r
+       sk_proxy_plug,\r
+       sk_proxy_close,\r
+       sk_proxy_write,\r
+       sk_proxy_write_oob,\r
+       sk_proxy_flush,\r
+       sk_proxy_set_private_ptr,\r
+       sk_proxy_get_private_ptr,\r
+       sk_proxy_set_frozen,\r
+       sk_proxy_socket_error\r
+    };\r
+\r
+    static const struct plug_function_table plug_fn_table = {\r
+       plug_proxy_log,\r
+       plug_proxy_closing,\r
+       plug_proxy_receive,\r
+       plug_proxy_sent,\r
+       plug_proxy_accepting\r
+    };\r
+\r
+    if (cfg->proxy_type != PROXY_NONE &&\r
+       proxy_for_destination(addr, hostname, port, cfg))\r
+    {\r
+       Proxy_Socket ret;\r
+       Proxy_Plug pplug;\r
+       SockAddr proxy_addr;\r
+       char *proxy_canonical_name;\r
+       Socket sret;\r
+\r
+       if ((sret = platform_new_connection(addr, hostname, port, privport,\r
+                                           oobinline, nodelay, keepalive,\r
+                                           plug, cfg)) !=\r
+           NULL)\r
+           return sret;\r
+\r
+       ret = snew(struct Socket_proxy_tag);\r
+       ret->fn = &socket_fn_table;\r
+       ret->cfg = *cfg;               /* STRUCTURE COPY */\r
+       ret->plug = plug;\r
+       ret->remote_addr = addr;       /* will need to be freed on close */\r
+       ret->remote_port = port;\r
+\r
+       ret->error = NULL;\r
+       ret->pending_flush = 0;\r
+       ret->freeze = 0;\r
+\r
+       bufchain_init(&ret->pending_input_data);\r
+       bufchain_init(&ret->pending_output_data);\r
+       bufchain_init(&ret->pending_oob_output_data);\r
+\r
+       ret->sub_socket = NULL;\r
+       ret->state = PROXY_STATE_NEW;\r
+       ret->negotiate = NULL;\r
+       \r
+       if (cfg->proxy_type == PROXY_HTTP) {\r
+           ret->negotiate = proxy_http_negotiate;\r
+       } else if (cfg->proxy_type == PROXY_SOCKS4) {\r
+            ret->negotiate = proxy_socks4_negotiate;\r
+       } else if (cfg->proxy_type == PROXY_SOCKS5) {\r
+            ret->negotiate = proxy_socks5_negotiate;\r
+       } else if (cfg->proxy_type == PROXY_TELNET) {\r
+           ret->negotiate = proxy_telnet_negotiate;\r
+       } else {\r
+           ret->error = "Proxy error: Unknown proxy method";\r
+           return (Socket) ret;\r
+       }\r
+\r
+       /* create the proxy plug to map calls from the actual\r
+        * socket into our proxy socket layer */\r
+       pplug = snew(struct Plug_proxy_tag);\r
+       pplug->fn = &plug_fn_table;\r
+       pplug->proxy_socket = ret;\r
+\r
+       /* look-up proxy */\r
+       proxy_addr = sk_namelookup(cfg->proxy_host,\r
+                                  &proxy_canonical_name, cfg->addressfamily);\r
+       if (sk_addr_error(proxy_addr) != NULL) {\r
+           ret->error = "Proxy error: Unable to resolve proxy host name";\r
+           return (Socket)ret;\r
+       }\r
+       sfree(proxy_canonical_name);\r
+\r
+       /* create the actual socket we will be using,\r
+        * connected to our proxy server and port.\r
+        */\r
+       ret->sub_socket = sk_new(proxy_addr, cfg->proxy_port,\r
+                                privport, oobinline,\r
+                                nodelay, keepalive, (Plug) pplug);\r
+       if (sk_socket_error(ret->sub_socket) != NULL)\r
+           return (Socket) ret;\r
+\r
+       /* start the proxy negotiation process... */\r
+       sk_set_frozen(ret->sub_socket, 0);\r
+       ret->negotiate(ret, PROXY_CHANGE_NEW);\r
+\r
+       return (Socket) ret;\r
+    }\r
+\r
+    /* no proxy, so just return the direct socket */\r
+    return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);\r
+}\r
+\r
+Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,\r
+                   const Config *cfg, int addressfamily)\r
+{\r
+    /* TODO: SOCKS (and potentially others) support inbound\r
+     * TODO: connections via the proxy. support them.\r
+     */\r
+\r
+    return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * HTTP CONNECT proxy type.\r
+ */\r
+\r
+static int get_line_end (char * data, int len)\r
+{\r
+    int off = 0;\r
+\r
+    while (off < len)\r
+    {\r
+       if (data[off] == '\n') {\r
+           /* we have a newline */\r
+           off++;\r
+\r
+           /* is that the only thing on this line? */\r
+           if (off <= 2) return off;\r
+\r
+           /* if not, then there is the possibility that this header\r
+            * continues onto the next line, if it starts with a space\r
+            * or a tab.\r
+            */\r
+\r
+           if (off + 1 < len &&\r
+               data[off+1] != ' ' &&\r
+               data[off+1] != '\t') return off;\r
+\r
+           /* the line does continue, so we have to keep going\r
+            * until we see an the header's "real" end of line.\r
+            */\r
+           off++;\r
+       }\r
+\r
+       off++;\r
+    }\r
+\r
+    return -1;\r
+}\r
+\r
+int proxy_http_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_STATE_NEW) {\r
+       /* we are just beginning the proxy negotiate process,\r
+        * so we'll send off the initial bits of the request.\r
+        * for this proxy method, it's just a simple HTTP\r
+        * request\r
+        */\r
+       char *buf, dest[512];\r
+\r
+       sk_getaddr(p->remote_addr, dest, lenof(dest));\r
+\r
+       buf = dupprintf("CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n",\r
+                       dest, p->remote_port, dest, p->remote_port);\r
+       sk_write(p->sub_socket, buf, strlen(buf));\r
+       sfree(buf);\r
+\r
+       if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+           char buf[sizeof(p->cfg.proxy_username)+sizeof(p->cfg.proxy_password)];\r
+           char buf2[sizeof(buf)*4/3 + 100];\r
+           int i, j, len;\r
+           sprintf(buf, "%s:%s", p->cfg.proxy_username, p->cfg.proxy_password);\r
+           len = strlen(buf);\r
+           sprintf(buf2, "Proxy-Authorization: Basic ");\r
+           for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4)\r
+               base64_encode_atom((unsigned char *)(buf+i),\r
+                                  (len-i > 3 ? 3 : len-i), buf2+j);\r
+           strcpy(buf2+j, "\r\n");\r
+           sk_write(p->sub_socket, buf2, strlen(buf2));\r
+       }\r
+\r
+       sk_write(p->sub_socket, "\r\n", 2);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       char *data, *datap;\r
+       int len;\r
+       int eol;\r
+\r
+       if (p->state == 1) {\r
+\r
+           int min_ver, maj_ver, status;\r
+\r
+           /* get the status line */\r
+           len = bufchain_size(&p->pending_input_data);\r
+           assert(len > 0);           /* or we wouldn't be here */\r
+           data = snewn(len+1, char);\r
+           bufchain_fetch(&p->pending_input_data, data, len);\r
+           /*\r
+            * We must NUL-terminate this data, because Windows\r
+            * sscanf appears to require a NUL at the end of the\r
+            * string because it strlens it _first_. Sigh.\r
+            */\r
+           data[len] = '\0';\r
+\r
+           eol = get_line_end(data, len);\r
+           if (eol < 0) {\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           status = -1;\r
+           /* We can't rely on whether the %n incremented the sscanf return */\r
+           if (sscanf((char *)data, "HTTP/%i.%i %n",\r
+                      &maj_ver, &min_ver, &status) < 2 || status == -1) {\r
+               plug_closing(p->plug, "Proxy error: HTTP response was absent",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           /* remove the status line from the input buffer. */\r
+           bufchain_consume(&p->pending_input_data, eol);\r
+           if (data[status] != '2') {\r
+               /* error */\r
+               char *buf;\r
+               data[eol] = '\0';\r
+               while (eol > status &&\r
+                      (data[eol-1] == '\r' || data[eol-1] == '\n'))\r
+                   data[--eol] = '\0';\r
+               buf = dupprintf("Proxy error: %s", data+status);\r
+               plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
+               sfree(buf);\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           sfree(data);\r
+\r
+           p->state = 2;\r
+       }\r
+\r
+       if (p->state == 2) {\r
+\r
+           /* get headers. we're done when we get a\r
+            * header of length 2, (ie. just "\r\n")\r
+            */\r
+\r
+           len = bufchain_size(&p->pending_input_data);\r
+           assert(len > 0);           /* or we wouldn't be here */\r
+           data = snewn(len, char);\r
+           datap = data;\r
+           bufchain_fetch(&p->pending_input_data, data, len);\r
+\r
+           eol = get_line_end(datap, len);\r
+           if (eol < 0) {\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+           while (eol > 2)\r
+           {\r
+               bufchain_consume(&p->pending_input_data, eol);\r
+               datap += eol;\r
+               len   -= eol;\r
+               eol = get_line_end(datap, len);\r
+           }\r
+\r
+           if (eol == 2) {\r
+               /* we're done */\r
+               bufchain_consume(&p->pending_input_data, 2);\r
+               proxy_activate(p);\r
+               /* proxy activate will have dealt with\r
+                * whatever is left of the buffer */\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           sfree(data);\r
+           return 1;\r
+       }\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * SOCKS proxy type.\r
+ */\r
+\r
+/* SOCKS version 4 */\r
+int proxy_socks4_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_CHANGE_NEW) {\r
+\r
+       /* request format:\r
+        *  version number (1 byte) = 4\r
+        *  command code (1 byte)\r
+        *    1 = CONNECT\r
+        *    2 = BIND\r
+        *  dest. port (2 bytes) [network order]\r
+        *  dest. address (4 bytes)\r
+        *  user ID (variable length, null terminated string)\r
+        */\r
+\r
+       int length, type, namelen;\r
+       char *command, addr[4], hostname[512];\r
+\r
+       type = sk_addrtype(p->remote_addr);\r
+       if (type == ADDRTYPE_IPV6) {\r
+           plug_closing(p->plug, "Proxy error: SOCKS version 4 does"\r
+                        " not support IPv6", PROXY_ERROR_GENERAL, 0);\r
+           return 1;\r
+       } else if (type == ADDRTYPE_IPV4) {\r
+           namelen = 0;\r
+           sk_addrcopy(p->remote_addr, addr);\r
+       } else {                       /* type == ADDRTYPE_NAME */\r
+           assert(type == ADDRTYPE_NAME);\r
+           sk_getaddr(p->remote_addr, hostname, lenof(hostname));\r
+           namelen = strlen(hostname) + 1;   /* include the NUL */\r
+           addr[0] = addr[1] = addr[2] = 0;\r
+           addr[3] = 1;\r
+       }\r
+\r
+       length = strlen(p->cfg.proxy_username) + namelen + 9;\r
+       command = snewn(length, char);\r
+       strcpy(command + 8, p->cfg.proxy_username);\r
+\r
+       command[0] = 4; /* version 4 */\r
+       command[1] = 1; /* CONNECT command */\r
+\r
+       /* port */\r
+       command[2] = (char) (p->remote_port >> 8) & 0xff;\r
+       command[3] = (char) p->remote_port & 0xff;\r
+\r
+       /* address */\r
+       memcpy(command + 4, addr, 4);\r
+\r
+       /* hostname */\r
+       memcpy(command + 8 + strlen(p->cfg.proxy_username) + 1,\r
+              hostname, namelen);\r
+\r
+       sk_write(p->sub_socket, command, length);\r
+       sfree(command);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       if (p->state == 1) {\r
+           /* response format:\r
+            *  version number (1 byte) = 4\r
+            *  reply code (1 byte)\r
+            *    90 = request granted\r
+            *    91 = request rejected or failed\r
+            *    92 = request rejected due to lack of IDENTD on client\r
+            *    93 = request rejected due to difference in user ID \r
+            *         (what we sent vs. what IDENTD said)\r
+            *  dest. port (2 bytes)\r
+            *  dest. address (4 bytes)\r
+            */\r
+\r
+           char data[8];\r
+\r
+           if (bufchain_size(&p->pending_input_data) < 8)\r
+               return 1;              /* not got anything yet */\r
+           \r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 8);\r
+\r
+           if (data[0] != 0) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy responded with "\r
+                                     "unexpected reply code version",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] != 90) {\r
+\r
+               switch (data[1]) {\r
+                 case 92:\r
+                   plug_closing(p->plug, "Proxy error: SOCKS server wanted IDENTD on client",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   break;\r
+                 case 93:\r
+                   plug_closing(p->plug, "Proxy error: Username and IDENTD on client don't agree",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   break;\r
+                 case 91:\r
+                 default:\r
+                   plug_closing(p->plug, "Proxy error: Error while communicating with proxy",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   break;\r
+               }\r
+\r
+               return 1;\r
+           }\r
+           bufchain_consume(&p->pending_input_data, 8);\r
+\r
+           /* we're done */\r
+           proxy_activate(p);\r
+           /* proxy activate will have dealt with\r
+            * whatever is left of the buffer */\r
+           return 1;\r
+       }\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
+\r
+/* SOCKS version 5 */\r
+int proxy_socks5_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_CHANGE_NEW) {\r
+\r
+       /* initial command:\r
+        *  version number (1 byte) = 5\r
+        *  number of available authentication methods (1 byte)\r
+        *  available authentication methods (1 byte * previous value)\r
+        *    authentication methods:\r
+        *     0x00 = no authentication\r
+        *     0x01 = GSSAPI\r
+        *     0x02 = username/password\r
+        *     0x03 = CHAP\r
+        */\r
+\r
+       char command[5];\r
+       int len;\r
+\r
+       command[0] = 5; /* version 5 */\r
+       if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+           command[2] = 0x00;         /* no authentication */\r
+           len = 3;\r
+           proxy_socks5_offerencryptedauth (command, &len);\r
+           command[len++] = 0x02;             /* username/password */\r
+           command[1] = len - 2;       /* Number of methods supported */\r
+       } else {\r
+           command[1] = 1;            /* one methods supported: */\r
+           command[2] = 0x00;         /* no authentication */\r
+           len = 3;\r
+       }\r
+\r
+       sk_write(p->sub_socket, command, len);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       if (p->state == 1) {\r
+\r
+           /* initial response:\r
+            *  version number (1 byte) = 5\r
+            *  authentication method (1 byte)\r
+            *    authentication methods:\r
+            *     0x00 = no authentication\r
+            *     0x01 = GSSAPI\r
+            *     0x02 = username/password\r
+            *     0x03 = CHAP\r
+            *     0xff = no acceptable methods\r
+            */\r
+           char data[2];\r
+\r
+           if (bufchain_size(&p->pending_input_data) < 2)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 2);\r
+\r
+           if (data[0] != 5) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] == 0x00) p->state = 2; /* no authentication needed */\r
+           else if (data[1] == 0x01) p->state = 4; /* GSSAPI authentication */\r
+           else if (data[1] == 0x02) p->state = 5; /* username/password authentication */\r
+           else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */\r
+           else {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           bufchain_consume(&p->pending_input_data, 2);\r
+       }\r
+\r
+       if (p->state == 7) {\r
+\r
+           /* password authentication reply format:\r
+            *  version number (1 bytes) = 1\r
+            *  reply code (1 byte)\r
+            *    0 = succeeded\r
+            *    >0 = failed\r
+            */\r
+           char data[2];\r
+\r
+           if (bufchain_size(&p->pending_input_data) < 2)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 2);\r
+\r
+           if (data[0] != 1) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS password "\r
+                            "subnegotiation contained wrong version number",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] != 0) {\r
+\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy refused"\r
+                            " password authentication",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           bufchain_consume(&p->pending_input_data, 2);\r
+           p->state = 2;              /* now proceed as authenticated */\r
+       }\r
+\r
+       if (p->state == 8) {\r
+           int ret;\r
+           ret = proxy_socks5_handlechap(p);\r
+           if (ret) return ret;\r
+       }\r
+\r
+       if (p->state == 2) {\r
+\r
+           /* request format:\r
+            *  version number (1 byte) = 5\r
+            *  command code (1 byte)\r
+            *    1 = CONNECT\r
+            *    2 = BIND\r
+            *    3 = UDP ASSOCIATE\r
+            *  reserved (1 byte) = 0x00\r
+            *  address type (1 byte)\r
+            *    1 = IPv4\r
+            *    3 = domainname (first byte has length, no terminating null)\r
+            *    4 = IPv6\r
+            *  dest. address (variable)\r
+            *  dest. port (2 bytes) [network order]\r
+            */\r
+\r
+           char command[512];\r
+           int len;\r
+           int type;\r
+\r
+           type = sk_addrtype(p->remote_addr);\r
+           if (type == ADDRTYPE_IPV4) {\r
+               len = 10;              /* 4 hdr + 4 addr + 2 trailer */\r
+               command[3] = 1; /* IPv4 */\r
+               sk_addrcopy(p->remote_addr, command+4);\r
+           } else if (type == ADDRTYPE_IPV6) {\r
+               len = 22;              /* 4 hdr + 16 addr + 2 trailer */\r
+               command[3] = 4; /* IPv6 */\r
+               sk_addrcopy(p->remote_addr, command+4);\r
+           } else {\r
+               assert(type == ADDRTYPE_NAME);\r
+               command[3] = 3;\r
+               sk_getaddr(p->remote_addr, command+5, 256);\r
+               command[4] = strlen(command+5);\r
+               len = 7 + command[4];  /* 4 hdr, 1 len, N addr, 2 trailer */\r
+           }\r
+\r
+           command[0] = 5; /* version 5 */\r
+           command[1] = 1; /* CONNECT command */\r
+           command[2] = 0x00;\r
+\r
+           /* port */\r
+           command[len-2] = (char) (p->remote_port >> 8) & 0xff;\r
+           command[len-1] = (char) p->remote_port & 0xff;\r
+\r
+           sk_write(p->sub_socket, command, len);\r
+\r
+           p->state = 3;\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 3) {\r
+\r
+           /* reply format:\r
+            *  version number (1 bytes) = 5\r
+            *  reply code (1 byte)\r
+            *    0 = succeeded\r
+            *    1 = general SOCKS server failure\r
+            *    2 = connection not allowed by ruleset\r
+            *    3 = network unreachable\r
+            *    4 = host unreachable\r
+            *    5 = connection refused\r
+            *    6 = TTL expired\r
+            *    7 = command not supported\r
+            *    8 = address type not supported\r
+            * reserved (1 byte) = x00\r
+            * address type (1 byte)\r
+            *    1 = IPv4\r
+            *    3 = domainname (first byte has length, no terminating null)\r
+            *    4 = IPv6\r
+            * server bound address (variable)\r
+            * server bound port (2 bytes) [network order]\r
+            */\r
+           char data[5];\r
+           int len;\r
+\r
+           /* First 5 bytes of packet are enough to tell its length. */ \r
+           if (bufchain_size(&p->pending_input_data) < 5)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 5);\r
+\r
+           if (data[0] != 5) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] != 0) {\r
+               char buf[256];\r
+\r
+               strcpy(buf, "Proxy error: ");\r
+\r
+               switch (data[1]) {\r
+                 case 1: strcat(buf, "General SOCKS server failure"); break;\r
+                 case 2: strcat(buf, "Connection not allowed by ruleset"); break;\r
+                 case 3: strcat(buf, "Network unreachable"); break;\r
+                 case 4: strcat(buf, "Host unreachable"); break;\r
+                 case 5: strcat(buf, "Connection refused"); break;\r
+                 case 6: strcat(buf, "TTL expired"); break;\r
+                 case 7: strcat(buf, "Command not supported"); break;\r
+                 case 8: strcat(buf, "Address type not supported"); break;\r
+                 default: sprintf(buf+strlen(buf),\r
+                                  "Unrecognised SOCKS error code %d",\r
+                                  data[1]);\r
+                   break;\r
+               }\r
+               plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
+\r
+               return 1;\r
+           }\r
+\r
+           /*\r
+            * Eat the rest of the reply packet.\r
+            */\r
+           len = 6;                   /* first 4 bytes, last 2 */\r
+           switch (data[3]) {\r
+             case 1: len += 4; break; /* IPv4 address */\r
+             case 4: len += 16; break;/* IPv6 address */\r
+             case 3: len += (unsigned char)data[4]; break; /* domain name */\r
+             default:\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy returned "\r
+                            "unrecognised address format",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           if (bufchain_size(&p->pending_input_data) < len)\r
+               return 1;              /* not got whole reply yet */\r
+           bufchain_consume(&p->pending_input_data, len);\r
+\r
+           /* we're done */\r
+           proxy_activate(p);\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 4) {\r
+           /* TODO: Handle GSSAPI authentication */\r
+           plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication",\r
+                        PROXY_ERROR_GENERAL, 0);\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 5) {\r
+           if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+               char userpwbuf[514];\r
+               int ulen, plen;\r
+               ulen = strlen(p->cfg.proxy_username);\r
+               if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;\r
+               plen = strlen(p->cfg.proxy_password);\r
+               if (plen > 255) plen = 255; if (plen < 1) plen = 1;\r
+               userpwbuf[0] = 1;      /* version number of subnegotiation */\r
+               userpwbuf[1] = ulen;\r
+               memcpy(userpwbuf+2, p->cfg.proxy_username, ulen);\r
+               userpwbuf[ulen+2] = plen;\r
+               memcpy(userpwbuf+ulen+3, p->cfg.proxy_password, plen);\r
+               sk_write(p->sub_socket, userpwbuf, ulen + plen + 3);\r
+               p->state = 7;\r
+           } else \r
+               plug_closing(p->plug, "Proxy error: Server chose "\r
+                            "username/password authentication but we "\r
+                            "didn't offer it!",\r
+                        PROXY_ERROR_GENERAL, 0);\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 6) {\r
+           int ret;\r
+           ret = proxy_socks5_selectchap(p);\r
+           if (ret) return ret;\r
+       }\r
+\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * `Telnet' proxy type.\r
+ *\r
+ * (This is for ad-hoc proxies where you connect to the proxy's\r
+ * telnet port and send a command such as `connect host port'. The\r
+ * command is configurable, since this proxy type is typically not\r
+ * standardised or at all well-defined.)\r
+ */\r
+\r
+char *format_telnet_command(SockAddr addr, int port, const Config *cfg)\r
+{\r
+    char *ret = NULL;\r
+    int retlen = 0, retsize = 0;\r
+    int so = 0, eo = 0;\r
+#define ENSURE(n) do { \\r
+    if (retsize < retlen + n) { \\r
+       retsize = retlen + n + 512; \\r
+       ret = sresize(ret, retsize, char); \\r
+    } \\r
+} while (0)\r
+\r
+    /* we need to escape \\, \%, \r, \n, \t, \x??, \0???, \r
+     * %%, %host, %port, %user, and %pass\r
+     */\r
+\r
+    while (cfg->proxy_telnet_command[eo] != 0) {\r
+\r
+       /* scan forward until we hit end-of-line,\r
+        * or an escape character (\ or %) */\r
+       while (cfg->proxy_telnet_command[eo] != 0 &&\r
+              cfg->proxy_telnet_command[eo] != '%' &&\r
+              cfg->proxy_telnet_command[eo] != '\\') eo++;\r
+\r
+       /* if we hit eol, break out of our escaping loop */\r
+       if (cfg->proxy_telnet_command[eo] == 0) break;\r
+\r
+       /* if there was any unescaped text before the escape\r
+        * character, send that now */\r
+       if (eo != so) {\r
+           ENSURE(eo - so);\r
+           memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);\r
+           retlen += eo - so;\r
+       }\r
+\r
+       so = eo++;\r
+\r
+       /* if the escape character was the last character of\r
+        * the line, we'll just stop and send it. */\r
+       if (cfg->proxy_telnet_command[eo] == 0) break;\r
+\r
+       if (cfg->proxy_telnet_command[so] == '\\') {\r
+\r
+           /* we recognize \\, \%, \r, \n, \t, \x??.\r
+            * anything else, we just send unescaped (including the \).\r
+            */\r
+\r
+           switch (cfg->proxy_telnet_command[eo]) {\r
+\r
+             case '\\':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\\';\r
+               eo++;\r
+               break;\r
+\r
+             case '%':\r
+               ENSURE(1);\r
+               ret[retlen++] = '%';\r
+               eo++;\r
+               break;\r
+\r
+             case 'r':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\r';\r
+               eo++;\r
+               break;\r
+\r
+             case 'n':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\n';\r
+               eo++;\r
+               break;\r
+\r
+             case 't':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\t';\r
+               eo++;\r
+               break;\r
+\r
+             case 'x':\r
+             case 'X':\r
+               {\r
+                   /* escaped hexadecimal value (ie. \xff) */\r
+                   unsigned char v = 0;\r
+                   int i = 0;\r
+\r
+                   for (;;) {\r
+                       eo++;\r
+                       if (cfg->proxy_telnet_command[eo] >= '0' &&\r
+                           cfg->proxy_telnet_command[eo] <= '9')\r
+                           v += cfg->proxy_telnet_command[eo] - '0';\r
+                       else if (cfg->proxy_telnet_command[eo] >= 'a' &&\r
+                                cfg->proxy_telnet_command[eo] <= 'f')\r
+                           v += cfg->proxy_telnet_command[eo] - 'a' + 10;\r
+                       else if (cfg->proxy_telnet_command[eo] >= 'A' &&\r
+                                cfg->proxy_telnet_command[eo] <= 'F')\r
+                           v += cfg->proxy_telnet_command[eo] - 'A' + 10;\r
+                       else {\r
+                           /* non hex character, so we abort and just\r
+                            * send the whole thing unescaped (including \x)\r
+                            */\r
+                           ENSURE(1);\r
+                           ret[retlen++] = '\\';\r
+                           eo = so + 1;\r
+                           break;\r
+                       }\r
+\r
+                       /* we only extract two hex characters */\r
+                       if (i == 1) {\r
+                           ENSURE(1);\r
+                           ret[retlen++] = v;\r
+                           eo++;\r
+                           break;\r
+                       }\r
+\r
+                       i++;\r
+                       v <<= 4;\r
+                   }\r
+               }\r
+               break;\r
+\r
+             default:\r
+               ENSURE(2);\r
+               memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2);\r
+               retlen += 2;\r
+               eo++;\r
+               break;\r
+           }\r
+       } else {\r
+\r
+           /* % escape. we recognize %%, %host, %port, %user, %pass.\r
+            * %proxyhost, %proxyport. Anything else we just send\r
+            * unescaped (including the %).\r
+            */\r
+\r
+           if (cfg->proxy_telnet_command[eo] == '%') {\r
+               ENSURE(1);\r
+               ret[retlen++] = '%';\r
+               eo++;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "host", 4) == 0) {\r
+               char dest[512];\r
+               int destlen;\r
+               sk_getaddr(addr, dest, lenof(dest));\r
+               destlen = strlen(dest);\r
+               ENSURE(destlen);\r
+               memcpy(ret+retlen, dest, destlen);\r
+               retlen += destlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "port", 4) == 0) {\r
+               char portstr[8], portlen;\r
+               portlen = sprintf(portstr, "%i", port);\r
+               ENSURE(portlen);\r
+               memcpy(ret + retlen, portstr, portlen);\r
+               retlen += portlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "user", 4) == 0) {\r
+               int userlen = strlen(cfg->proxy_username);\r
+               ENSURE(userlen);\r
+               memcpy(ret+retlen, cfg->proxy_username, userlen);\r
+               retlen += userlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "pass", 4) == 0) {\r
+               int passlen = strlen(cfg->proxy_password);\r
+               ENSURE(passlen);\r
+               memcpy(ret+retlen, cfg->proxy_password, passlen);\r
+               retlen += passlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "proxyhost", 9) == 0) {\r
+               int phlen = strlen(cfg->proxy_host);\r
+               ENSURE(phlen);\r
+               memcpy(ret+retlen, cfg->proxy_host, phlen);\r
+               retlen += phlen;\r
+               eo += 9;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "proxyport", 9) == 0) {\r
+                char pport[50];\r
+               int pplen;\r
+                sprintf(pport, "%d", cfg->proxy_port);\r
+                pplen = strlen(pport);\r
+               ENSURE(pplen);\r
+               memcpy(ret+retlen, pport, pplen);\r
+               retlen += pplen;\r
+               eo += 9;\r
+           }\r
+           else {\r
+               /* we don't escape this, so send the % now, and\r
+                * don't advance eo, so that we'll consider the\r
+                * text immediately following the % as unescaped.\r
+                */\r
+               ENSURE(1);\r
+               ret[retlen++] = '%';\r
+           }\r
+       }\r
+\r
+       /* resume scanning for additional escapes after this one. */\r
+       so = eo;\r
+    }\r
+\r
+    /* if there is any unescaped text at the end of the line, send it */\r
+    if (eo != so) {\r
+       ENSURE(eo - so);\r
+       memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);\r
+       retlen += eo - so;\r
+    }\r
+\r
+    ENSURE(1);\r
+    ret[retlen] = '\0';\r
+    return ret;\r
+\r
+#undef ENSURE\r
+}\r
+\r
+int proxy_telnet_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_CHANGE_NEW) {\r
+       char *formatted_cmd;\r
+\r
+       formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,\r
+                                             &p->cfg);\r
+\r
+       sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));\r
+       sfree(formatted_cmd);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       /* we're done */\r
+       proxy_activate(p);\r
+       /* proxy activate will have dealt with\r
+        * whatever is left of the buffer */\r
+       return 1;\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
diff --git a/putty/PROXY.H b/putty/PROXY.H
new file mode 100644 (file)
index 0000000..683b260
--- /dev/null
@@ -0,0 +1,123 @@
+/*\r
+ * Network proxy abstraction in PuTTY\r
+ *\r
+ * A proxy layer, if necessary, wedges itself between the\r
+ * network code and the higher level backend.\r
+ *\r
+ * Supported proxies: HTTP CONNECT, generic telnet, SOCKS 4 & 5\r
+ */\r
+\r
+#ifndef PUTTY_PROXY_H\r
+#define PUTTY_PROXY_H\r
+\r
+#define PROXY_ERROR_GENERAL 8000\r
+#define PROXY_ERROR_UNEXPECTED 8001\r
+\r
+typedef struct Socket_proxy_tag * Proxy_Socket;\r
+\r
+struct Socket_proxy_tag {\r
+    const struct socket_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+\r
+    char * error;\r
+\r
+    Socket sub_socket;\r
+    Plug plug;\r
+    SockAddr remote_addr;\r
+    int remote_port;\r
+\r
+    bufchain pending_output_data;\r
+    bufchain pending_oob_output_data;\r
+    int pending_flush;\r
+    bufchain pending_input_data;\r
+\r
+#define PROXY_STATE_NEW    -1\r
+#define PROXY_STATE_ACTIVE  0\r
+\r
+    int state; /* proxy states greater than 0 are implementation\r
+               * dependent, but represent various stages/states\r
+               * of the initialization/setup/negotiation with the\r
+               * proxy server.\r
+               */\r
+    int freeze; /* should we freeze the underlying socket when\r
+                * we are done with the proxy negotiation? this\r
+                * simply caches the value of sk_set_frozen calls.\r
+                */\r
+\r
+#define PROXY_CHANGE_NEW      -1\r
+#define PROXY_CHANGE_CLOSING   0\r
+#define PROXY_CHANGE_SENT      1\r
+#define PROXY_CHANGE_RECEIVE   2\r
+#define PROXY_CHANGE_ACCEPTING 3\r
+\r
+    /* something has changed (a call from the sub socket\r
+     * layer into our Proxy Plug layer, or we were just\r
+     * created, etc), so the proxy layer needs to handle\r
+     * this change (the type of which is the second argument)\r
+     * and further the proxy negotiation process.\r
+     */\r
+\r
+    int (*negotiate) (Proxy_Socket /* this */, int /* change type */);\r
+\r
+    /* current arguments of plug handlers\r
+     * (for use by proxy's negotiate function)\r
+     */\r
+\r
+    /* closing */\r
+    const char *closing_error_msg;\r
+    int closing_error_code;\r
+    int closing_calling_back;\r
+\r
+    /* receive */\r
+    int receive_urgent;\r
+    char *receive_data;\r
+    int receive_len;\r
+\r
+    /* sent */\r
+    int sent_bufsize;\r
+\r
+    /* accepting */\r
+    OSSocket accepting_sock;\r
+\r
+    /* configuration, used to look up proxy settings */\r
+    Config cfg;\r
+\r
+    /* CHAP transient data */\r
+    int chap_num_attributes;\r
+    int chap_num_attributes_processed;\r
+    int chap_current_attribute;\r
+    int chap_current_datalen;\r
+};\r
+\r
+typedef struct Plug_proxy_tag * Proxy_Plug;\r
+\r
+struct Plug_proxy_tag {\r
+    const struct plug_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+\r
+    Proxy_Socket proxy_socket;\r
+\r
+};\r
+\r
+extern void proxy_activate (Proxy_Socket);\r
+\r
+extern int proxy_http_negotiate (Proxy_Socket, int);\r
+extern int proxy_telnet_negotiate (Proxy_Socket, int);\r
+extern int proxy_socks4_negotiate (Proxy_Socket, int);\r
+extern int proxy_socks5_negotiate (Proxy_Socket, int);\r
+\r
+/*\r
+ * This may be reused by local-command proxies on individual\r
+ * platforms.\r
+ */\r
+char *format_telnet_command(SockAddr addr, int port, const Config *cfg);\r
+\r
+/*\r
+ * These are implemented in cproxy.c or nocproxy.c, depending on\r
+ * whether encrypted proxy authentication is available.\r
+ */\r
+extern void proxy_socks5_offerencryptedauth(char *command, int *len);\r
+extern int proxy_socks5_handlechap (Proxy_Socket p);\r
+extern int proxy_socks5_selectchap(Proxy_Socket p);\r
+\r
+#endif\r
diff --git a/putty/PSCP.C b/putty/PSCP.C
new file mode 100644 (file)
index 0000000..14fff5c
--- /dev/null
@@ -0,0 +1,2308 @@
+/*\r
+ * scp.c  -  Scp (Secure Copy) client for PuTTY.\r
+ * Joris van Rantwijk, Simon Tatham\r
+ *\r
+ * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen.\r
+ * They, in turn, used stuff from BSD rcp.\r
+ * \r
+ * (SGT, 2001-09-10: Joris van Rantwijk assures me that although\r
+ * this file as originally submitted was inspired by, and\r
+ * _structurally_ based on, ssh-1.2.26's scp.c, there wasn't any\r
+ * actual code duplicated, so the above comment shouldn't give rise\r
+ * to licensing issues.)\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <limits.h>\r
+#include <time.h>\r
+#include <assert.h>\r
+\r
+#define PUTTY_DO_GLOBALS\r
+#include "putty.h"\r
+#include "psftp.h"\r
+#include "ssh.h"\r
+#include "sftp.h"\r
+#include "storage.h"\r
+#include "int64.h"\r
+\r
+static int list = 0;\r
+static int verbose = 0;\r
+static int recursive = 0;\r
+static int preserve = 0;\r
+static int targetshouldbedirectory = 0;\r
+static int statistics = 1;\r
+static int prev_stats_len = 0;\r
+static int scp_unsafe_mode = 0;\r
+static int errs = 0;\r
+static int try_scp = 1;\r
+static int try_sftp = 1;\r
+static int main_cmd_is_sftp = 0;\r
+static int fallback_cmd_is_sftp = 0;\r
+static int using_sftp = 0;\r
+\r
+static Backend *back;\r
+static void *backhandle;\r
+static Config cfg;\r
+\r
+static void source(char *src);\r
+static void rsource(char *src);\r
+static void sink(char *targ, char *src);\r
+\r
+const char *const appname = "PSCP";\r
+\r
+/*\r
+ * The maximum amount of queued data we accept before we stop and\r
+ * wait for the server to process some.\r
+ */\r
+#define MAX_SCP_BUFSIZE 16384\r
+\r
+void ldisc_send(void *handle, char *buf, int len, int interactive)\r
+{\r
+    /*\r
+     * This is only here because of the calls to ldisc_send(NULL,\r
+     * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc\r
+     * as an ldisc. So if we get called with any real data, I want\r
+     * to know about it.\r
+     */\r
+    assert(len == 0);\r
+}\r
+\r
+static void tell_char(FILE * stream, char c)\r
+{\r
+    fputc(c, stream);\r
+}\r
+\r
+static void tell_str(FILE * stream, char *str)\r
+{\r
+    unsigned int i;\r
+\r
+    for (i = 0; i < strlen(str); ++i)\r
+       tell_char(stream, str[i]);\r
+}\r
+\r
+static void tell_user(FILE * stream, char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    str2 = dupcat(str, "\n", NULL);\r
+    sfree(str);\r
+    tell_str(stream, str2);\r
+    sfree(str2);\r
+}\r
+\r
+/*\r
+ *  Print an error message and perform a fatal exit.\r
+ */\r
+void fatalbox(char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    str2 = dupcat("Fatal: ", str, "\n", NULL);\r
+    sfree(str);\r
+    va_end(ap);\r
+    tell_str(stderr, str2);\r
+    sfree(str2);\r
+    errs++;\r
+\r
+    cleanup_exit(1);\r
+}\r
+void modalfatalbox(char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    str2 = dupcat("Fatal: ", str, "\n", NULL);\r
+    sfree(str);\r
+    va_end(ap);\r
+    tell_str(stderr, str2);\r
+    sfree(str2);\r
+    errs++;\r
+\r
+    cleanup_exit(1);\r
+}\r
+void connection_fatal(void *frontend, char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    str2 = dupcat("Fatal: ", str, "\n", NULL);\r
+    sfree(str);\r
+    va_end(ap);\r
+    tell_str(stderr, str2);\r
+    sfree(str2);\r
+    errs++;\r
+\r
+    cleanup_exit(1);\r
+}\r
+\r
+/*\r
+ * In pscp, all agent requests should be synchronous, so this is a\r
+ * never-called stub.\r
+ */\r
+void agent_schedule_callback(void (*callback)(void *, void *, int),\r
+                            void *callback_ctx, void *data, int len)\r
+{\r
+    assert(!"We shouldn't be here");\r
+}\r
+\r
+/*\r
+ * Receive a block of data from the SSH link. Block until all data\r
+ * is available.\r
+ *\r
+ * To do this, we repeatedly call the SSH protocol module, with our\r
+ * own trap in from_backend() to catch the data that comes back. We\r
+ * do this until we have enough data.\r
+ */\r
+\r
+static unsigned char *outptr;         /* where to put the data */\r
+static unsigned outlen;                       /* how much data required */\r
+static unsigned char *pending = NULL;  /* any spare data */\r
+static unsigned pendlen = 0, pendsize = 0;     /* length and phys. size of buffer */\r
+int from_backend(void *frontend, int is_stderr, const char *data, int datalen)\r
+{\r
+    unsigned char *p = (unsigned char *) data;\r
+    unsigned len = (unsigned) datalen;\r
+\r
+    /*\r
+     * stderr data is just spouted to local stderr and otherwise\r
+     * ignored.\r
+     */\r
+    if (is_stderr) {\r
+       if (len > 0)\r
+           if (fwrite(data, 1, len, stderr) < len)\r
+               /* oh well */;\r
+       return 0;\r
+    }\r
+\r
+    if ((outlen > 0) && (len > 0)) {\r
+       unsigned used = outlen;\r
+       if (used > len)\r
+           used = len;\r
+       memcpy(outptr, p, used);\r
+       outptr += used;\r
+       outlen -= used;\r
+       p += used;\r
+       len -= used;\r
+    }\r
+\r
+    if (len > 0) {\r
+       if (pendsize < pendlen + len) {\r
+           pendsize = pendlen + len + 4096;\r
+           pending = sresize(pending, pendsize, unsigned char);\r
+       }\r
+       memcpy(pending + pendlen, p, len);\r
+       pendlen += len;\r
+    }\r
+\r
+    return 0;\r
+}\r
+int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
+{\r
+    /*\r
+     * No "untrusted" output should get here (the way the code is\r
+     * currently, it's all diverted by FLAG_STDERR).\r
+     */\r
+    assert(!"Unexpected call to from_backend_untrusted()");\r
+    return 0; /* not reached */\r
+}\r
+static int ssh_scp_recv(unsigned char *buf, int len)\r
+{\r
+    outptr = buf;\r
+    outlen = len;\r
+\r
+    /*\r
+     * See if the pending-input block contains some of what we\r
+     * need.\r
+     */\r
+    if (pendlen > 0) {\r
+       unsigned pendused = pendlen;\r
+       if (pendused > outlen)\r
+           pendused = outlen;\r
+       memcpy(outptr, pending, pendused);\r
+       memmove(pending, pending + pendused, pendlen - pendused);\r
+       outptr += pendused;\r
+       outlen -= pendused;\r
+       pendlen -= pendused;\r
+       if (pendlen == 0) {\r
+           pendsize = 0;\r
+           sfree(pending);\r
+           pending = NULL;\r
+       }\r
+       if (outlen == 0)\r
+           return len;\r
+    }\r
+\r
+    while (outlen > 0) {\r
+       if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)\r
+           return 0;                  /* doom */\r
+    }\r
+\r
+    return len;\r
+}\r
+\r
+/*\r
+ * Loop through the ssh connection and authentication process.\r
+ */\r
+static void ssh_scp_init(void)\r
+{\r
+    while (!back->sendok(backhandle)) {\r
+        if (back->exitcode(backhandle) >= 0) {\r
+            errs++;\r
+            return;\r
+        }\r
+       if (ssh_sftp_loop_iteration() < 0) {\r
+            errs++;\r
+           return;                    /* doom */\r
+        }\r
+    }\r
+\r
+    /* Work out which backend we ended up using. */\r
+    if (!ssh_fallback_cmd(backhandle))\r
+       using_sftp = main_cmd_is_sftp;\r
+    else\r
+       using_sftp = fallback_cmd_is_sftp;\r
+\r
+    if (verbose) {\r
+       if (using_sftp)\r
+           tell_user(stderr, "Using SFTP");\r
+       else\r
+           tell_user(stderr, "Using SCP1");\r
+    }\r
+}\r
+\r
+/*\r
+ *  Print an error message and exit after closing the SSH link.\r
+ */\r
+static void bump(char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    str2 = dupcat(str, "\n", NULL);\r
+    sfree(str);\r
+    tell_str(stderr, str2);\r
+    sfree(str2);\r
+    errs++;\r
+\r
+    if (back != NULL && back->connected(backhandle)) {\r
+       char ch;\r
+       back->special(backhandle, TS_EOF);\r
+       ssh_scp_recv((unsigned char *) &ch, 1);\r
+    }\r
+\r
+    cleanup_exit(1);\r
+}\r
+\r
+/*\r
+ *  Open an SSH connection to user@host and execute cmd.\r
+ */\r
+static void do_cmd(char *host, char *user, char *cmd)\r
+{\r
+    const char *err;\r
+    char *realhost;\r
+    void *logctx;\r
+\r
+    if (host == NULL || host[0] == '\0')\r
+       bump("Empty host name");\r
+\r
+    /*\r
+     * Remove fiddly bits of address: remove a colon suffix, and\r
+     * the square brackets around an IPv6 literal address.\r
+     */\r
+    if (host[0] == '[') {\r
+       host++;\r
+       host[strcspn(host, "]")] = '\0';\r
+    } else {\r
+       host[strcspn(host, ":")] = '\0';\r
+    }\r
+\r
+    /*\r
+     * If we haven't loaded session details already (e.g., from -load),\r
+     * try looking for a session called "host".\r
+     */\r
+    if (!loaded_session) {\r
+       /* Try to load settings for `host' into a temporary config */\r
+       Config cfg2;\r
+       cfg2.host[0] = '\0';\r
+       do_defaults(host, &cfg2);\r
+       if (cfg2.host[0] != '\0') {\r
+           /* Settings present and include hostname */\r
+           /* Re-load data into the real config. */\r
+           do_defaults(host, &cfg);\r
+       } else {\r
+           /* Session doesn't exist or mention a hostname. */\r
+           /* Use `host' as a bare hostname. */\r
+           strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
+           cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+       }\r
+    } else {\r
+       /* Patch in hostname `host' to session details. */\r
+       strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
+       cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+    }\r
+\r
+    /*\r
+     * Force use of SSH. (If they got the protocol wrong we assume the\r
+     * port is useless too.)\r
+     */\r
+    if (cfg.protocol != PROT_SSH) {\r
+        cfg.protocol = PROT_SSH;\r
+        cfg.port = 22;\r
+    }\r
+\r
+    /*\r
+     * Enact command-line overrides.\r
+     */\r
+    cmdline_run_saved(&cfg);\r
+\r
+    /*\r
+     * Trim leading whitespace off the hostname if it's there.\r
+     */\r
+    {\r
+       int space = strspn(cfg.host, " \t");\r
+       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
+    }\r
+\r
+    /* See if host is of the form user@host */\r
+    if (cfg.host[0] != '\0') {\r
+       char *atsign = strrchr(cfg.host, '@');\r
+       /* Make sure we're not overflowing the user field */\r
+       if (atsign) {\r
+           if (atsign - cfg.host < sizeof cfg.username) {\r
+               strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
+               cfg.username[atsign - cfg.host] = '\0';\r
+           }\r
+           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Remove any remaining whitespace from the hostname.\r
+     */\r
+    {\r
+       int p1 = 0, p2 = 0;\r
+       while (cfg.host[p2] != '\0') {\r
+           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
+               cfg.host[p1] = cfg.host[p2];\r
+               p1++;\r
+           }\r
+           p2++;\r
+       }\r
+       cfg.host[p1] = '\0';\r
+    }\r
+\r
+    /* Set username */\r
+    if (user != NULL && user[0] != '\0') {\r
+       strncpy(cfg.username, user, sizeof(cfg.username) - 1);\r
+       cfg.username[sizeof(cfg.username) - 1] = '\0';\r
+    } else if (cfg.username[0] == '\0') {\r
+       user = get_username();\r
+       if (!user)\r
+           bump("Empty user name");\r
+       else {\r
+           if (verbose)\r
+               tell_user(stderr, "Guessing user name: %s", user);\r
+           strncpy(cfg.username, user, sizeof(cfg.username) - 1);\r
+           cfg.username[sizeof(cfg.username) - 1] = '\0';\r
+           sfree(user);\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Disable scary things which shouldn't be enabled for simple\r
+     * things like SCP and SFTP: agent forwarding, port forwarding,\r
+     * X forwarding.\r
+     */\r
+    cfg.x11_forward = 0;\r
+    cfg.agentfwd = 0;\r
+    cfg.portfwd[0] = cfg.portfwd[1] = '\0';\r
+    cfg.ssh_simple = TRUE;\r
+\r
+    /*\r
+     * Set up main and possibly fallback command depending on\r
+     * options specified by user.\r
+     * Attempt to start the SFTP subsystem as a first choice,\r
+     * falling back to the provided scp command if that fails.\r
+     */\r
+    cfg.remote_cmd_ptr2 = NULL;\r
+    if (try_sftp) {\r
+       /* First choice is SFTP subsystem. */\r
+       main_cmd_is_sftp = 1;\r
+       strcpy(cfg.remote_cmd, "sftp");\r
+       cfg.ssh_subsys = TRUE;\r
+       if (try_scp) {\r
+           /* Fallback is to use the provided scp command. */\r
+           fallback_cmd_is_sftp = 0;\r
+           cfg.remote_cmd_ptr2 = cmd;\r
+           cfg.ssh_subsys2 = FALSE;\r
+       } else {\r
+           /* Since we're not going to try SCP, we may as well try\r
+            * harder to find an SFTP server, since in the current\r
+            * implementation we have a spare slot. */\r
+           fallback_cmd_is_sftp = 1;\r
+           /* see psftp.c for full explanation of this kludge */\r
+           cfg.remote_cmd_ptr2 = \r
+               "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"\r
+               "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"\r
+               "exec sftp-server";\r
+           cfg.ssh_subsys2 = FALSE;\r
+       }\r
+    } else {\r
+       /* Don't try SFTP at all; just try the scp command. */\r
+       main_cmd_is_sftp = 0;\r
+       cfg.remote_cmd_ptr = cmd;\r
+       cfg.ssh_subsys = FALSE;\r
+    }\r
+    cfg.nopty = TRUE;\r
+\r
+    back = &ssh_backend;\r
+\r
+    err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost, \r
+                    0, cfg.tcp_keepalives);\r
+    if (err != NULL)\r
+       bump("ssh_init: %s", err);\r
+    logctx = log_init(NULL, &cfg);\r
+    back->provide_logctx(backhandle, logctx);\r
+    console_provide_logctx(logctx);\r
+    ssh_scp_init();\r
+    if (verbose && realhost != NULL && errs == 0)\r
+       tell_user(stderr, "Connected to %s\n", realhost);\r
+    sfree(realhost);\r
+}\r
+\r
+/*\r
+ *  Update statistic information about current file.\r
+ */\r
+static void print_stats(char *name, uint64 size, uint64 done,\r
+                       time_t start, time_t now)\r
+{\r
+    float ratebs;\r
+    unsigned long eta;\r
+    char *etastr;\r
+    int pct;\r
+    int len;\r
+    int elap;\r
+    double donedbl;\r
+    double sizedbl;\r
+\r
+    elap = (unsigned long) difftime(now, start);\r
+\r
+    if (now > start)\r
+       ratebs = (float) (uint64_to_double(done) / elap);\r
+    else\r
+       ratebs = (float) uint64_to_double(done);\r
+\r
+    if (ratebs < 1.0)\r
+       eta = (unsigned long) (uint64_to_double(uint64_subtract(size, done)));\r
+    else {\r
+        eta = (unsigned long)\r
+           ((uint64_to_double(uint64_subtract(size, done)) / ratebs));\r
+    }\r
+\r
+    etastr = dupprintf("%02ld:%02ld:%02ld",\r
+                      eta / 3600, (eta % 3600) / 60, eta % 60);\r
+\r
+    donedbl = uint64_to_double(done);\r
+    sizedbl = uint64_to_double(size);\r
+    pct = (int) (100 * (donedbl * 1.0 / sizedbl));\r
+\r
+    {\r
+       char donekb[40];\r
+       /* divide by 1024 to provide kB */\r
+       uint64_decimal(uint64_shift_right(done, 10), donekb);\r
+       len = printf("\r%-25.25s | %s kB | %5.1f kB/s | ETA: %8s | %3d%%",\r
+                    name,\r
+                    donekb, ratebs / 1024.0, etastr, pct);\r
+       if (len < prev_stats_len)\r
+           printf("%*s", prev_stats_len - len, "");\r
+       prev_stats_len = len;\r
+\r
+       if (uint64_compare(done, size) == 0)\r
+           printf("\n");\r
+\r
+       fflush(stdout);\r
+    }\r
+\r
+    free(etastr);\r
+}\r
+\r
+/*\r
+ *  Find a colon in str and return a pointer to the colon.\r
+ *  This is used to separate hostname from filename.\r
+ */\r
+static char *colon(char *str)\r
+{\r
+    /* We ignore a leading colon, since the hostname cannot be\r
+       empty. We also ignore a colon as second character because\r
+       of filenames like f:myfile.txt. */\r
+    if (str[0] == '\0' || str[0] == ':' ||\r
+        (str[0] != '[' && str[1] == ':'))\r
+       return (NULL);\r
+    while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\') {\r
+       if (*str == '[') {\r
+           /* Skip over IPv6 literal addresses\r
+            * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */\r
+           char *ipv6_end = strchr(str, ']');\r
+           if (ipv6_end) {\r
+               str = ipv6_end;\r
+           }\r
+       }\r
+       str++;\r
+    }\r
+    if (*str == ':')\r
+       return (str);\r
+    else\r
+       return (NULL);\r
+}\r
+\r
+/*\r
+ * Return a pointer to the portion of str that comes after the last\r
+ * slash (or backslash or colon, if `local' is TRUE).\r
+ */\r
+static char *stripslashes(char *str, int local)\r
+{\r
+    char *p;\r
+\r
+    if (local) {\r
+        p = strchr(str, ':');\r
+        if (p) str = p+1;\r
+    }\r
+\r
+    p = strrchr(str, '/');\r
+    if (p) str = p+1;\r
+\r
+    if (local) {\r
+       p = strrchr(str, '\\');\r
+       if (p) str = p+1;\r
+    }\r
+\r
+    return str;\r
+}\r
+\r
+/*\r
+ * Determine whether a string is entirely composed of dots.\r
+ */\r
+static int is_dots(char *str)\r
+{\r
+    return str[strspn(str, ".")] == '\0';\r
+}\r
+\r
+/*\r
+ *  Wait for a response from the other side.\r
+ *  Return 0 if ok, -1 if error.\r
+ */\r
+static int response(void)\r
+{\r
+    char ch, resp, rbuf[2048];\r
+    int p;\r
+\r
+    if (ssh_scp_recv((unsigned char *) &resp, 1) <= 0)\r
+       bump("Lost connection");\r
+\r
+    p = 0;\r
+    switch (resp) {\r
+      case 0:                         /* ok */\r
+       return (0);\r
+      default:\r
+       rbuf[p++] = resp;\r
+       /* fallthrough */\r
+      case 1:                         /* error */\r
+      case 2:                         /* fatal error */\r
+       do {\r
+           if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)\r
+               bump("Protocol error: Lost connection");\r
+           rbuf[p++] = ch;\r
+       } while (p < sizeof(rbuf) && ch != '\n');\r
+       rbuf[p - 1] = '\0';\r
+       if (resp == 1)\r
+           tell_user(stderr, "%s\n", rbuf);\r
+       else\r
+           bump("%s", rbuf);\r
+       errs++;\r
+       return (-1);\r
+    }\r
+}\r
+\r
+int sftp_recvdata(char *buf, int len)\r
+{\r
+    return ssh_scp_recv((unsigned char *) buf, len);\r
+}\r
+int sftp_senddata(char *buf, int len)\r
+{\r
+    back->send(backhandle, buf, len);\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * sftp-based replacement for the hacky `pscp -ls'.\r
+ */\r
+static int sftp_ls_compare(const void *av, const void *bv)\r
+{\r
+    const struct fxp_name *a = (const struct fxp_name *) av;\r
+    const struct fxp_name *b = (const struct fxp_name *) bv;\r
+    return strcmp(a->filename, b->filename);\r
+}\r
+void scp_sftp_listdir(char *dirname)\r
+{\r
+    struct fxp_handle *dirh;\r
+    struct fxp_names *names;\r
+    struct fxp_name *ournames;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    int nnames, namesize;\r
+    int i;\r
+\r
+    if (!fxp_init()) {\r
+       tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());\r
+       errs++;\r
+       return;\r
+    }\r
+\r
+    printf("Listing directory %s\n", dirname);\r
+\r
+    sftp_register(req = fxp_opendir_send(dirname));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    dirh = fxp_opendir_recv(pktin, rreq);\r
+\r
+    if (dirh == NULL) {\r
+       printf("Unable to open %s: %s\n", dirname, fxp_error());\r
+    } else {\r
+       nnames = namesize = 0;\r
+       ournames = NULL;\r
+\r
+       while (1) {\r
+\r
+           sftp_register(req = fxp_readdir_send(dirh));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           names = fxp_readdir_recv(pktin, rreq);\r
+\r
+           if (names == NULL) {\r
+               if (fxp_error_type() == SSH_FX_EOF)\r
+                   break;\r
+               printf("Reading directory %s: %s\n", dirname, fxp_error());\r
+               break;\r
+           }\r
+           if (names->nnames == 0) {\r
+               fxp_free_names(names);\r
+               break;\r
+           }\r
+\r
+           if (nnames + names->nnames >= namesize) {\r
+               namesize += names->nnames + 128;\r
+               ournames = sresize(ournames, namesize, struct fxp_name);\r
+           }\r
+\r
+           for (i = 0; i < names->nnames; i++)\r
+               ournames[nnames++] = names->names[i];\r
+           names->nnames = 0;         /* prevent free_names */\r
+           fxp_free_names(names);\r
+       }\r
+       sftp_register(req = fxp_close_send(dirh));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       fxp_close_recv(pktin, rreq);\r
+\r
+       /*\r
+        * Now we have our filenames. Sort them by actual file\r
+        * name, and then output the longname parts.\r
+        */\r
+       qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);\r
+\r
+       /*\r
+        * And print them.\r
+        */\r
+       for (i = 0; i < nnames; i++)\r
+           printf("%s\n", ournames[i].longname);\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Helper routines that contain the actual SCP protocol elements,\r
+ * implemented both as SCP1 and SFTP.\r
+ */\r
+\r
+static struct scp_sftp_dirstack {\r
+    struct scp_sftp_dirstack *next;\r
+    struct fxp_name *names;\r
+    int namepos, namelen;\r
+    char *dirpath;\r
+    char *wildcard;\r
+    int matched_something;            /* wildcard match set was non-empty */\r
+} *scp_sftp_dirstack_head;\r
+static char *scp_sftp_remotepath, *scp_sftp_currentname;\r
+static char *scp_sftp_wildcard;\r
+static int scp_sftp_targetisdir, scp_sftp_donethistarget;\r
+static int scp_sftp_preserve, scp_sftp_recursive;\r
+static unsigned long scp_sftp_mtime, scp_sftp_atime;\r
+static int scp_has_times;\r
+static struct fxp_handle *scp_sftp_filehandle;\r
+static struct fxp_xfer *scp_sftp_xfer;\r
+static uint64 scp_sftp_fileoffset;\r
+\r
+int scp_source_setup(char *target, int shouldbedir)\r
+{\r
+    if (using_sftp) {\r
+       /*\r
+        * Find out whether the target filespec is in fact a\r
+        * directory.\r
+        */\r
+       struct sftp_packet *pktin;\r
+       struct sftp_request *req, *rreq;\r
+       struct fxp_attrs attrs;\r
+       int ret;\r
+\r
+       if (!fxp_init()) {\r
+           tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());\r
+           errs++;\r
+           return 1;\r
+       }\r
+\r
+       sftp_register(req = fxp_stat_send(target));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       ret = fxp_stat_recv(pktin, rreq, &attrs);\r
+\r
+       if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS))\r
+           scp_sftp_targetisdir = 0;\r
+       else\r
+           scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0;\r
+\r
+       if (shouldbedir && !scp_sftp_targetisdir) {\r
+           bump("pscp: remote filespec %s: not a directory\n", target);\r
+       }\r
+\r
+       scp_sftp_remotepath = dupstr(target);\r
+\r
+       scp_has_times = 0;\r
+    } else {\r
+       (void) response();\r
+    }\r
+    return 0;\r
+}\r
+\r
+int scp_send_errmsg(char *str)\r
+{\r
+    if (using_sftp) {\r
+       /* do nothing; we never need to send our errors to the server */\r
+    } else {\r
+       back->send(backhandle, "\001", 1);/* scp protocol error prefix */\r
+       back->send(backhandle, str, strlen(str));\r
+    }\r
+    return 0;                         /* can't fail */\r
+}\r
+\r
+int scp_send_filetimes(unsigned long mtime, unsigned long atime)\r
+{\r
+    if (using_sftp) {\r
+       scp_sftp_mtime = mtime;\r
+       scp_sftp_atime = atime;\r
+       scp_has_times = 1;\r
+       return 0;\r
+    } else {\r
+       char buf[80];\r
+       sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);\r
+       back->send(backhandle, buf, strlen(buf));\r
+       return response();\r
+    }\r
+}\r
+\r
+int scp_send_filename(char *name, uint64 size, int modes)\r
+{\r
+    if (using_sftp) {\r
+       char *fullname;\r
+       struct sftp_packet *pktin;\r
+       struct sftp_request *req, *rreq;\r
+\r
+       if (scp_sftp_targetisdir) {\r
+           fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);\r
+       } else {\r
+           fullname = dupstr(scp_sftp_remotepath);\r
+       }\r
+\r
+       sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE |\r
+                                         SSH_FXF_CREAT | SSH_FXF_TRUNC));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       scp_sftp_filehandle = fxp_open_recv(pktin, rreq);\r
+\r
+       if (!scp_sftp_filehandle) {\r
+           tell_user(stderr, "pscp: unable to open %s: %s",\r
+                     fullname, fxp_error());\r
+           errs++;\r
+           return 1;\r
+       }\r
+       scp_sftp_fileoffset = uint64_make(0, 0);\r
+       scp_sftp_xfer = xfer_upload_init(scp_sftp_filehandle,\r
+                                        scp_sftp_fileoffset);\r
+       sfree(fullname);\r
+       return 0;\r
+    } else {\r
+       char buf[40];\r
+       char sizestr[40];\r
+       uint64_decimal(size, sizestr);\r
+       sprintf(buf, "C%04o %s ", modes, sizestr);\r
+       back->send(backhandle, buf, strlen(buf));\r
+       back->send(backhandle, name, strlen(name));\r
+       back->send(backhandle, "\n", 1);\r
+       return response();\r
+    }\r
+}\r
+\r
+int scp_send_filedata(char *data, int len)\r
+{\r
+    if (using_sftp) {\r
+       int ret;\r
+       struct sftp_packet *pktin;\r
+\r
+       if (!scp_sftp_filehandle) {\r
+           return 1;\r
+       }\r
+\r
+       while (!xfer_upload_ready(scp_sftp_xfer)) {\r
+           pktin = sftp_recv();\r
+           ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin);\r
+           if (!ret) {\r
+               tell_user(stderr, "error while writing: %s\n", fxp_error());\r
+               errs++;\r
+               return 1;\r
+           }\r
+       }\r
+\r
+       xfer_upload_data(scp_sftp_xfer, data, len);\r
+\r
+       scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len);\r
+       return 0;\r
+    } else {\r
+       int bufsize = back->send(backhandle, data, len);\r
+\r
+       /*\r
+        * If the network transfer is backing up - that is, the\r
+        * remote site is not accepting data as fast as we can\r
+        * produce it - then we must loop on network events until\r
+        * we have space in the buffer again.\r
+        */\r
+       while (bufsize > MAX_SCP_BUFSIZE) {\r
+           if (ssh_sftp_loop_iteration() < 0)\r
+               return 1;\r
+           bufsize = back->sendbuffer(backhandle);\r
+       }\r
+\r
+       return 0;\r
+    }\r
+}\r
+\r
+int scp_send_finish(void)\r
+{\r
+    if (using_sftp) {\r
+       struct fxp_attrs attrs;\r
+       struct sftp_packet *pktin;\r
+       struct sftp_request *req, *rreq;\r
+       int ret;\r
+\r
+       while (!xfer_done(scp_sftp_xfer)) {\r
+           pktin = sftp_recv();\r
+           xfer_upload_gotpkt(scp_sftp_xfer, pktin);\r
+       }\r
+       xfer_cleanup(scp_sftp_xfer);\r
+\r
+       if (!scp_sftp_filehandle) {\r
+           return 1;\r
+       }\r
+       if (scp_has_times) {\r
+           attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME;\r
+           attrs.atime = scp_sftp_atime;\r
+           attrs.mtime = scp_sftp_mtime;\r
+           sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           ret = fxp_fsetstat_recv(pktin, rreq);\r
+           if (!ret) {\r
+               tell_user(stderr, "unable to set file times: %s\n", fxp_error());\r
+               errs++;\r
+           }\r
+       }\r
+       sftp_register(req = fxp_close_send(scp_sftp_filehandle));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       fxp_close_recv(pktin, rreq);\r
+       scp_has_times = 0;\r
+       return 0;\r
+    } else {\r
+       back->send(backhandle, "", 1);\r
+       return response();\r
+    }\r
+}\r
+\r
+char *scp_save_remotepath(void)\r
+{\r
+    if (using_sftp)\r
+       return scp_sftp_remotepath;\r
+    else\r
+       return NULL;\r
+}\r
+\r
+void scp_restore_remotepath(char *data)\r
+{\r
+    if (using_sftp)\r
+       scp_sftp_remotepath = data;\r
+}\r
+\r
+int scp_send_dirname(char *name, int modes)\r
+{\r
+    if (using_sftp) {\r
+       char *fullname;\r
+       char const *err;\r
+       struct fxp_attrs attrs;\r
+       struct sftp_packet *pktin;\r
+       struct sftp_request *req, *rreq;\r
+       int ret;\r
+\r
+       if (scp_sftp_targetisdir) {\r
+           fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);\r
+       } else {\r
+           fullname = dupstr(scp_sftp_remotepath);\r
+       }\r
+\r
+       /*\r
+        * We don't worry about whether we managed to create the\r
+        * directory, because if it exists already it's OK just to\r
+        * use it. Instead, we will stat it afterwards, and if it\r
+        * exists and is a directory we will assume we were either\r
+        * successful or it didn't matter.\r
+        */\r
+       sftp_register(req = fxp_mkdir_send(fullname));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       ret = fxp_mkdir_recv(pktin, rreq);\r
+\r
+       if (!ret)\r
+           err = fxp_error();\r
+       else\r
+           err = "server reported no error";\r
+\r
+       sftp_register(req = fxp_stat_send(fullname));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       ret = fxp_stat_recv(pktin, rreq, &attrs);\r
+\r
+       if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||\r
+           !(attrs.permissions & 0040000)) {\r
+           tell_user(stderr, "unable to create directory %s: %s",\r
+                     fullname, err);\r
+           errs++;\r
+           return 1;\r
+       }\r
+\r
+       scp_sftp_remotepath = fullname;\r
+\r
+       return 0;\r
+    } else {\r
+       char buf[40];\r
+       sprintf(buf, "D%04o 0 ", modes);\r
+       back->send(backhandle, buf, strlen(buf));\r
+       back->send(backhandle, name, strlen(name));\r
+       back->send(backhandle, "\n", 1);\r
+       return response();\r
+    }\r
+}\r
+\r
+int scp_send_enddir(void)\r
+{\r
+    if (using_sftp) {\r
+       sfree(scp_sftp_remotepath);\r
+       return 0;\r
+    } else {\r
+       back->send(backhandle, "E\n", 2);\r
+       return response();\r
+    }\r
+}\r
+\r
+/*\r
+ * Yes, I know; I have an scp_sink_setup _and_ an scp_sink_init.\r
+ * That's bad. The difference is that scp_sink_setup is called once\r
+ * right at the start, whereas scp_sink_init is called to\r
+ * initialise every level of recursion in the protocol.\r
+ */\r
+int scp_sink_setup(char *source, int preserve, int recursive)\r
+{\r
+    if (using_sftp) {\r
+       char *newsource;\r
+\r
+       if (!fxp_init()) {\r
+           tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());\r
+           errs++;\r
+           return 1;\r
+       }\r
+       /*\r
+        * It's possible that the source string we've been given\r
+        * contains a wildcard. If so, we must split the directory\r
+        * away from the wildcard itself (throwing an error if any\r
+        * wildcardness comes before the final slash) and arrange\r
+        * things so that a dirstack entry will be set up.\r
+        */\r
+       newsource = snewn(1+strlen(source), char);\r
+       if (!wc_unescape(newsource, source)) {\r
+           /* Yes, here we go; it's a wildcard. Bah. */\r
+           char *dupsource, *lastpart, *dirpart, *wildcard;\r
+           dupsource = dupstr(source);\r
+           lastpart = stripslashes(dupsource, 0);\r
+           wildcard = dupstr(lastpart);\r
+           *lastpart = '\0';\r
+           if (*dupsource && dupsource[1]) {\r
+               /*\r
+                * The remains of dupsource are at least two\r
+                * characters long, meaning the pathname wasn't\r
+                * empty or just `/'. Hence, we remove the trailing\r
+                * slash.\r
+                */\r
+               lastpart[-1] = '\0';\r
+           } else if (!*dupsource) {\r
+               /*\r
+                * The remains of dupsource are _empty_ - the whole\r
+                * pathname was a wildcard. Hence we need to\r
+                * replace it with ".".\r
+                */\r
+               sfree(dupsource);\r
+               dupsource = dupstr(".");\r
+           }\r
+\r
+           /*\r
+            * Now we have separated our string into dupsource (the\r
+            * directory part) and wildcard. Both of these will\r
+            * need freeing at some point. Next step is to remove\r
+            * wildcard escapes from the directory part, throwing\r
+            * an error if it contains a real wildcard.\r
+            */\r
+           dirpart = snewn(1+strlen(dupsource), char);\r
+           if (!wc_unescape(dirpart, dupsource)) {\r
+               tell_user(stderr, "%s: multiple-level wildcards unsupported",\r
+                         source);\r
+               errs++;\r
+               sfree(dirpart);\r
+               sfree(wildcard);\r
+               sfree(dupsource);\r
+               return 1;\r
+           }\r
+\r
+           /*\r
+            * Now we have dirpart (unescaped, ie a valid remote\r
+            * path), and wildcard (a wildcard). This will be\r
+            * sufficient to arrange a dirstack entry.\r
+            */\r
+           scp_sftp_remotepath = dirpart;\r
+           scp_sftp_wildcard = wildcard;\r
+           sfree(dupsource);\r
+       } else {\r
+           scp_sftp_remotepath = newsource;\r
+           scp_sftp_wildcard = NULL;\r
+       }\r
+       scp_sftp_preserve = preserve;\r
+       scp_sftp_recursive = recursive;\r
+       scp_sftp_donethistarget = 0;\r
+       scp_sftp_dirstack_head = NULL;\r
+    }\r
+    return 0;\r
+}\r
+\r
+int scp_sink_init(void)\r
+{\r
+    if (!using_sftp) {\r
+       back->send(backhandle, "", 1);\r
+    }\r
+    return 0;\r
+}\r
+\r
+#define SCP_SINK_FILE   1\r
+#define SCP_SINK_DIR    2\r
+#define SCP_SINK_ENDDIR 3\r
+#define SCP_SINK_RETRY  4             /* not an action; just try again */\r
+struct scp_sink_action {\r
+    int action;                               /* FILE, DIR, ENDDIR */\r
+    char *buf;                        /* will need freeing after use */\r
+    char *name;                               /* filename or dirname (not ENDDIR) */\r
+    int mode;                         /* access mode (not ENDDIR) */\r
+    uint64 size;                      /* file size (not ENDDIR) */\r
+    int settime;                      /* 1 if atime and mtime are filled */\r
+    unsigned long atime, mtime;               /* access times for the file */\r
+};\r
+\r
+int scp_get_sink_action(struct scp_sink_action *act)\r
+{\r
+    if (using_sftp) {\r
+       char *fname;\r
+       int must_free_fname;\r
+       struct fxp_attrs attrs;\r
+       struct sftp_packet *pktin;\r
+       struct sftp_request *req, *rreq;\r
+       int ret;\r
+\r
+       if (!scp_sftp_dirstack_head) {\r
+           if (!scp_sftp_donethistarget) {\r
+               /*\r
+                * Simple case: we are only dealing with one file.\r
+                */\r
+               fname = scp_sftp_remotepath;\r
+               must_free_fname = 0;\r
+               scp_sftp_donethistarget = 1;\r
+           } else {\r
+               /*\r
+                * Even simpler case: one file _which we've done_.\r
+                * Return 1 (finished).\r
+                */\r
+               return 1;\r
+           }\r
+       } else {\r
+           /*\r
+            * We're now in the middle of stepping through a list\r
+            * of names returned from fxp_readdir(); so let's carry\r
+            * on.\r
+            */\r
+           struct scp_sftp_dirstack *head = scp_sftp_dirstack_head;\r
+           while (head->namepos < head->namelen &&\r
+                  (is_dots(head->names[head->namepos].filename) ||\r
+                   (head->wildcard &&\r
+                    !wc_match(head->wildcard,\r
+                              head->names[head->namepos].filename))))\r
+               head->namepos++;       /* skip . and .. */\r
+           if (head->namepos < head->namelen) {\r
+               head->matched_something = 1;\r
+               fname = dupcat(head->dirpath, "/",\r
+                              head->names[head->namepos++].filename,\r
+                              NULL);\r
+               must_free_fname = 1;\r
+           } else {\r
+               /*\r
+                * We've come to the end of the list; pop it off\r
+                * the stack and return an ENDDIR action (or RETRY\r
+                * if this was a wildcard match).\r
+                */\r
+               if (head->wildcard) {\r
+                   act->action = SCP_SINK_RETRY;\r
+                   if (!head->matched_something) {\r
+                       tell_user(stderr, "pscp: wildcard '%s' matched "\r
+                                 "no files", head->wildcard);\r
+                       errs++;\r
+                   }\r
+                   sfree(head->wildcard);\r
+\r
+               } else {\r
+                   act->action = SCP_SINK_ENDDIR;\r
+               }\r
+\r
+               sfree(head->dirpath);\r
+               sfree(head->names);\r
+               scp_sftp_dirstack_head = head->next;\r
+               sfree(head);\r
+\r
+               return 0;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Now we have a filename. Stat it, and see if it's a file\r
+        * or a directory.\r
+        */\r
+       sftp_register(req = fxp_stat_send(fname));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       ret = fxp_stat_recv(pktin, rreq, &attrs);\r
+\r
+       if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {\r
+           tell_user(stderr, "unable to identify %s: %s", fname,\r
+                     ret ? "file type not supplied" : fxp_error());\r
+           errs++;\r
+           return 1;\r
+       }\r
+\r
+       if (attrs.permissions & 0040000) {\r
+           struct scp_sftp_dirstack *newitem;\r
+           struct fxp_handle *dirhandle;\r
+           int nnames, namesize;\r
+           struct fxp_name *ournames;\r
+           struct fxp_names *names;\r
+\r
+           /*\r
+            * It's a directory. If we're not in recursive mode,\r
+            * this merits a complaint (which is fatal if the name\r
+            * was specified directly, but not if it was matched by\r
+            * a wildcard).\r
+            * \r
+            * We skip this complaint completely if\r
+            * scp_sftp_wildcard is set, because that's an\r
+            * indication that we're not actually supposed to\r
+            * _recursively_ transfer the dir, just scan it for\r
+            * things matching the wildcard.\r
+            */\r
+           if (!scp_sftp_recursive && !scp_sftp_wildcard) {\r
+               tell_user(stderr, "pscp: %s: is a directory", fname);\r
+               errs++;\r
+               if (must_free_fname) sfree(fname);\r
+               if (scp_sftp_dirstack_head) {\r
+                   act->action = SCP_SINK_RETRY;\r
+                   return 0;\r
+               } else {\r
+                   return 1;\r
+               }\r
+           }\r
+\r
+           /*\r
+            * Otherwise, the fun begins. We must fxp_opendir() the\r
+            * directory, slurp the filenames into memory, return\r
+            * SCP_SINK_DIR (unless this is a wildcard match), and\r
+            * set targetisdir. The next time we're called, we will\r
+            * run through the list of filenames one by one,\r
+            * matching them against a wildcard if present.\r
+            * \r
+            * If targetisdir is _already_ set (meaning we're\r
+            * already in the middle of going through another such\r
+            * list), we must push the other (target,namelist) pair\r
+            * on a stack.\r
+            */\r
+           sftp_register(req = fxp_opendir_send(fname));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           dirhandle = fxp_opendir_recv(pktin, rreq);\r
+\r
+           if (!dirhandle) {\r
+               tell_user(stderr, "scp: unable to open directory %s: %s",\r
+                         fname, fxp_error());\r
+               if (must_free_fname) sfree(fname);\r
+               errs++;\r
+               return 1;\r
+           }\r
+           nnames = namesize = 0;\r
+           ournames = NULL;\r
+           while (1) {\r
+               int i;\r
+\r
+               sftp_register(req = fxp_readdir_send(dirhandle));\r
+               rreq = sftp_find_request(pktin = sftp_recv());\r
+               assert(rreq == req);\r
+               names = fxp_readdir_recv(pktin, rreq);\r
+\r
+               if (names == NULL) {\r
+                   if (fxp_error_type() == SSH_FX_EOF)\r
+                       break;\r
+                   tell_user(stderr, "scp: reading directory %s: %s\n",\r
+                             fname, fxp_error());\r
+                   if (must_free_fname) sfree(fname);\r
+                   sfree(ournames);\r
+                   errs++;\r
+                   return 1;\r
+               }\r
+               if (names->nnames == 0) {\r
+                   fxp_free_names(names);\r
+                   break;\r
+               }\r
+               if (nnames + names->nnames >= namesize) {\r
+                   namesize += names->nnames + 128;\r
+                   ournames = sresize(ournames, namesize, struct fxp_name);\r
+               }\r
+               for (i = 0; i < names->nnames; i++) {\r
+                   if (!strcmp(names->names[i].filename, ".") ||\r
+                       !strcmp(names->names[i].filename, "..")) {\r
+                       /*\r
+                        * . and .. are normal consequences of\r
+                        * reading a directory, and aren't worth\r
+                        * complaining about.\r
+                        */\r
+                   } else if (!vet_filename(names->names[i].filename)) {\r
+                       tell_user(stderr, "ignoring potentially dangerous server-"\r
+                                 "supplied filename '%s'\n",\r
+                                 names->names[i].filename);\r
+                   } else\r
+                       ournames[nnames++] = names->names[i];\r
+               }\r
+               names->nnames = 0;             /* prevent free_names */\r
+               fxp_free_names(names);\r
+           }\r
+           sftp_register(req = fxp_close_send(dirhandle));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           fxp_close_recv(pktin, rreq);\r
+\r
+           newitem = snew(struct scp_sftp_dirstack);\r
+           newitem->next = scp_sftp_dirstack_head;\r
+           newitem->names = ournames;\r
+           newitem->namepos = 0;\r
+           newitem->namelen = nnames;\r
+           if (must_free_fname)\r
+               newitem->dirpath = fname;\r
+           else\r
+               newitem->dirpath = dupstr(fname);\r
+           if (scp_sftp_wildcard) {\r
+               newitem->wildcard = scp_sftp_wildcard;\r
+               newitem->matched_something = 0;\r
+               scp_sftp_wildcard = NULL;\r
+           } else {\r
+               newitem->wildcard = NULL;\r
+           }\r
+           scp_sftp_dirstack_head = newitem;\r
+\r
+           if (newitem->wildcard) {\r
+               act->action = SCP_SINK_RETRY;\r
+           } else {\r
+               act->action = SCP_SINK_DIR;\r
+               act->buf = dupstr(stripslashes(fname, 0));\r
+               act->name = act->buf;\r
+               act->size = uint64_make(0,0);     /* duhh, it's a directory */\r
+               act->mode = 07777 & attrs.permissions;\r
+               if (scp_sftp_preserve &&\r
+                   (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {\r
+                   act->atime = attrs.atime;\r
+                   act->mtime = attrs.mtime;\r
+                   act->settime = 1;\r
+               } else\r
+                   act->settime = 0;\r
+           }\r
+           return 0;\r
+\r
+       } else {\r
+           /*\r
+            * It's a file. Return SCP_SINK_FILE.\r
+            */\r
+           act->action = SCP_SINK_FILE;\r
+           act->buf = dupstr(stripslashes(fname, 0));\r
+           act->name = act->buf;\r
+           if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {\r
+               act->size = attrs.size;\r
+           } else\r
+               act->size = uint64_make(ULONG_MAX,ULONG_MAX);   /* no idea */\r
+           act->mode = 07777 & attrs.permissions;\r
+           if (scp_sftp_preserve &&\r
+               (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {\r
+               act->atime = attrs.atime;\r
+               act->mtime = attrs.mtime;\r
+               act->settime = 1;\r
+           } else\r
+               act->settime = 0;\r
+           if (must_free_fname)\r
+               scp_sftp_currentname = fname;\r
+           else\r
+               scp_sftp_currentname = dupstr(fname);\r
+           return 0;\r
+       }\r
+\r
+    } else {\r
+       int done = 0;\r
+       int i, bufsize;\r
+       int action;\r
+       char ch;\r
+\r
+       act->settime = 0;\r
+       act->buf = NULL;\r
+       bufsize = 0;\r
+\r
+       while (!done) {\r
+           if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)\r
+               return 1;\r
+           if (ch == '\n')\r
+               bump("Protocol error: Unexpected newline");\r
+           i = 0;\r
+           action = ch;\r
+           do {\r
+               if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)\r
+                   bump("Lost connection");\r
+               if (i >= bufsize) {\r
+                   bufsize = i + 128;\r
+                   act->buf = sresize(act->buf, bufsize, char);\r
+               }\r
+               act->buf[i++] = ch;\r
+           } while (ch != '\n');\r
+           act->buf[i - 1] = '\0';\r
+           switch (action) {\r
+             case '\01':                      /* error */\r
+               tell_user(stderr, "%s\n", act->buf);\r
+               errs++;\r
+               continue;                      /* go round again */\r
+             case '\02':                      /* fatal error */\r
+               bump("%s", act->buf);\r
+             case 'E':\r
+               back->send(backhandle, "", 1);\r
+               act->action = SCP_SINK_ENDDIR;\r
+               return 0;\r
+             case 'T':\r
+               if (sscanf(act->buf, "%ld %*d %ld %*d",\r
+                          &act->mtime, &act->atime) == 2) {\r
+                   act->settime = 1;\r
+                   back->send(backhandle, "", 1);\r
+                   continue;          /* go round again */\r
+               }\r
+               bump("Protocol error: Illegal time format");\r
+             case 'C':\r
+             case 'D':\r
+               act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);\r
+               break;\r
+             default:\r
+               bump("Protocol error: Expected control record");\r
+           }\r
+           /*\r
+            * We will go round this loop only once, unless we hit\r
+            * `continue' above.\r
+            */\r
+           done = 1;\r
+       }\r
+\r
+       /*\r
+        * If we get here, we must have seen SCP_SINK_FILE or\r
+        * SCP_SINK_DIR.\r
+        */\r
+       {\r
+           char sizestr[40];\r
+       \r
+           if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2)\r
+               bump("Protocol error: Illegal file descriptor format");\r
+           act->size = uint64_from_decimal(sizestr);\r
+           act->name = act->buf + i;\r
+           return 0;\r
+       }\r
+    }\r
+}\r
+\r
+int scp_accept_filexfer(void)\r
+{\r
+    if (using_sftp) {\r
+       struct sftp_packet *pktin;\r
+       struct sftp_request *req, *rreq;\r
+\r
+       sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       scp_sftp_filehandle = fxp_open_recv(pktin, rreq);\r
+\r
+       if (!scp_sftp_filehandle) {\r
+           tell_user(stderr, "pscp: unable to open %s: %s",\r
+                     scp_sftp_currentname, fxp_error());\r
+           errs++;\r
+           return 1;\r
+       }\r
+       scp_sftp_fileoffset = uint64_make(0, 0);\r
+       scp_sftp_xfer = xfer_download_init(scp_sftp_filehandle,\r
+                                          scp_sftp_fileoffset);\r
+       sfree(scp_sftp_currentname);\r
+       return 0;\r
+    } else {\r
+       back->send(backhandle, "", 1);\r
+       return 0;                      /* can't fail */\r
+    }\r
+}\r
+\r
+int scp_recv_filedata(char *data, int len)\r
+{\r
+    if (using_sftp) {\r
+       struct sftp_packet *pktin;\r
+       int ret, actuallen;\r
+       void *vbuf;\r
+\r
+       xfer_download_queue(scp_sftp_xfer);\r
+       pktin = sftp_recv();\r
+       ret = xfer_download_gotpkt(scp_sftp_xfer, pktin);\r
+\r
+       if (ret < 0) {\r
+           tell_user(stderr, "pscp: error while reading: %s", fxp_error());\r
+           errs++;\r
+           return -1;\r
+       }\r
+\r
+       if (xfer_download_data(scp_sftp_xfer, &vbuf, &actuallen)) {\r
+           /*\r
+            * This assertion relies on the fact that the natural\r
+            * block size used in the xfer manager is at most that\r
+            * used in this module. I don't like crossing layers in\r
+            * this way, but it'll do for now.\r
+            */\r
+           assert(actuallen <= len);\r
+           memcpy(data, vbuf, actuallen);\r
+           sfree(vbuf);\r
+       } else\r
+           actuallen = 0;\r
+\r
+       scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, actuallen);\r
+\r
+       return actuallen;\r
+    } else {\r
+       return ssh_scp_recv((unsigned char *) data, len);\r
+    }\r
+}\r
+\r
+int scp_finish_filerecv(void)\r
+{\r
+    if (using_sftp) {\r
+       struct sftp_packet *pktin;\r
+       struct sftp_request *req, *rreq;\r
+\r
+       /*\r
+        * Ensure that xfer_done() will work correctly, so we can\r
+        * clean up any outstanding requests from the file\r
+        * transfer.\r
+        */\r
+       xfer_set_error(scp_sftp_xfer);\r
+       while (!xfer_done(scp_sftp_xfer)) {\r
+           void *vbuf;\r
+           int len;\r
+\r
+           pktin = sftp_recv();\r
+           xfer_download_gotpkt(scp_sftp_xfer, pktin);\r
+           if (xfer_download_data(scp_sftp_xfer, &vbuf, &len))\r
+               sfree(vbuf);\r
+       }\r
+       xfer_cleanup(scp_sftp_xfer);\r
+\r
+       sftp_register(req = fxp_close_send(scp_sftp_filehandle));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       fxp_close_recv(pktin, rreq);\r
+       return 0;\r
+    } else {\r
+       back->send(backhandle, "", 1);\r
+       return response();\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ *  Send an error message to the other side and to the screen.\r
+ *  Increment error counter.\r
+ */\r
+static void run_err(const char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    errs++;\r
+    str = dupvprintf(fmt, ap);\r
+    str2 = dupcat("scp: ", str, "\n", NULL);\r
+    sfree(str);\r
+    scp_send_errmsg(str2);\r
+    tell_user(stderr, "%s", str2);\r
+    va_end(ap);\r
+    sfree(str2);\r
+}\r
+\r
+/*\r
+ *  Execute the source part of the SCP protocol.\r
+ */\r
+static void source(char *src)\r
+{\r
+    uint64 size;\r
+    unsigned long mtime, atime;\r
+    char *last;\r
+    RFile *f;\r
+    int attr;\r
+    uint64 i;\r
+    uint64 stat_bytes;\r
+    time_t stat_starttime, stat_lasttime;\r
+\r
+    attr = file_type(src);\r
+    if (attr == FILE_TYPE_NONEXISTENT ||\r
+       attr == FILE_TYPE_WEIRD) {\r
+       run_err("%s: %s file or directory", src,\r
+               (attr == FILE_TYPE_WEIRD ? "Not a" : "No such"));\r
+       return;\r
+    }\r
+\r
+    if (attr == FILE_TYPE_DIRECTORY) {\r
+       if (recursive) {\r
+           /*\r
+            * Avoid . and .. directories.\r
+            */\r
+           char *p;\r
+           p = strrchr(src, '/');\r
+           if (!p)\r
+               p = strrchr(src, '\\');\r
+           if (!p)\r
+               p = src;\r
+           else\r
+               p++;\r
+           if (!strcmp(p, ".") || !strcmp(p, ".."))\r
+               /* skip . and .. */ ;\r
+           else\r
+               rsource(src);\r
+       } else {\r
+           run_err("%s: not a regular file", src);\r
+       }\r
+       return;\r
+    }\r
+\r
+    if ((last = strrchr(src, '/')) == NULL)\r
+       last = src;\r
+    else\r
+       last++;\r
+    if (strrchr(last, '\\') != NULL)\r
+       last = strrchr(last, '\\') + 1;\r
+    if (last == src && strchr(src, ':') != NULL)\r
+       last = strchr(src, ':') + 1;\r
+\r
+    f = open_existing_file(src, &size, &mtime, &atime);\r
+    if (f == NULL) {\r
+       run_err("%s: Cannot open file", src);\r
+       return;\r
+    }\r
+    if (preserve) {\r
+       if (scp_send_filetimes(mtime, atime))\r
+           return;\r
+    }\r
+\r
+    if (verbose) {\r
+       char sizestr[40];\r
+       uint64_decimal(size, sizestr);\r
+       tell_user(stderr, "Sending file %s, size=%s", last, sizestr);\r
+    }\r
+    if (scp_send_filename(last, size, 0644))\r
+       return;\r
+\r
+    stat_bytes = uint64_make(0,0);\r
+    stat_starttime = time(NULL);\r
+    stat_lasttime = 0;\r
+\r
+    for (i = uint64_make(0,0);\r
+        uint64_compare(i,size) < 0;\r
+        i = uint64_add32(i,4096)) {\r
+       char transbuf[4096];\r
+       int j, k = 4096;\r
+\r
+       if (uint64_compare(uint64_add32(i, k),size) > 0) /* i + k > size */ \r
+           k = (uint64_subtract(size, i)).lo;  /* k = size - i; */\r
+       if ((j = read_from_file(f, transbuf, k)) != k) {\r
+           if (statistics)\r
+               printf("\n");\r
+           bump("%s: Read error", src);\r
+       }\r
+       if (scp_send_filedata(transbuf, k))\r
+           bump("%s: Network error occurred", src);\r
+\r
+       if (statistics) {\r
+           stat_bytes = uint64_add32(stat_bytes, k);\r
+           if (time(NULL) != stat_lasttime ||\r
+               (uint64_compare(uint64_add32(i, k), size) == 0)) {\r
+               stat_lasttime = time(NULL);\r
+               print_stats(last, size, stat_bytes,\r
+                           stat_starttime, stat_lasttime);\r
+           }\r
+       }\r
+\r
+    }\r
+    close_rfile(f);\r
+\r
+    (void) scp_send_finish();\r
+}\r
+\r
+/*\r
+ *  Recursively send the contents of a directory.\r
+ */\r
+static void rsource(char *src)\r
+{\r
+    char *last;\r
+    char *save_target;\r
+    DirHandle *dir;\r
+\r
+    if ((last = strrchr(src, '/')) == NULL)\r
+       last = src;\r
+    else\r
+       last++;\r
+    if (strrchr(last, '\\') != NULL)\r
+       last = strrchr(last, '\\') + 1;\r
+    if (last == src && strchr(src, ':') != NULL)\r
+       last = strchr(src, ':') + 1;\r
+\r
+    /* maybe send filetime */\r
+\r
+    save_target = scp_save_remotepath();\r
+\r
+    if (verbose)\r
+       tell_user(stderr, "Entering directory: %s", last);\r
+    if (scp_send_dirname(last, 0755))\r
+       return;\r
+\r
+    dir = open_directory(src);\r
+    if (dir != NULL) {\r
+       char *filename;\r
+       while ((filename = read_filename(dir)) != NULL) {\r
+           char *foundfile = dupcat(src, "/", filename, NULL);\r
+           source(foundfile);\r
+           sfree(foundfile);\r
+           sfree(filename);\r
+       }\r
+    }\r
+    close_directory(dir);\r
+\r
+    (void) scp_send_enddir();\r
+\r
+    scp_restore_remotepath(save_target);\r
+}\r
+\r
+/*\r
+ * Execute the sink part of the SCP protocol.\r
+ */\r
+static void sink(char *targ, char *src)\r
+{\r
+    char *destfname;\r
+    int targisdir = 0;\r
+    int exists;\r
+    int attr;\r
+    WFile *f;\r
+    uint64 received;\r
+    int wrerror = 0;\r
+    uint64 stat_bytes;\r
+    time_t stat_starttime, stat_lasttime;\r
+    char *stat_name;\r
+\r
+    attr = file_type(targ);\r
+    if (attr == FILE_TYPE_DIRECTORY)\r
+       targisdir = 1;\r
+\r
+    if (targetshouldbedirectory && !targisdir)\r
+       bump("%s: Not a directory", targ);\r
+\r
+    scp_sink_init();\r
+    while (1) {\r
+       struct scp_sink_action act;\r
+       if (scp_get_sink_action(&act))\r
+           return;\r
+\r
+       if (act.action == SCP_SINK_ENDDIR)\r
+           return;\r
+\r
+       if (act.action == SCP_SINK_RETRY)\r
+           continue;\r
+\r
+       if (targisdir) {\r
+           /*\r
+            * Prevent the remote side from maliciously writing to\r
+            * files outside the target area by sending a filename\r
+            * containing `../'. In fact, it shouldn't be sending\r
+            * filenames with any slashes or colons in at all; so\r
+            * we'll find the last slash, backslash or colon in the\r
+            * filename and use only the part after that. (And\r
+            * warn!)\r
+            * \r
+            * In addition, we also ensure here that if we're\r
+            * copying a single file and the target is a directory\r
+            * (common usage: `pscp host:filename .') the remote\r
+            * can't send us a _different_ file name. We can\r
+            * distinguish this case because `src' will be non-NULL\r
+            * and the last component of that will fail to match\r
+            * (the last component of) the name sent.\r
+            * \r
+            * Well, not always; if `src' is a wildcard, we do\r
+            * expect to get back filenames that don't correspond\r
+            * exactly to it. Ideally in this case, we would like\r
+            * to ensure that the returned filename actually\r
+            * matches the wildcard pattern - but one of SCP's\r
+            * protocol infelicities is that wildcard matching is\r
+            * done at the server end _by the server's rules_ and\r
+            * so in general this is infeasible. Hence, we only\r
+            * accept filenames that don't correspond to `src' if\r
+            * unsafe mode is enabled or we are using SFTP (which\r
+            * resolves remote wildcards on the client side and can\r
+            * be trusted).\r
+            */\r
+           char *striptarget, *stripsrc;\r
+\r
+           striptarget = stripslashes(act.name, 1);\r
+           if (striptarget != act.name) {\r
+               tell_user(stderr, "warning: remote host sent a compound"\r
+                         " pathname '%s'", act.name);\r
+               tell_user(stderr, "         renaming local file to '%s'",\r
+                          striptarget);\r
+           }\r
+\r
+           /*\r
+            * Also check to see if the target filename is '.' or\r
+            * '..', or indeed '...' and so on because Windows\r
+            * appears to interpret those like '..'.\r
+            */\r
+           if (is_dots(striptarget)) {\r
+               bump("security violation: remote host attempted to write to"\r
+                    " a '.' or '..' path!");\r
+           }\r
+\r
+           if (src) {\r
+               stripsrc = stripslashes(src, 1);\r
+               if (strcmp(striptarget, stripsrc) &&\r
+                   !using_sftp && !scp_unsafe_mode) {\r
+                   tell_user(stderr, "warning: remote host tried to write "\r
+                             "to a file called '%s'", striptarget);\r
+                   tell_user(stderr, "         when we requested a file "\r
+                             "called '%s'.", stripsrc);\r
+                   tell_user(stderr, "         If this is a wildcard, "\r
+                             "consider upgrading to SSH-2 or using");\r
+                   tell_user(stderr, "         the '-unsafe' option. Renaming"\r
+                             " of this file has been disallowed.");\r
+                   /* Override the name the server provided with our own. */\r
+                   striptarget = stripsrc;\r
+               }\r
+           }\r
+\r
+           if (targ[0] != '\0')\r
+               destfname = dir_file_cat(targ, striptarget);\r
+           else\r
+               destfname = dupstr(striptarget);\r
+       } else {\r
+           /*\r
+            * In this branch of the if, the target area is a\r
+            * single file with an explicitly specified name in any\r
+            * case, so there's no danger.\r
+            */\r
+           destfname = dupstr(targ);\r
+       }\r
+       attr = file_type(destfname);\r
+       exists = (attr != FILE_TYPE_NONEXISTENT);\r
+\r
+       if (act.action == SCP_SINK_DIR) {\r
+           if (exists && attr != FILE_TYPE_DIRECTORY) {\r
+               run_err("%s: Not a directory", destfname);\r
+               continue;\r
+           }\r
+           if (!exists) {\r
+               if (!create_directory(destfname)) {\r
+                   run_err("%s: Cannot create directory", destfname);\r
+                   continue;\r
+               }\r
+           }\r
+           sink(destfname, NULL);\r
+           /* can we set the timestamp for directories ? */\r
+           continue;\r
+       }\r
+\r
+       f = open_new_file(destfname);\r
+       if (f == NULL) {\r
+           run_err("%s: Cannot create file", destfname);\r
+           continue;\r
+       }\r
+\r
+       if (scp_accept_filexfer())\r
+           return;\r
+\r
+       stat_bytes = uint64_make(0, 0);\r
+       stat_starttime = time(NULL);\r
+       stat_lasttime = 0;\r
+       stat_name = stripslashes(destfname, 1);\r
+\r
+       received = uint64_make(0, 0);\r
+       while (uint64_compare(received,act.size) < 0) {\r
+           char transbuf[32768];\r
+           uint64 blksize;\r
+           int read;\r
+           blksize = uint64_make(0, 32768);\r
+           if (uint64_compare(blksize,uint64_subtract(act.size,received)) > 0)\r
+             blksize = uint64_subtract(act.size,received);\r
+           read = scp_recv_filedata(transbuf, (int)blksize.lo);\r
+           if (read <= 0)\r
+               bump("Lost connection");\r
+           if (wrerror)\r
+               continue;\r
+           if (write_to_file(f, transbuf, read) != (int)read) {\r
+               wrerror = 1;\r
+               /* FIXME: in sftp we can actually abort the transfer */\r
+               if (statistics)\r
+                   printf("\r%-25.25s | %50s\n",\r
+                          stat_name,\r
+                          "Write error.. waiting for end of file");\r
+               continue;\r
+           }\r
+           if (statistics) {\r
+               stat_bytes = uint64_add32(stat_bytes,read);\r
+               if (time(NULL) > stat_lasttime ||\r
+                   uint64_compare(uint64_add32(received, read), act.size) == 0) {\r
+                   stat_lasttime = time(NULL);\r
+                   print_stats(stat_name, act.size, stat_bytes,\r
+                               stat_starttime, stat_lasttime);\r
+               }\r
+           }\r
+           received = uint64_add32(received, read);\r
+       }\r
+       if (act.settime) {\r
+           set_file_times(f, act.mtime, act.atime);\r
+       }\r
+\r
+       close_wfile(f);\r
+       if (wrerror) {\r
+           run_err("%s: Write error", destfname);\r
+           continue;\r
+       }\r
+       (void) scp_finish_filerecv();\r
+       sfree(destfname);\r
+       sfree(act.buf);\r
+    }\r
+}\r
+\r
+/*\r
+ * We will copy local files to a remote server.\r
+ */\r
+static void toremote(int argc, char *argv[])\r
+{\r
+    char *src, *targ, *host, *user;\r
+    char *cmd;\r
+    int i, wc_type;\r
+\r
+    targ = argv[argc - 1];\r
+\r
+    /* Separate host from filename */\r
+    host = targ;\r
+    targ = colon(targ);\r
+    if (targ == NULL)\r
+       bump("targ == NULL in toremote()");\r
+    *targ++ = '\0';\r
+    if (*targ == '\0')\r
+       targ = ".";\r
+    /* Substitute "." for empty target */\r
+\r
+    /* Separate host and username */\r
+    user = host;\r
+    host = strrchr(host, '@');\r
+    if (host == NULL) {\r
+       host = user;\r
+       user = NULL;\r
+    } else {\r
+       *host++ = '\0';\r
+       if (*user == '\0')\r
+           user = NULL;\r
+    }\r
+\r
+    if (argc == 2) {\r
+       if (colon(argv[0]) != NULL)\r
+           bump("%s: Remote to remote not supported", argv[0]);\r
+\r
+       wc_type = test_wildcard(argv[0], 1);\r
+       if (wc_type == WCTYPE_NONEXISTENT)\r
+           bump("%s: No such file or directory\n", argv[0]);\r
+       else if (wc_type == WCTYPE_WILDCARD)\r
+           targetshouldbedirectory = 1;\r
+    }\r
+\r
+    cmd = dupprintf("scp%s%s%s%s -t %s",\r
+                   verbose ? " -v" : "",\r
+                   recursive ? " -r" : "",\r
+                   preserve ? " -p" : "",\r
+                   targetshouldbedirectory ? " -d" : "", targ);\r
+    do_cmd(host, user, cmd);\r
+    sfree(cmd);\r
+\r
+    if (scp_source_setup(targ, targetshouldbedirectory))\r
+       return;\r
+\r
+    for (i = 0; i < argc - 1; i++) {\r
+       src = argv[i];\r
+       if (colon(src) != NULL) {\r
+           tell_user(stderr, "%s: Remote to remote not supported\n", src);\r
+           errs++;\r
+           continue;\r
+       }\r
+\r
+       wc_type = test_wildcard(src, 1);\r
+       if (wc_type == WCTYPE_NONEXISTENT) {\r
+           run_err("%s: No such file or directory", src);\r
+           continue;\r
+       } else if (wc_type == WCTYPE_FILENAME) {\r
+           source(src);\r
+           continue;\r
+       } else {\r
+           WildcardMatcher *wc;\r
+           char *filename;\r
+\r
+           wc = begin_wildcard_matching(src);\r
+           if (wc == NULL) {\r
+               run_err("%s: No such file or directory", src);\r
+               continue;\r
+           }\r
+\r
+           while ((filename = wildcard_get_filename(wc)) != NULL) {\r
+               source(filename);\r
+               sfree(filename);\r
+           }\r
+\r
+           finish_wildcard_matching(wc);\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ *  We will copy files from a remote server to the local machine.\r
+ */\r
+static void tolocal(int argc, char *argv[])\r
+{\r
+    char *src, *targ, *host, *user;\r
+    char *cmd;\r
+\r
+    if (argc != 2)\r
+       bump("More than one remote source not supported");\r
+\r
+    src = argv[0];\r
+    targ = argv[1];\r
+\r
+    /* Separate host from filename */\r
+    host = src;\r
+    src = colon(src);\r
+    if (src == NULL)\r
+       bump("Local to local copy not supported");\r
+    *src++ = '\0';\r
+    if (*src == '\0')\r
+       src = ".";\r
+    /* Substitute "." for empty filename */\r
+\r
+    /* Separate username and hostname */\r
+    user = host;\r
+    host = strrchr(host, '@');\r
+    if (host == NULL) {\r
+       host = user;\r
+       user = NULL;\r
+    } else {\r
+       *host++ = '\0';\r
+       if (*user == '\0')\r
+           user = NULL;\r
+    }\r
+\r
+    cmd = dupprintf("scp%s%s%s%s -f %s",\r
+                   verbose ? " -v" : "",\r
+                   recursive ? " -r" : "",\r
+                   preserve ? " -p" : "",\r
+                   targetshouldbedirectory ? " -d" : "", src);\r
+    do_cmd(host, user, cmd);\r
+    sfree(cmd);\r
+\r
+    if (scp_sink_setup(src, preserve, recursive))\r
+       return;\r
+\r
+    sink(targ, src);\r
+}\r
+\r
+/*\r
+ *  We will issue a list command to get a remote directory.\r
+ */\r
+static void get_dir_list(int argc, char *argv[])\r
+{\r
+    char *src, *host, *user;\r
+    char *cmd, *p, *q;\r
+    char c;\r
+\r
+    src = argv[0];\r
+\r
+    /* Separate host from filename */\r
+    host = src;\r
+    src = colon(src);\r
+    if (src == NULL)\r
+       bump("Local file listing not supported");\r
+    *src++ = '\0';\r
+    if (*src == '\0')\r
+       src = ".";\r
+    /* Substitute "." for empty filename */\r
+\r
+    /* Separate username and hostname */\r
+    user = host;\r
+    host = strrchr(host, '@');\r
+    if (host == NULL) {\r
+       host = user;\r
+       user = NULL;\r
+    } else {\r
+       *host++ = '\0';\r
+       if (*user == '\0')\r
+           user = NULL;\r
+    }\r
+\r
+    cmd = snewn(4 * strlen(src) + 100, char);\r
+    strcpy(cmd, "ls -la '");\r
+    p = cmd + strlen(cmd);\r
+    for (q = src; *q; q++) {\r
+       if (*q == '\'') {\r
+           *p++ = '\'';\r
+           *p++ = '\\';\r
+           *p++ = '\'';\r
+           *p++ = '\'';\r
+       } else {\r
+           *p++ = *q;\r
+       }\r
+    }\r
+    *p++ = '\'';\r
+    *p = '\0';\r
+\r
+    do_cmd(host, user, cmd);\r
+    sfree(cmd);\r
+\r
+    if (using_sftp) {\r
+       scp_sftp_listdir(src);\r
+    } else {\r
+       while (ssh_scp_recv((unsigned char *) &c, 1) > 0)\r
+           tell_char(stdout, c);\r
+    }\r
+}\r
+\r
+/*\r
+ *  Short description of parameters.\r
+ */\r
+static void usage(void)\r
+{\r
+    printf("PuTTY Secure Copy client\n");\r
+    printf("%s\n", ver);\r
+    printf("Usage: pscp [options] [user@]host:source target\n");\r
+    printf\r
+       ("       pscp [options] source [source...] [user@]host:target\n");\r
+    printf("       pscp [options] -ls [user@]host:filespec\n");\r
+    printf("Options:\n");\r
+    printf("  -V        print version information and exit\n");\r
+    printf("  -pgpfp    print PGP key fingerprints and exit\n");\r
+    printf("  -p        preserve file attributes\n");\r
+    printf("  -q        quiet, don't show statistics\n");\r
+    printf("  -r        copy directories recursively\n");\r
+    printf("  -v        show verbose messages\n");\r
+    printf("  -load sessname  Load settings from saved session\n");\r
+    printf("  -P port   connect to specified port\n");\r
+    printf("  -l user   connect with specified username\n");\r
+    printf("  -pw passw login with specified password\n");\r
+    printf("  -1 -2     force use of particular SSH protocol version\n");\r
+    printf("  -4 -6     force use of IPv4 or IPv6\n");\r
+    printf("  -C        enable compression\n");\r
+    printf("  -i key    private key file for authentication\n");\r
+    printf("  -noagent  disable use of Pageant\n");\r
+    printf("  -agent    enable use of Pageant\n");\r
+    printf("  -batch    disable all interactive prompts\n");\r
+    printf("  -unsafe   allow server-side wildcards (DANGEROUS)\n");\r
+    printf("  -sftp     force use of SFTP protocol\n");\r
+    printf("  -scp      force use of SCP protocol\n");\r
+#if 0\r
+    /*\r
+     * -gui is an internal option, used by GUI front ends to get\r
+     * pscp to pass progress reports back to them. It's not an\r
+     * ordinary user-accessible option, so it shouldn't be part of\r
+     * the command-line help. The only people who need to know\r
+     * about it are programmers, and they can read the source.\r
+     */\r
+    printf\r
+       ("  -gui hWnd GUI mode with the windows handle for receiving messages\n");\r
+#endif\r
+    cleanup_exit(1);\r
+}\r
+\r
+void version(void)\r
+{\r
+    printf("pscp: %s\n", ver);\r
+    cleanup_exit(1);\r
+}\r
+\r
+void cmdline_error(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "pscp: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fprintf(stderr, "\n      try typing just \"pscp\" for help\n");\r
+    exit(1);\r
+}\r
+\r
+/*\r
+ * Main program. (Called `psftp_main' because it gets called from\r
+ * *sftp.c; bit silly, I know, but it had to be called _something_.)\r
+ */\r
+int psftp_main(int argc, char *argv[])\r
+{\r
+    int i;\r
+\r
+    default_protocol = PROT_TELNET;\r
+\r
+    flags = FLAG_STDERR\r
+#ifdef FLAG_SYNCAGENT\r
+       | FLAG_SYNCAGENT\r
+#endif\r
+       ;\r
+    cmdline_tooltype = TOOLTYPE_FILETRANSFER;\r
+    sk_init();\r
+\r
+    /* Load Default Settings before doing anything else. */\r
+    do_defaults(NULL, &cfg);\r
+    loaded_session = FALSE;\r
+\r
+    for (i = 1; i < argc; i++) {\r
+       int ret;\r
+       if (argv[i][0] != '-')\r
+           break;\r
+       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg);\r
+       if (ret == -2) {\r
+           cmdline_error("option \"%s\" requires an argument", argv[i]);\r
+       } else if (ret == 2) {\r
+           i++;               /* skip next argument */\r
+       } else if (ret == 1) {\r
+           /* We have our own verbosity in addition to `flags'. */\r
+           if (flags & FLAG_VERBOSE)\r
+               verbose = 1;\r
+        } else if (strcmp(argv[i], "-pgpfp") == 0) {\r
+            pgp_fingerprints();\r
+            return 1;\r
+       } else if (strcmp(argv[i], "-r") == 0) {\r
+           recursive = 1;\r
+       } else if (strcmp(argv[i], "-p") == 0) {\r
+           preserve = 1;\r
+       } else if (strcmp(argv[i], "-q") == 0) {\r
+           statistics = 0;\r
+       } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) {\r
+           usage();\r
+       } else if (strcmp(argv[i], "-V") == 0) {\r
+            version();\r
+        } else if (strcmp(argv[i], "-ls") == 0) {\r
+           list = 1;\r
+       } else if (strcmp(argv[i], "-batch") == 0) {\r
+           console_batch_mode = 1;\r
+       } else if (strcmp(argv[i], "-unsafe") == 0) {\r
+           scp_unsafe_mode = 1;\r
+       } else if (strcmp(argv[i], "-sftp") == 0) {\r
+           try_scp = 0; try_sftp = 1;\r
+       } else if (strcmp(argv[i], "-scp") == 0) {\r
+           try_scp = 1; try_sftp = 0;\r
+       } else if (strcmp(argv[i], "--") == 0) {\r
+           i++;\r
+           break;\r
+       } else {\r
+           cmdline_error("unknown option \"%s\"", argv[i]);\r
+       }\r
+    }\r
+    argc -= i;\r
+    argv += i;\r
+    back = NULL;\r
+\r
+    if (list) {\r
+       if (argc != 1)\r
+           usage();\r
+       get_dir_list(argc, argv);\r
+\r
+    } else {\r
+\r
+       if (argc < 2)\r
+           usage();\r
+       if (argc > 2)\r
+           targetshouldbedirectory = 1;\r
+\r
+       if (colon(argv[argc - 1]) != NULL)\r
+           toremote(argc, argv);\r
+       else\r
+           tolocal(argc, argv);\r
+    }\r
+\r
+    if (back != NULL && back->connected(backhandle)) {\r
+       char ch;\r
+       back->special(backhandle, TS_EOF);\r
+       ssh_scp_recv((unsigned char *) &ch, 1);\r
+    }\r
+    random_save_seed();\r
+\r
+    cmdline_cleanup();\r
+    console_provide_logctx(NULL);\r
+    back->free(backhandle);\r
+    backhandle = NULL;\r
+    back = NULL;\r
+    sk_cleanup();\r
+    return (errs == 0 ? 0 : 1);\r
+}\r
+\r
+/* end */\r
diff --git a/putty/PSFTP.C b/putty/PSFTP.C
new file mode 100644 (file)
index 0000000..3583fd7
--- /dev/null
@@ -0,0 +1,2946 @@
+/*\r
+ * psftp.c: (platform-independent) front end for PSFTP.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <assert.h>\r
+#include <limits.h>\r
+\r
+#define PUTTY_DO_GLOBALS\r
+#include "putty.h"\r
+#include "psftp.h"\r
+#include "storage.h"\r
+#include "ssh.h"\r
+#include "sftp.h"\r
+#include "int64.h"\r
+\r
+const char *const appname = "PSFTP";\r
+\r
+/*\r
+ * Since SFTP is a request-response oriented protocol, it requires\r
+ * no buffer management: when we send data, we stop and wait for an\r
+ * acknowledgement _anyway_, and so we can't possibly overfill our\r
+ * send buffer.\r
+ */\r
+\r
+static int psftp_connect(char *userhost, char *user, int portnumber);\r
+static int do_sftp_init(void);\r
+void do_sftp_cleanup();\r
+\r
+/* ----------------------------------------------------------------------\r
+ * sftp client state.\r
+ */\r
+\r
+char *pwd, *homedir;\r
+static Backend *back;\r
+static void *backhandle;\r
+static Config cfg;\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Higher-level helper functions used in commands.\r
+ */\r
+\r
+/*\r
+ * Attempt to canonify a pathname starting from the pwd. If\r
+ * canonification fails, at least fall back to returning a _valid_\r
+ * pathname (though it may be ugly, eg /home/simon/../foobar).\r
+ */\r
+char *canonify(char *name)\r
+{\r
+    char *fullname, *canonname;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+\r
+    if (name[0] == '/') {\r
+       fullname = dupstr(name);\r
+    } else {\r
+       char *slash;\r
+       if (pwd[strlen(pwd) - 1] == '/')\r
+           slash = "";\r
+       else\r
+           slash = "/";\r
+       fullname = dupcat(pwd, slash, name, NULL);\r
+    }\r
+\r
+    sftp_register(req = fxp_realpath_send(fullname));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    canonname = fxp_realpath_recv(pktin, rreq);\r
+\r
+    if (canonname) {\r
+       sfree(fullname);\r
+       return canonname;\r
+    } else {\r
+       /*\r
+        * Attempt number 2. Some FXP_REALPATH implementations\r
+        * (glibc-based ones, in particular) require the _whole_\r
+        * path to point to something that exists, whereas others\r
+        * (BSD-based) only require all but the last component to\r
+        * exist. So if the first call failed, we should strip off\r
+        * everything from the last slash onwards and try again,\r
+        * then put the final component back on.\r
+        * \r
+        * Special cases:\r
+        * \r
+        *  - if the last component is "/." or "/..", then we don't\r
+        *    bother trying this because there's no way it can work.\r
+        * \r
+        *  - if the thing actually ends with a "/", we remove it\r
+        *    before we start. Except if the string is "/" itself\r
+        *    (although I can't see why we'd have got here if so,\r
+        *    because surely "/" would have worked the first\r
+        *    time?), in which case we don't bother.\r
+        * \r
+        *  - if there's no slash in the string at all, give up in\r
+        *    confusion (we expect at least one because of the way\r
+        *    we constructed the string).\r
+        */\r
+\r
+       int i;\r
+       char *returnname;\r
+\r
+       i = strlen(fullname);\r
+       if (i > 2 && fullname[i - 1] == '/')\r
+           fullname[--i] = '\0';      /* strip trailing / unless at pos 0 */\r
+       while (i > 0 && fullname[--i] != '/');\r
+\r
+       /*\r
+        * Give up on special cases.\r
+        */\r
+       if (fullname[i] != '/' ||      /* no slash at all */\r
+           !strcmp(fullname + i, "/.") ||      /* ends in /. */\r
+           !strcmp(fullname + i, "/..") ||     /* ends in /.. */\r
+           !strcmp(fullname, "/")) {\r
+           return fullname;\r
+       }\r
+\r
+       /*\r
+        * Now i points at the slash. Deal with the final special\r
+        * case i==0 (ie the whole path was "/nonexistentfile").\r
+        */\r
+       fullname[i] = '\0';            /* separate the string */\r
+       if (i == 0) {\r
+           sftp_register(req = fxp_realpath_send("/"));\r
+       } else {\r
+           sftp_register(req = fxp_realpath_send(fullname));\r
+       }\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       canonname = fxp_realpath_recv(pktin, rreq);\r
+\r
+       if (!canonname) {\r
+           /* Even that failed. Restore our best guess at the\r
+            * constructed filename and give up */\r
+           fullname[i] = '/';  /* restore slash and last component */\r
+           return fullname;\r
+       }\r
+\r
+       /*\r
+        * We have a canonical name for all but the last path\r
+        * component. Concatenate the last component and return.\r
+        */\r
+       returnname = dupcat(canonname,\r
+                           canonname[strlen(canonname) - 1] ==\r
+                           '/' ? "" : "/", fullname + i + 1, NULL);\r
+       sfree(fullname);\r
+       sfree(canonname);\r
+       return returnname;\r
+    }\r
+}\r
+\r
+/*\r
+ * Return a pointer to the portion of str that comes after the last\r
+ * slash (or backslash or colon, if `local' is TRUE).\r
+ */\r
+static char *stripslashes(char *str, int local)\r
+{\r
+    char *p;\r
+\r
+    if (local) {\r
+        p = strchr(str, ':');\r
+        if (p) str = p+1;\r
+    }\r
+\r
+    p = strrchr(str, '/');\r
+    if (p) str = p+1;\r
+\r
+    if (local) {\r
+       p = strrchr(str, '\\');\r
+       if (p) str = p+1;\r
+    }\r
+\r
+    return str;\r
+}\r
+\r
+/*\r
+ * qsort comparison routine for fxp_name structures. Sorts by real\r
+ * file name.\r
+ */\r
+static int sftp_name_compare(const void *av, const void *bv)\r
+{\r
+    const struct fxp_name *const *a = (const struct fxp_name *const *) av;\r
+    const struct fxp_name *const *b = (const struct fxp_name *const *) bv;\r
+    return strcmp((*a)->filename, (*b)->filename);\r
+}\r
+\r
+/*\r
+ * Likewise, but for a bare char *.\r
+ */\r
+static int bare_name_compare(const void *av, const void *bv)\r
+{\r
+    const char **a = (const char **) av;\r
+    const char **b = (const char **) bv;\r
+    return strcmp(*a, *b);\r
+}\r
+\r
+static void not_connected(void)\r
+{\r
+    printf("psftp: not connected to a host; use \"open host.name\"\n");\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * The meat of the `get' and `put' commands.\r
+ */\r
+int sftp_get_file(char *fname, char *outfname, int recurse, int restart)\r
+{\r
+    struct fxp_handle *fh;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    struct fxp_xfer *xfer;\r
+    uint64 offset;\r
+    WFile *file;\r
+    int ret, shown_err = FALSE;\r
+\r
+    /*\r
+     * In recursive mode, see if we're dealing with a directory.\r
+     * (If we're not in recursive mode, we need not even check: the\r
+     * subsequent FXP_OPEN will return a usable error message.)\r
+     */\r
+    if (recurse) {\r
+       struct fxp_attrs attrs;\r
+       int result;\r
+\r
+       sftp_register(req = fxp_stat_send(fname));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       result = fxp_stat_recv(pktin, rreq, &attrs);\r
+\r
+       if (result &&\r
+           (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&\r
+           (attrs.permissions & 0040000)) {\r
+\r
+           struct fxp_handle *dirhandle;\r
+           int nnames, namesize;\r
+           struct fxp_name **ournames;\r
+           struct fxp_names *names;\r
+           int i;\r
+\r
+           /*\r
+            * First, attempt to create the destination directory,\r
+            * unless it already exists.\r
+            */\r
+           if (file_type(outfname) != FILE_TYPE_DIRECTORY &&\r
+               !create_directory(outfname)) {\r
+               printf("%s: Cannot create directory\n", outfname);\r
+               return 0;\r
+           }\r
+\r
+           /*\r
+            * Now get the list of filenames in the remote\r
+            * directory.\r
+            */\r
+           sftp_register(req = fxp_opendir_send(fname));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           dirhandle = fxp_opendir_recv(pktin, rreq);\r
+\r
+           if (!dirhandle) {\r
+               printf("%s: unable to open directory: %s\n",\r
+                      fname, fxp_error());\r
+               return 0;\r
+           }\r
+           nnames = namesize = 0;\r
+           ournames = NULL;\r
+           while (1) {\r
+               int i;\r
+\r
+               sftp_register(req = fxp_readdir_send(dirhandle));\r
+               rreq = sftp_find_request(pktin = sftp_recv());\r
+               assert(rreq == req);\r
+               names = fxp_readdir_recv(pktin, rreq);\r
+\r
+               if (names == NULL) {\r
+                   if (fxp_error_type() == SSH_FX_EOF)\r
+                       break;\r
+                   printf("%s: reading directory: %s\n", fname, fxp_error());\r
+                   sfree(ournames);\r
+                   return 0;\r
+               }\r
+               if (names->nnames == 0) {\r
+                   fxp_free_names(names);\r
+                   break;\r
+               }\r
+               if (nnames + names->nnames >= namesize) {\r
+                   namesize += names->nnames + 128;\r
+                   ournames = sresize(ournames, namesize, struct fxp_name *);\r
+               }\r
+               for (i = 0; i < names->nnames; i++)\r
+                   if (strcmp(names->names[i].filename, ".") &&\r
+                       strcmp(names->names[i].filename, "..")) {\r
+                       if (!vet_filename(names->names[i].filename)) {\r
+                           printf("ignoring potentially dangerous server-"\r
+                                  "supplied filename '%s'\n",\r
+                                  names->names[i].filename);\r
+                       } else {\r
+                           ournames[nnames++] =\r
+                               fxp_dup_name(&names->names[i]);\r
+                       }\r
+                   }\r
+               fxp_free_names(names);\r
+           }\r
+           sftp_register(req = fxp_close_send(dirhandle));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           fxp_close_recv(pktin, rreq);\r
+\r
+           /*\r
+            * Sort the names into a clear order. This ought to\r
+            * make things more predictable when we're doing a\r
+            * reget of the same directory, just in case two\r
+            * readdirs on the same remote directory return a\r
+            * different order.\r
+            */\r
+           qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);\r
+\r
+           /*\r
+            * If we're in restart mode, find the last filename on\r
+            * this list that already exists. We may have to do a\r
+            * reget on _that_ file, but shouldn't have to do\r
+            * anything on the previous files.\r
+            * \r
+            * If none of them exists, of course, we start at 0.\r
+            */\r
+           i = 0;\r
+            if (restart) {\r
+                while (i < nnames) {\r
+                    char *nextoutfname;\r
+                    int ret;\r
+                    if (outfname)\r
+                        nextoutfname = dir_file_cat(outfname,\r
+                                                    ournames[i]->filename);\r
+                    else\r
+                        nextoutfname = dupstr(ournames[i]->filename);\r
+                    ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);\r
+                    sfree(nextoutfname);\r
+                    if (ret)\r
+                        break;\r
+                    i++;\r
+                }\r
+                if (i > 0)\r
+                    i--;\r
+            }\r
+\r
+           /*\r
+            * Now we're ready to recurse. Starting at ournames[i]\r
+            * and continuing on to the end of the list, we\r
+            * construct a new source and target file name, and\r
+            * call sftp_get_file again.\r
+            */\r
+           for (; i < nnames; i++) {\r
+               char *nextfname, *nextoutfname;\r
+               int ret;\r
+               \r
+               nextfname = dupcat(fname, "/", ournames[i]->filename, NULL);\r
+               if (outfname)\r
+                   nextoutfname = dir_file_cat(outfname,\r
+                                               ournames[i]->filename);\r
+               else\r
+                   nextoutfname = dupstr(ournames[i]->filename);\r
+               ret = sftp_get_file(nextfname, nextoutfname, recurse, restart);\r
+               restart = FALSE;       /* after first partial file, do full */\r
+               sfree(nextoutfname);\r
+               sfree(nextfname);\r
+               if (!ret) {\r
+                   for (i = 0; i < nnames; i++) {\r
+                       fxp_free_name(ournames[i]);\r
+                   }\r
+                   sfree(ournames);\r
+                   return 0;\r
+               }\r
+           }\r
+\r
+           /*\r
+            * Done this recursion level. Free everything.\r
+            */\r
+           for (i = 0; i < nnames; i++) {\r
+               fxp_free_name(ournames[i]);\r
+           }\r
+           sfree(ournames);\r
+\r
+           return 1;\r
+       }\r
+    }\r
+\r
+    sftp_register(req = fxp_open_send(fname, SSH_FXF_READ));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    fh = fxp_open_recv(pktin, rreq);\r
+\r
+    if (!fh) {\r
+       printf("%s: open for read: %s\n", fname, fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    if (restart) {\r
+       file = open_existing_wfile(outfname, NULL);\r
+    } else {\r
+       file = open_new_file(outfname);\r
+    }\r
+\r
+    if (!file) {\r
+       printf("local: unable to open %s\n", outfname);\r
+\r
+       sftp_register(req = fxp_close_send(fh));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       fxp_close_recv(pktin, rreq);\r
+\r
+       return 0;\r
+    }\r
+\r
+    if (restart) {\r
+       char decbuf[30];\r
+       if (seek_file(file, uint64_make(0,0) , FROM_END) == -1) {\r
+           close_wfile(file);\r
+           printf("reget: cannot restart %s - file too large\n",\r
+                  outfname);\r
+           sftp_register(req = fxp_close_send(fh));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           fxp_close_recv(pktin, rreq);\r
+               \r
+           return 0;\r
+       }\r
+           \r
+       offset = get_file_posn(file);\r
+       uint64_decimal(offset, decbuf);\r
+       printf("reget: restarting at file position %s\n", decbuf);\r
+    } else {\r
+       offset = uint64_make(0, 0);\r
+    }\r
+\r
+    printf("remote:%s => local:%s\n", fname, outfname);\r
+\r
+    /*\r
+     * FIXME: we can use FXP_FSTAT here to get the file size, and\r
+     * thus put up a progress bar.\r
+     */\r
+    ret = 1;\r
+    xfer = xfer_download_init(fh, offset);\r
+    while (!xfer_done(xfer)) {\r
+       void *vbuf;\r
+       int ret, len;\r
+       int wpos, wlen;\r
+\r
+       xfer_download_queue(xfer);\r
+       pktin = sftp_recv();\r
+       ret = xfer_download_gotpkt(xfer, pktin);\r
+\r
+       if (ret < 0) {\r
+           if (!shown_err) {\r
+               printf("error while reading: %s\n", fxp_error());\r
+               shown_err = TRUE;\r
+           }\r
+            ret = 0;\r
+       }\r
+\r
+       while (xfer_download_data(xfer, &vbuf, &len)) {\r
+           unsigned char *buf = (unsigned char *)vbuf;\r
+\r
+           wpos = 0;\r
+           while (wpos < len) {\r
+               wlen = write_to_file(file, buf + wpos, len - wpos);\r
+               if (wlen <= 0) {\r
+                   printf("error while writing local file\n");\r
+                   ret = 0;\r
+                   xfer_set_error(xfer);\r
+                   break;\r
+               }\r
+               wpos += wlen;\r
+           }\r
+           if (wpos < len) {          /* we had an error */\r
+               ret = 0;\r
+               xfer_set_error(xfer);\r
+           }\r
+\r
+           sfree(vbuf);\r
+       }\r
+    }\r
+\r
+    xfer_cleanup(xfer);\r
+\r
+    close_wfile(file);\r
+\r
+    sftp_register(req = fxp_close_send(fh));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    fxp_close_recv(pktin, rreq);\r
+\r
+    return ret;\r
+}\r
+\r
+int sftp_put_file(char *fname, char *outfname, int recurse, int restart)\r
+{\r
+    struct fxp_handle *fh;\r
+    struct fxp_xfer *xfer;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    uint64 offset;\r
+    RFile *file;\r
+    int ret, err, eof;\r
+\r
+    /*\r
+     * In recursive mode, see if we're dealing with a directory.\r
+     * (If we're not in recursive mode, we need not even check: the\r
+     * subsequent fopen will return an error message.)\r
+     */\r
+    if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) {\r
+       struct fxp_attrs attrs;\r
+       int result;\r
+       int nnames, namesize;\r
+       char *name, **ournames;\r
+       DirHandle *dh;\r
+       int i;\r
+\r
+       /*\r
+        * First, attempt to create the destination directory,\r
+        * unless it already exists.\r
+        */\r
+       sftp_register(req = fxp_stat_send(outfname));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       result = fxp_stat_recv(pktin, rreq, &attrs);\r
+       if (!result ||\r
+           !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||\r
+           !(attrs.permissions & 0040000)) {\r
+           sftp_register(req = fxp_mkdir_send(outfname));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           result = fxp_mkdir_recv(pktin, rreq);\r
+\r
+           if (!result) {\r
+               printf("%s: create directory: %s\n",\r
+                      outfname, fxp_error());\r
+               return 0;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Now get the list of filenames in the local directory.\r
+        */\r
+       nnames = namesize = 0;\r
+       ournames = NULL;\r
+\r
+       dh = open_directory(fname);\r
+       if (!dh) {\r
+           printf("%s: unable to open directory\n", fname);\r
+           return 0;\r
+       }\r
+       while ((name = read_filename(dh)) != NULL) {\r
+           if (nnames >= namesize) {\r
+               namesize += 128;\r
+               ournames = sresize(ournames, namesize, char *);\r
+           }\r
+           ournames[nnames++] = name;\r
+       }\r
+       close_directory(dh);\r
+\r
+       /*\r
+        * Sort the names into a clear order. This ought to make\r
+        * things more predictable when we're doing a reput of the\r
+        * same directory, just in case two readdirs on the same\r
+        * local directory return a different order.\r
+        */\r
+       qsort(ournames, nnames, sizeof(*ournames), bare_name_compare);\r
+\r
+       /*\r
+        * If we're in restart mode, find the last filename on this\r
+        * list that already exists. We may have to do a reput on\r
+        * _that_ file, but shouldn't have to do anything on the\r
+        * previous files.\r
+        *\r
+        * If none of them exists, of course, we start at 0.\r
+        */\r
+       i = 0;\r
+        if (restart) {\r
+            while (i < nnames) {\r
+                char *nextoutfname;\r
+                nextoutfname = dupcat(outfname, "/", ournames[i], NULL);\r
+                sftp_register(req = fxp_stat_send(nextoutfname));\r
+                rreq = sftp_find_request(pktin = sftp_recv());\r
+                assert(rreq == req);\r
+                result = fxp_stat_recv(pktin, rreq, &attrs);\r
+                sfree(nextoutfname);\r
+                if (!result)\r
+                    break;\r
+                i++;\r
+            }\r
+            if (i > 0)\r
+                i--;\r
+        }\r
+\r
+        /*\r
+         * Now we're ready to recurse. Starting at ournames[i]\r
+        * and continuing on to the end of the list, we\r
+        * construct a new source and target file name, and\r
+        * call sftp_put_file again.\r
+        */\r
+       for (; i < nnames; i++) {\r
+           char *nextfname, *nextoutfname;\r
+           int ret;\r
+\r
+           if (fname)\r
+               nextfname = dir_file_cat(fname, ournames[i]);\r
+           else\r
+               nextfname = dupstr(ournames[i]);\r
+           nextoutfname = dupcat(outfname, "/", ournames[i], NULL);\r
+           ret = sftp_put_file(nextfname, nextoutfname, recurse, restart);\r
+           restart = FALSE;           /* after first partial file, do full */\r
+           sfree(nextoutfname);\r
+           sfree(nextfname);\r
+           if (!ret) {\r
+               for (i = 0; i < nnames; i++) {\r
+                   sfree(ournames[i]);\r
+               }\r
+               sfree(ournames);\r
+               return 0;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Done this recursion level. Free everything.\r
+        */\r
+       for (i = 0; i < nnames; i++) {\r
+           sfree(ournames[i]);\r
+       }\r
+       sfree(ournames);\r
+\r
+       return 1;\r
+    }\r
+\r
+    file = open_existing_file(fname, NULL, NULL, NULL);\r
+    if (!file) {\r
+       printf("local: unable to open %s\n", fname);\r
+       return 0;\r
+    }\r
+    if (restart) {\r
+       sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE));\r
+    } else {\r
+       sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE |\r
+                                         SSH_FXF_CREAT | SSH_FXF_TRUNC));\r
+    }\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    fh = fxp_open_recv(pktin, rreq);\r
+\r
+    if (!fh) {\r
+       close_rfile(file);\r
+       printf("%s: open for write: %s\n", outfname, fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    if (restart) {\r
+       char decbuf[30];\r
+       struct fxp_attrs attrs;\r
+       int ret;\r
+\r
+       sftp_register(req = fxp_fstat_send(fh));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       ret = fxp_fstat_recv(pktin, rreq, &attrs);\r
+\r
+       if (!ret) {\r
+           close_rfile(file);\r
+           printf("read size of %s: %s\n", outfname, fxp_error());\r
+           return 0;\r
+       }\r
+       if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {\r
+           close_rfile(file);\r
+           printf("read size of %s: size was not given\n", outfname);\r
+           return 0;\r
+       }\r
+       offset = attrs.size;\r
+       uint64_decimal(offset, decbuf);\r
+       printf("reput: restarting at file position %s\n", decbuf);\r
+\r
+       if (seek_file((WFile *)file, offset, FROM_START) != 0)\r
+           seek_file((WFile *)file, uint64_make(0,0), FROM_END);    /* *shrug* */\r
+    } else {\r
+       offset = uint64_make(0, 0);\r
+    }\r
+\r
+    printf("local:%s => remote:%s\n", fname, outfname);\r
+\r
+    /*\r
+     * FIXME: we can use FXP_FSTAT here to get the file size, and\r
+     * thus put up a progress bar.\r
+     */\r
+    ret = 1;\r
+    xfer = xfer_upload_init(fh, offset);\r
+    err = eof = 0;\r
+    while ((!err && !eof) || !xfer_done(xfer)) {\r
+       char buffer[4096];\r
+       int len, ret;\r
+\r
+       while (xfer_upload_ready(xfer) && !err && !eof) {\r
+           len = read_from_file(file, buffer, sizeof(buffer));\r
+           if (len == -1) {\r
+               printf("error while reading local file\n");\r
+               err = 1;\r
+           } else if (len == 0) {\r
+               eof = 1;\r
+           } else {\r
+               xfer_upload_data(xfer, buffer, len);\r
+           }\r
+       }\r
+\r
+       if (!xfer_done(xfer)) {\r
+           pktin = sftp_recv();\r
+           ret = xfer_upload_gotpkt(xfer, pktin);\r
+           if (ret <= 0 && !err) {\r
+               printf("error while writing: %s\n", fxp_error());\r
+               err = 1;\r
+           }\r
+       }\r
+    }\r
+\r
+    xfer_cleanup(xfer);\r
+\r
+    sftp_register(req = fxp_close_send(fh));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    fxp_close_recv(pktin, rreq);\r
+\r
+    close_rfile(file);\r
+\r
+    return ret;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * A remote wildcard matcher, providing a similar interface to the\r
+ * local one in psftp.h.\r
+ */\r
+\r
+typedef struct SftpWildcardMatcher {\r
+    struct fxp_handle *dirh;\r
+    struct fxp_names *names;\r
+    int namepos;\r
+    char *wildcard, *prefix;\r
+} SftpWildcardMatcher;\r
+\r
+SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name)\r
+{\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    char *wildcard;\r
+    char *unwcdir, *tmpdir, *cdir;\r
+    int len, check;\r
+    SftpWildcardMatcher *swcm;\r
+    struct fxp_handle *dirh;\r
+\r
+    /*\r
+     * We don't handle multi-level wildcards; so we expect to find\r
+     * a fully specified directory part, followed by a wildcard\r
+     * after that.\r
+     */\r
+    wildcard = stripslashes(name, 0);\r
+\r
+    unwcdir = dupstr(name);\r
+    len = wildcard - name;\r
+    unwcdir[len] = '\0';\r
+    if (len > 0 && unwcdir[len-1] == '/')\r
+       unwcdir[len-1] = '\0';\r
+    tmpdir = snewn(1 + len, char);\r
+    check = wc_unescape(tmpdir, unwcdir);\r
+    sfree(tmpdir);\r
+\r
+    if (!check) {\r
+       printf("Multiple-level wildcards are not supported\n");\r
+       sfree(unwcdir);\r
+       return NULL;\r
+    }\r
+\r
+    cdir = canonify(unwcdir);\r
+\r
+    sftp_register(req = fxp_opendir_send(cdir));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    dirh = fxp_opendir_recv(pktin, rreq);\r
+\r
+    if (dirh) {\r
+       swcm = snew(SftpWildcardMatcher);\r
+       swcm->dirh = dirh;\r
+       swcm->names = NULL;\r
+       swcm->wildcard = dupstr(wildcard);\r
+       swcm->prefix = unwcdir;\r
+    } else {\r
+       printf("Unable to open %s: %s\n", cdir, fxp_error());\r
+       swcm = NULL;\r
+       sfree(unwcdir);\r
+    }\r
+\r
+    sfree(cdir);\r
+\r
+    return swcm;\r
+}\r
+\r
+char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)\r
+{\r
+    struct fxp_name *name;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+\r
+    while (1) {\r
+       if (swcm->names && swcm->namepos >= swcm->names->nnames) {\r
+           fxp_free_names(swcm->names);\r
+           swcm->names = NULL;\r
+       }\r
+\r
+       if (!swcm->names) {\r
+           sftp_register(req = fxp_readdir_send(swcm->dirh));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           swcm->names = fxp_readdir_recv(pktin, rreq);\r
+\r
+           if (!swcm->names) {\r
+               if (fxp_error_type() != SSH_FX_EOF)\r
+                   printf("%s: reading directory: %s\n", swcm->prefix,\r
+                          fxp_error());\r
+               return NULL;\r
+           }\r
+\r
+           swcm->namepos = 0;\r
+       }\r
+\r
+       assert(swcm->names && swcm->namepos < swcm->names->nnames);\r
+\r
+       name = &swcm->names->names[swcm->namepos++];\r
+\r
+       if (!strcmp(name->filename, ".") || !strcmp(name->filename, ".."))\r
+           continue;                  /* expected bad filenames */\r
+\r
+       if (!vet_filename(name->filename)) {\r
+           printf("ignoring potentially dangerous server-"\r
+                  "supplied filename '%s'\n", name->filename);\r
+           continue;                  /* unexpected bad filename */\r
+       }\r
+\r
+       if (!wc_match(swcm->wildcard, name->filename))\r
+           continue;                  /* doesn't match the wildcard */\r
+\r
+       /*\r
+        * We have a working filename. Return it.\r
+        */\r
+       return dupprintf("%s%s%s", swcm->prefix,\r
+                        (!swcm->prefix[0] ||\r
+                         swcm->prefix[strlen(swcm->prefix)-1]=='/' ?\r
+                         "" : "/"),\r
+                        name->filename);\r
+    }\r
+}\r
+\r
+void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm)\r
+{\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+\r
+    sftp_register(req = fxp_close_send(swcm->dirh));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    fxp_close_recv(pktin, rreq);\r
+\r
+    if (swcm->names)\r
+       fxp_free_names(swcm->names);\r
+\r
+    sfree(swcm->prefix);\r
+    sfree(swcm->wildcard);\r
+\r
+    sfree(swcm);\r
+}\r
+\r
+/*\r
+ * General function to match a potential wildcard in a filename\r
+ * argument and iterate over every matching file. Used in several\r
+ * PSFTP commands (rmdir, rm, chmod, mv).\r
+ */\r
+int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx)\r
+{\r
+    char *unwcfname, *newname, *cname;\r
+    int is_wc, ret;\r
+\r
+    unwcfname = snewn(strlen(filename)+1, char);\r
+    is_wc = !wc_unescape(unwcfname, filename);\r
+\r
+    if (is_wc) {\r
+       SftpWildcardMatcher *swcm = sftp_begin_wildcard_matching(filename);\r
+       int matched = FALSE;\r
+       sfree(unwcfname);\r
+\r
+       if (!swcm)\r
+           return 0;\r
+\r
+       ret = 1;\r
+\r
+       while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) {\r
+           cname = canonify(newname);\r
+           if (!cname) {\r
+               printf("%s: canonify: %s\n", newname, fxp_error());\r
+               ret = 0;\r
+           }\r
+           matched = TRUE;\r
+           ret &= func(ctx, cname);\r
+           sfree(cname);\r
+       }\r
+\r
+       if (!matched) {\r
+           /* Politely warn the user that nothing matched. */\r
+           printf("%s: nothing matched\n", filename);\r
+       }\r
+\r
+       sftp_finish_wildcard_matching(swcm);\r
+    } else {\r
+       cname = canonify(unwcfname);\r
+       if (!cname) {\r
+           printf("%s: canonify: %s\n", filename, fxp_error());\r
+           ret = 0;\r
+       }\r
+       ret = func(ctx, cname);\r
+       sfree(cname);\r
+       sfree(unwcfname);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Handy helper function.\r
+ */\r
+int is_wildcard(char *name)\r
+{\r
+    char *unwcfname = snewn(strlen(name)+1, char);\r
+    int is_wc = !wc_unescape(unwcfname, name);\r
+    sfree(unwcfname);\r
+    return is_wc;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Actual sftp commands.\r
+ */\r
+struct sftp_command {\r
+    char **words;\r
+    int nwords, wordssize;\r
+    int (*obey) (struct sftp_command *);       /* returns <0 to quit */\r
+};\r
+\r
+int sftp_cmd_null(struct sftp_command *cmd)\r
+{\r
+    return 1;                         /* success */\r
+}\r
+\r
+int sftp_cmd_unknown(struct sftp_command *cmd)\r
+{\r
+    printf("psftp: unknown command \"%s\"\n", cmd->words[0]);\r
+    return 0;                         /* failure */\r
+}\r
+\r
+int sftp_cmd_quit(struct sftp_command *cmd)\r
+{\r
+    return -1;\r
+}\r
+\r
+int sftp_cmd_close(struct sftp_command *cmd)\r
+{\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (back != NULL && back->connected(backhandle)) {\r
+       char ch;\r
+       back->special(backhandle, TS_EOF);\r
+       sftp_recvdata(&ch, 1);\r
+    }\r
+    do_sftp_cleanup();\r
+\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * List a directory. If no arguments are given, list pwd; otherwise\r
+ * list the directory given in words[1].\r
+ */\r
+int sftp_cmd_ls(struct sftp_command *cmd)\r
+{\r
+    struct fxp_handle *dirh;\r
+    struct fxp_names *names;\r
+    struct fxp_name **ournames;\r
+    int nnames, namesize;\r
+    char *dir, *cdir, *unwcdir, *wildcard;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    int i;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 2)\r
+       dir = ".";\r
+    else\r
+       dir = cmd->words[1];\r
+\r
+    unwcdir = snewn(1 + strlen(dir), char);\r
+    if (wc_unescape(unwcdir, dir)) {\r
+       dir = unwcdir;\r
+       wildcard = NULL;\r
+    } else {\r
+       char *tmpdir;\r
+       int len, check;\r
+\r
+       wildcard = stripslashes(dir, 0);\r
+       unwcdir = dupstr(dir);\r
+       len = wildcard - dir;\r
+       unwcdir[len] = '\0';\r
+       if (len > 0 && unwcdir[len-1] == '/')\r
+           unwcdir[len-1] = '\0';\r
+       tmpdir = snewn(1 + len, char);\r
+       check = wc_unescape(tmpdir, unwcdir);\r
+       sfree(tmpdir);\r
+       if (!check) {\r
+           printf("Multiple-level wildcards are not supported\n");\r
+           sfree(unwcdir);\r
+           return 0;\r
+       }\r
+       dir = unwcdir;\r
+    }\r
+\r
+    cdir = canonify(dir);\r
+    if (!cdir) {\r
+       printf("%s: canonify: %s\n", dir, fxp_error());\r
+       sfree(unwcdir);\r
+       return 0;\r
+    }\r
+\r
+    printf("Listing directory %s\n", cdir);\r
+\r
+    sftp_register(req = fxp_opendir_send(cdir));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    dirh = fxp_opendir_recv(pktin, rreq);\r
+\r
+    if (dirh == NULL) {\r
+       printf("Unable to open %s: %s\n", dir, fxp_error());\r
+    } else {\r
+       nnames = namesize = 0;\r
+       ournames = NULL;\r
+\r
+       while (1) {\r
+\r
+           sftp_register(req = fxp_readdir_send(dirh));\r
+           rreq = sftp_find_request(pktin = sftp_recv());\r
+           assert(rreq == req);\r
+           names = fxp_readdir_recv(pktin, rreq);\r
+\r
+           if (names == NULL) {\r
+               if (fxp_error_type() == SSH_FX_EOF)\r
+                   break;\r
+               printf("Reading directory %s: %s\n", dir, fxp_error());\r
+               break;\r
+           }\r
+           if (names->nnames == 0) {\r
+               fxp_free_names(names);\r
+               break;\r
+           }\r
+\r
+           if (nnames + names->nnames >= namesize) {\r
+               namesize += names->nnames + 128;\r
+               ournames = sresize(ournames, namesize, struct fxp_name *);\r
+           }\r
+\r
+           for (i = 0; i < names->nnames; i++)\r
+               if (!wildcard || wc_match(wildcard, names->names[i].filename))\r
+                   ournames[nnames++] = fxp_dup_name(&names->names[i]);\r
+\r
+           fxp_free_names(names);\r
+       }\r
+       sftp_register(req = fxp_close_send(dirh));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       fxp_close_recv(pktin, rreq);\r
+\r
+       /*\r
+        * Now we have our filenames. Sort them by actual file\r
+        * name, and then output the longname parts.\r
+        */\r
+       qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);\r
+\r
+       /*\r
+        * And print them.\r
+        */\r
+       for (i = 0; i < nnames; i++) {\r
+           printf("%s\n", ournames[i]->longname);\r
+           fxp_free_name(ournames[i]);\r
+       }\r
+       sfree(ournames);\r
+    }\r
+\r
+    sfree(cdir);\r
+    sfree(unwcdir);\r
+\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Change directories. We do this by canonifying the new name, then\r
+ * trying to OPENDIR it. Only if that succeeds do we set the new pwd.\r
+ */\r
+int sftp_cmd_cd(struct sftp_command *cmd)\r
+{\r
+    struct fxp_handle *dirh;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    char *dir;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 2)\r
+       dir = dupstr(homedir);\r
+    else\r
+       dir = canonify(cmd->words[1]);\r
+\r
+    if (!dir) {\r
+       printf("%s: canonify: %s\n", dir, fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    sftp_register(req = fxp_opendir_send(dir));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    dirh = fxp_opendir_recv(pktin, rreq);\r
+\r
+    if (!dirh) {\r
+       printf("Directory %s: %s\n", dir, fxp_error());\r
+       sfree(dir);\r
+       return 0;\r
+    }\r
+\r
+    sftp_register(req = fxp_close_send(dirh));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    fxp_close_recv(pktin, rreq);\r
+\r
+    sfree(pwd);\r
+    pwd = dir;\r
+    printf("Remote directory is now %s\n", pwd);\r
+\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Print current directory. Easy as pie.\r
+ */\r
+int sftp_cmd_pwd(struct sftp_command *cmd)\r
+{\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    printf("Remote directory is %s\n", pwd);\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Get a file and save it at the local end. We have three very\r
+ * similar commands here. The basic one is `get'; `reget' differs\r
+ * in that it checks for the existence of the destination file and\r
+ * starts from where a previous aborted transfer left off; `mget'\r
+ * differs in that it interprets all its arguments as files to\r
+ * transfer (never as a different local name for a remote file) and\r
+ * can handle wildcards.\r
+ */\r
+int sftp_general_get(struct sftp_command *cmd, int restart, int multiple)\r
+{\r
+    char *fname, *unwcfname, *origfname, *origwfname, *outfname;\r
+    int i, ret;\r
+    int recurse = FALSE;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    i = 1;\r
+    while (i < cmd->nwords && cmd->words[i][0] == '-') {\r
+       if (!strcmp(cmd->words[i], "--")) {\r
+           /* finish processing options */\r
+           i++;\r
+           break;\r
+       } else if (!strcmp(cmd->words[i], "-r")) {\r
+           recurse = TRUE;\r
+       } else {\r
+           printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);\r
+           return 0;\r
+       }\r
+       i++;\r
+    }\r
+\r
+    if (i >= cmd->nwords) {\r
+       printf("%s: expects a filename\n", cmd->words[0]);\r
+       return 0;\r
+    }\r
+\r
+    ret = 1;\r
+    do {\r
+       SftpWildcardMatcher *swcm;\r
+\r
+       origfname = cmd->words[i++];\r
+       unwcfname = snewn(strlen(origfname)+1, char);\r
+\r
+       if (multiple && !wc_unescape(unwcfname, origfname)) {\r
+           swcm = sftp_begin_wildcard_matching(origfname);\r
+           if (!swcm) {\r
+               sfree(unwcfname);\r
+               continue;\r
+           }\r
+           origwfname = sftp_wildcard_get_filename(swcm);\r
+           if (!origwfname) {\r
+               /* Politely warn the user that nothing matched. */\r
+               printf("%s: nothing matched\n", origfname);\r
+               sftp_finish_wildcard_matching(swcm);\r
+               sfree(unwcfname);\r
+               continue;\r
+           }\r
+       } else {\r
+           origwfname = origfname;\r
+           swcm = NULL;\r
+       }\r
+\r
+       while (origwfname) {\r
+           fname = canonify(origwfname);\r
+\r
+           if (!fname) {\r
+               printf("%s: canonify: %s\n", origwfname, fxp_error());\r
+               sfree(unwcfname);\r
+               return 0;\r
+           }\r
+\r
+           if (!multiple && i < cmd->nwords)\r
+               outfname = cmd->words[i++];\r
+           else\r
+               outfname = stripslashes(origwfname, 0);\r
+\r
+           ret = sftp_get_file(fname, outfname, recurse, restart);\r
+\r
+           sfree(fname);\r
+\r
+           if (swcm) {\r
+               sfree(origwfname);\r
+               origwfname = sftp_wildcard_get_filename(swcm);\r
+           } else {\r
+               origwfname = NULL;\r
+           }\r
+       }\r
+       sfree(unwcfname);\r
+       if (swcm)\r
+           sftp_finish_wildcard_matching(swcm);\r
+       if (!ret)\r
+           return ret;\r
+\r
+    } while (multiple && i < cmd->nwords);\r
+\r
+    return ret;\r
+}\r
+int sftp_cmd_get(struct sftp_command *cmd)\r
+{\r
+    return sftp_general_get(cmd, 0, 0);\r
+}\r
+int sftp_cmd_mget(struct sftp_command *cmd)\r
+{\r
+    return sftp_general_get(cmd, 0, 1);\r
+}\r
+int sftp_cmd_reget(struct sftp_command *cmd)\r
+{\r
+    return sftp_general_get(cmd, 1, 0);\r
+}\r
+\r
+/*\r
+ * Send a file and store it at the remote end. We have three very\r
+ * similar commands here. The basic one is `put'; `reput' differs\r
+ * in that it checks for the existence of the destination file and\r
+ * starts from where a previous aborted transfer left off; `mput'\r
+ * differs in that it interprets all its arguments as files to\r
+ * transfer (never as a different remote name for a local file) and\r
+ * can handle wildcards.\r
+ */\r
+int sftp_general_put(struct sftp_command *cmd, int restart, int multiple)\r
+{\r
+    char *fname, *wfname, *origoutfname, *outfname;\r
+    int i, ret;\r
+    int recurse = FALSE;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    i = 1;\r
+    while (i < cmd->nwords && cmd->words[i][0] == '-') {\r
+       if (!strcmp(cmd->words[i], "--")) {\r
+           /* finish processing options */\r
+           i++;\r
+           break;\r
+       } else if (!strcmp(cmd->words[i], "-r")) {\r
+           recurse = TRUE;\r
+       } else {\r
+           printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);\r
+           return 0;\r
+       }\r
+       i++;\r
+    }\r
+\r
+    if (i >= cmd->nwords) {\r
+       printf("%s: expects a filename\n", cmd->words[0]);\r
+       return 0;\r
+    }\r
+\r
+    ret = 1;\r
+    do {\r
+       WildcardMatcher *wcm;\r
+       fname = cmd->words[i++];\r
+\r
+       if (multiple && test_wildcard(fname, FALSE) == WCTYPE_WILDCARD) {\r
+           wcm = begin_wildcard_matching(fname);\r
+           wfname = wildcard_get_filename(wcm);\r
+           if (!wfname) {\r
+               /* Politely warn the user that nothing matched. */\r
+               printf("%s: nothing matched\n", fname);\r
+               finish_wildcard_matching(wcm);\r
+               continue;\r
+           }\r
+       } else {\r
+           wfname = fname;\r
+           wcm = NULL;\r
+       }\r
+\r
+       while (wfname) {\r
+           if (!multiple && i < cmd->nwords)\r
+               origoutfname = cmd->words[i++];\r
+           else\r
+               origoutfname = stripslashes(wfname, 1);\r
+\r
+           outfname = canonify(origoutfname);\r
+           if (!outfname) {\r
+               printf("%s: canonify: %s\n", origoutfname, fxp_error());\r
+               if (wcm) {\r
+                   sfree(wfname);\r
+                   finish_wildcard_matching(wcm);\r
+               }\r
+               return 0;\r
+           }\r
+           ret = sftp_put_file(wfname, outfname, recurse, restart);\r
+           sfree(outfname);\r
+\r
+           if (wcm) {\r
+               sfree(wfname);\r
+               wfname = wildcard_get_filename(wcm);\r
+           } else {\r
+               wfname = NULL;\r
+           }\r
+       }\r
+\r
+       if (wcm)\r
+           finish_wildcard_matching(wcm);\r
+\r
+       if (!ret)\r
+           return ret;\r
+\r
+    } while (multiple && i < cmd->nwords);\r
+\r
+    return ret;\r
+}\r
+int sftp_cmd_put(struct sftp_command *cmd)\r
+{\r
+    return sftp_general_put(cmd, 0, 0);\r
+}\r
+int sftp_cmd_mput(struct sftp_command *cmd)\r
+{\r
+    return sftp_general_put(cmd, 0, 1);\r
+}\r
+int sftp_cmd_reput(struct sftp_command *cmd)\r
+{\r
+    return sftp_general_put(cmd, 1, 0);\r
+}\r
+\r
+int sftp_cmd_mkdir(struct sftp_command *cmd)\r
+{\r
+    char *dir;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    int result;\r
+    int i, ret;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 2) {\r
+       printf("mkdir: expects a directory\n");\r
+       return 0;\r
+    }\r
+\r
+    ret = 1;\r
+    for (i = 1; i < cmd->nwords; i++) {\r
+       dir = canonify(cmd->words[i]);\r
+       if (!dir) {\r
+           printf("%s: canonify: %s\n", dir, fxp_error());\r
+           return 0;\r
+       }\r
+\r
+       sftp_register(req = fxp_mkdir_send(dir));\r
+       rreq = sftp_find_request(pktin = sftp_recv());\r
+       assert(rreq == req);\r
+       result = fxp_mkdir_recv(pktin, rreq);\r
+\r
+       if (!result) {\r
+           printf("mkdir %s: %s\n", dir, fxp_error());\r
+           ret = 0;\r
+       } else\r
+           printf("mkdir %s: OK\n", dir);\r
+\r
+       sfree(dir);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+static int sftp_action_rmdir(void *vctx, char *dir)\r
+{\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    int result;\r
+\r
+    sftp_register(req = fxp_rmdir_send(dir));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    result = fxp_rmdir_recv(pktin, rreq);\r
+\r
+    if (!result) {\r
+       printf("rmdir %s: %s\n", dir, fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    printf("rmdir %s: OK\n", dir);\r
+\r
+    return 1;\r
+}\r
+\r
+int sftp_cmd_rmdir(struct sftp_command *cmd)\r
+{\r
+    int i, ret;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 2) {\r
+       printf("rmdir: expects a directory\n");\r
+       return 0;\r
+    }\r
+\r
+    ret = 1;\r
+    for (i = 1; i < cmd->nwords; i++)\r
+       ret &= wildcard_iterate(cmd->words[i], sftp_action_rmdir, NULL);\r
+\r
+    return ret;\r
+}\r
+\r
+static int sftp_action_rm(void *vctx, char *fname)\r
+{\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    int result;\r
+\r
+    sftp_register(req = fxp_remove_send(fname));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    result = fxp_remove_recv(pktin, rreq);\r
+\r
+    if (!result) {\r
+       printf("rm %s: %s\n", fname, fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    printf("rm %s: OK\n", fname);\r
+\r
+    return 1;\r
+}\r
+\r
+int sftp_cmd_rm(struct sftp_command *cmd)\r
+{\r
+    int i, ret;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 2) {\r
+       printf("rm: expects a filename\n");\r
+       return 0;\r
+    }\r
+\r
+    ret = 1;\r
+    for (i = 1; i < cmd->nwords; i++)\r
+       ret &= wildcard_iterate(cmd->words[i], sftp_action_rm, NULL);\r
+\r
+    return ret;\r
+}\r
+\r
+static int check_is_dir(char *dstfname)\r
+{\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    struct fxp_attrs attrs;\r
+    int result;\r
+\r
+    sftp_register(req = fxp_stat_send(dstfname));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    result = fxp_stat_recv(pktin, rreq, &attrs);\r
+\r
+    if (result &&\r
+       (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&\r
+       (attrs.permissions & 0040000))\r
+       return TRUE;\r
+    else\r
+       return FALSE;\r
+}\r
+\r
+struct sftp_context_mv {\r
+    char *dstfname;\r
+    int dest_is_dir;\r
+};\r
+\r
+static int sftp_action_mv(void *vctx, char *srcfname)\r
+{\r
+    struct sftp_context_mv *ctx = (struct sftp_context_mv *)vctx;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    const char *error;\r
+    char *finalfname, *newcanon = NULL;\r
+    int ret, result;\r
+\r
+    if (ctx->dest_is_dir) {\r
+       char *p;\r
+       char *newname;\r
+\r
+       p = srcfname + strlen(srcfname);\r
+       while (p > srcfname && p[-1] != '/') p--;\r
+       newname = dupcat(ctx->dstfname, "/", p, NULL);\r
+       newcanon = canonify(newname);\r
+       if (!newcanon) {\r
+           printf("%s: canonify: %s\n", newname, fxp_error());\r
+           sfree(newname);\r
+           return 0;\r
+       }\r
+       sfree(newname);\r
+\r
+       finalfname = newcanon;\r
+    } else {\r
+       finalfname = ctx->dstfname;\r
+    }\r
+\r
+    sftp_register(req = fxp_rename_send(srcfname, finalfname));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    result = fxp_rename_recv(pktin, rreq);\r
+\r
+    error = result ? NULL : fxp_error();\r
+\r
+    if (error) {\r
+       printf("mv %s %s: %s\n", srcfname, finalfname, error);\r
+       ret = 0;\r
+    } else {\r
+       printf("%s -> %s\n", srcfname, finalfname);\r
+       ret = 1;\r
+    }\r
+\r
+    sfree(newcanon);\r
+    return ret;\r
+}\r
+\r
+int sftp_cmd_mv(struct sftp_command *cmd)\r
+{\r
+    struct sftp_context_mv actx, *ctx = &actx;\r
+    int i, ret;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 3) {\r
+       printf("mv: expects two filenames\n");\r
+       return 0;\r
+    }\r
+\r
+    ctx->dstfname = canonify(cmd->words[cmd->nwords-1]);\r
+    if (!ctx->dstfname) {\r
+       printf("%s: canonify: %s\n", ctx->dstfname, fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    /*\r
+     * If there's more than one source argument, or one source\r
+     * argument which is a wildcard, we _require_ that the\r
+     * destination is a directory.\r
+     */\r
+    ctx->dest_is_dir = check_is_dir(ctx->dstfname);\r
+    if ((cmd->nwords > 3 || is_wildcard(cmd->words[1])) && !ctx->dest_is_dir) {\r
+       printf("mv: multiple or wildcard arguments require the destination"\r
+              " to be a directory\n");\r
+       sfree(ctx->dstfname);\r
+       return 0;\r
+    }\r
+\r
+    /*\r
+     * Now iterate over the source arguments.\r
+     */\r
+    ret = 1;\r
+    for (i = 1; i < cmd->nwords-1; i++)\r
+       ret &= wildcard_iterate(cmd->words[i], sftp_action_mv, ctx);\r
+\r
+    sfree(ctx->dstfname);\r
+    return ret;\r
+}\r
+\r
+struct sftp_context_chmod {\r
+    unsigned attrs_clr, attrs_xor;\r
+};\r
+\r
+static int sftp_action_chmod(void *vctx, char *fname)\r
+{\r
+    struct fxp_attrs attrs;\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+    int result;\r
+    unsigned oldperms, newperms;\r
+    struct sftp_context_chmod *ctx = (struct sftp_context_chmod *)vctx;\r
+\r
+    sftp_register(req = fxp_stat_send(fname));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    result = fxp_stat_recv(pktin, rreq, &attrs);\r
+\r
+    if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {\r
+       printf("get attrs for %s: %s\n", fname,\r
+              result ? "file permissions not provided" : fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS;   /* perms _only_ */\r
+    oldperms = attrs.permissions & 07777;\r
+    attrs.permissions &= ~ctx->attrs_clr;\r
+    attrs.permissions ^= ctx->attrs_xor;\r
+    newperms = attrs.permissions & 07777;\r
+\r
+    if (oldperms == newperms)\r
+       return 1;                      /* no need to do anything! */\r
+\r
+    sftp_register(req = fxp_setstat_send(fname, attrs));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    result = fxp_setstat_recv(pktin, rreq);\r
+\r
+    if (!result) {\r
+       printf("set attrs for %s: %s\n", fname, fxp_error());\r
+       return 0;\r
+    }\r
+\r
+    printf("%s: %04o -> %04o\n", fname, oldperms, newperms);\r
+\r
+    return 1;\r
+}\r
+\r
+int sftp_cmd_chmod(struct sftp_command *cmd)\r
+{\r
+    char *mode;\r
+    int i, ret;\r
+    struct sftp_context_chmod actx, *ctx = &actx;\r
+\r
+    if (back == NULL) {\r
+       not_connected();\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 3) {\r
+       printf("chmod: expects a mode specifier and a filename\n");\r
+       return 0;\r
+    }\r
+\r
+    /*\r
+     * Attempt to parse the mode specifier in cmd->words[1]. We\r
+     * don't support the full horror of Unix chmod; instead we\r
+     * support a much simpler syntax in which the user can either\r
+     * specify an octal number, or a comma-separated sequence of\r
+     * [ugoa]*[-+=][rwxst]+. (The initial [ugoa] sequence may\r
+     * _only_ be omitted if the only attribute mentioned is t,\r
+     * since all others require a user/group/other specification.\r
+     * Additionally, the s attribute may not be specified for any\r
+     * [ugoa] specifications other than exactly u or exactly g.\r
+     */\r
+    ctx->attrs_clr = ctx->attrs_xor = 0;\r
+    mode = cmd->words[1];\r
+    if (mode[0] >= '0' && mode[0] <= '9') {\r
+       if (mode[strspn(mode, "01234567")]) {\r
+           printf("chmod: numeric file modes should"\r
+                  " contain digits 0-7 only\n");\r
+           return 0;\r
+       }\r
+       ctx->attrs_clr = 07777;\r
+       sscanf(mode, "%o", &ctx->attrs_xor);\r
+       ctx->attrs_xor &= ctx->attrs_clr;\r
+    } else {\r
+       while (*mode) {\r
+           char *modebegin = mode;\r
+           unsigned subset, perms;\r
+           int action;\r
+\r
+           subset = 0;\r
+           while (*mode && *mode != ',' &&\r
+                  *mode != '+' && *mode != '-' && *mode != '=') {\r
+               switch (*mode) {\r
+                 case 'u': subset |= 04700; break; /* setuid, user perms */\r
+                 case 'g': subset |= 02070; break; /* setgid, group perms */\r
+                 case 'o': subset |= 00007; break; /* just other perms */\r
+                 case 'a': subset |= 06777; break; /* all of the above */\r
+                 default:\r
+                   printf("chmod: file mode '%.*s' contains unrecognised"\r
+                          " user/group/other specifier '%c'\n",\r
+                          (int)strcspn(modebegin, ","), modebegin, *mode);\r
+                   return 0;\r
+               }\r
+               mode++;\r
+           }\r
+           if (!*mode || *mode == ',') {\r
+               printf("chmod: file mode '%.*s' is incomplete\n",\r
+                      (int)strcspn(modebegin, ","), modebegin);\r
+               return 0;\r
+           }\r
+           action = *mode++;\r
+           if (!*mode || *mode == ',') {\r
+               printf("chmod: file mode '%.*s' is incomplete\n",\r
+                      (int)strcspn(modebegin, ","), modebegin);\r
+               return 0;\r
+           }\r
+           perms = 0;\r
+           while (*mode && *mode != ',') {\r
+               switch (*mode) {\r
+                 case 'r': perms |= 00444; break;\r
+                 case 'w': perms |= 00222; break;\r
+                 case 'x': perms |= 00111; break;\r
+                 case 't': perms |= 01000; subset |= 01000; break;\r
+                 case 's':\r
+                   if ((subset & 06777) != 04700 &&\r
+                       (subset & 06777) != 02070) {\r
+                       printf("chmod: file mode '%.*s': set[ug]id bit should"\r
+                              " be used with exactly one of u or g only\n",\r
+                              (int)strcspn(modebegin, ","), modebegin);\r
+                       return 0;\r
+                   }\r
+                   perms |= 06000;\r
+                   break;\r
+                 default:\r
+                   printf("chmod: file mode '%.*s' contains unrecognised"\r
+                          " permission specifier '%c'\n",\r
+                          (int)strcspn(modebegin, ","), modebegin, *mode);\r
+                   return 0;\r
+               }\r
+               mode++;\r
+           }\r
+           if (!(subset & 06777) && (perms &~ subset)) {\r
+               printf("chmod: file mode '%.*s' contains no user/group/other"\r
+                      " specifier and permissions other than 't' \n",\r
+                      (int)strcspn(modebegin, ","), modebegin);\r
+               return 0;\r
+           }\r
+           perms &= subset;\r
+           switch (action) {\r
+             case '+':\r
+               ctx->attrs_clr |= perms;\r
+               ctx->attrs_xor |= perms;\r
+               break;\r
+             case '-':\r
+               ctx->attrs_clr |= perms;\r
+               ctx->attrs_xor &= ~perms;\r
+               break;\r
+             case '=':\r
+               ctx->attrs_clr |= subset;\r
+               ctx->attrs_xor |= perms;\r
+               break;\r
+           }\r
+           if (*mode) mode++;         /* eat comma */\r
+       }\r
+    }\r
+\r
+    ret = 1;\r
+    for (i = 2; i < cmd->nwords; i++)\r
+       ret &= wildcard_iterate(cmd->words[i], sftp_action_chmod, ctx);\r
+\r
+    return ret;\r
+}\r
+\r
+static int sftp_cmd_open(struct sftp_command *cmd)\r
+{\r
+    int portnumber;\r
+\r
+    if (back != NULL) {\r
+       printf("psftp: already connected\n");\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords < 2) {\r
+       printf("open: expects a host name\n");\r
+       return 0;\r
+    }\r
+\r
+    if (cmd->nwords > 2) {\r
+       portnumber = atoi(cmd->words[2]);\r
+       if (portnumber == 0) {\r
+           printf("open: invalid port number\n");\r
+           return 0;\r
+       }\r
+    } else\r
+       portnumber = 0;\r
+\r
+    if (psftp_connect(cmd->words[1], NULL, portnumber)) {\r
+       back = NULL;                   /* connection is already closed */\r
+       return -1;                     /* this is fatal */\r
+    }\r
+    do_sftp_init();\r
+    return 1;\r
+}\r
+\r
+static int sftp_cmd_lcd(struct sftp_command *cmd)\r
+{\r
+    char *currdir, *errmsg;\r
+\r
+    if (cmd->nwords < 2) {\r
+       printf("lcd: expects a local directory name\n");\r
+       return 0;\r
+    }\r
+\r
+    errmsg = psftp_lcd(cmd->words[1]);\r
+    if (errmsg) {\r
+       printf("lcd: unable to change directory: %s\n", errmsg);\r
+       sfree(errmsg);\r
+       return 0;\r
+    }\r
+\r
+    currdir = psftp_getcwd();\r
+    printf("New local directory is %s\n", currdir);\r
+    sfree(currdir);\r
+\r
+    return 1;\r
+}\r
+\r
+static int sftp_cmd_lpwd(struct sftp_command *cmd)\r
+{\r
+    char *currdir;\r
+\r
+    currdir = psftp_getcwd();\r
+    printf("Current local directory is %s\n", currdir);\r
+    sfree(currdir);\r
+\r
+    return 1;\r
+}\r
+\r
+static int sftp_cmd_pling(struct sftp_command *cmd)\r
+{\r
+    int exitcode;\r
+\r
+    exitcode = system(cmd->words[1]);\r
+    return (exitcode == 0);\r
+}\r
+\r
+static int sftp_cmd_help(struct sftp_command *cmd);\r
+\r
+static struct sftp_cmd_lookup {\r
+    char *name;\r
+    /*\r
+     * For help purposes, there are two kinds of command:\r
+     * \r
+     *  - primary commands, in which `longhelp' is non-NULL. In\r
+     *    this case `shorthelp' is descriptive text, and `longhelp'\r
+     *    is longer descriptive text intended to be printed after\r
+     *    the command name.\r
+     * \r
+     *  - alias commands, in which `longhelp' is NULL. In this case\r
+     *    `shorthelp' is the name of a primary command, which\r
+     *    contains the help that should double up for this command.\r
+     */\r
+    int listed;                               /* do we list this in primary help? */\r
+    char *shorthelp;\r
+    char *longhelp;\r
+    int (*obey) (struct sftp_command *);\r
+} sftp_lookup[] = {\r
+    /*\r
+     * List of sftp commands. This is binary-searched so it MUST be\r
+     * in ASCII order.\r
+     */\r
+    {\r
+       "!", TRUE, "run a local command",\r
+           "<command>\n"\r
+           /* FIXME: this example is crap for non-Windows. */\r
+           "  Runs a local command. For example, \"!del myfile\".\n",\r
+           sftp_cmd_pling\r
+    },\r
+    {\r
+       "bye", TRUE, "finish your SFTP session",\r
+           "\n"\r
+           "  Terminates your SFTP session and quits the PSFTP program.\n",\r
+           sftp_cmd_quit\r
+    },\r
+    {\r
+       "cd", TRUE, "change your remote working directory",\r
+           " [ <new working directory> ]\n"\r
+           "  Change the remote working directory for your SFTP session.\n"\r
+           "  If a new working directory is not supplied, you will be\n"\r
+           "  returned to your home directory.\n",\r
+           sftp_cmd_cd\r
+    },\r
+    {\r
+       "chmod", TRUE, "change file permissions and modes",\r
+           " <modes> <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"\r
+           "  Change the file permissions on one or more remote files or\n"\r
+           "  directories.\n"\r
+           "  <modes> can be any octal Unix permission specifier.\n"\r
+           "  Alternatively, <modes> can include the following modifiers:\n"\r
+           "    u+r     make file readable by owning user\n"\r
+           "    u+w     make file writable by owning user\n"\r
+           "    u+x     make file executable by owning user\n"\r
+           "    u-r     make file not readable by owning user\n"\r
+           "    [also u-w, u-x]\n"\r
+           "    g+r     make file readable by members of owning group\n"\r
+           "    [also g+w, g+x, g-r, g-w, g-x]\n"\r
+           "    o+r     make file readable by all other users\n"\r
+           "    [also o+w, o+x, o-r, o-w, o-x]\n"\r
+           "    a+r     make file readable by absolutely everybody\n"\r
+           "    [also a+w, a+x, a-r, a-w, a-x]\n"\r
+           "    u+s     enable the Unix set-user-ID bit\n"\r
+           "    u-s     disable the Unix set-user-ID bit\n"\r
+           "    g+s     enable the Unix set-group-ID bit\n"\r
+           "    g-s     disable the Unix set-group-ID bit\n"\r
+           "    +t      enable the Unix \"sticky bit\"\n"\r
+           "  You can give more than one modifier for the same user (\"g-rwx\"), and\n"\r
+           "  more than one user for the same modifier (\"ug+w\"). You can\n"\r
+           "  use commas to separate different modifiers (\"u+rwx,g+s\").\n",\r
+           sftp_cmd_chmod\r
+    },\r
+    {\r
+       "close", TRUE, "finish your SFTP session but do not quit PSFTP",\r
+           "\n"\r
+           "  Terminates your SFTP session, but does not quit the PSFTP\n"\r
+           "  program. You can then use \"open\" to start another SFTP\n"\r
+           "  session, to the same server or to a different one.\n",\r
+           sftp_cmd_close\r
+    },\r
+    {\r
+       "del", TRUE, "delete files on the remote server",\r
+           " <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"\r
+           "  Delete a file or files from the server.\n",\r
+           sftp_cmd_rm\r
+    },\r
+    {\r
+       "delete", FALSE, "del", NULL, sftp_cmd_rm\r
+    },\r
+    {\r
+       "dir", TRUE, "list remote files",\r
+           " [ <directory-name> ]/[ <wildcard> ]\n"\r
+           "  List the contents of a specified directory on the server.\n"\r
+           "  If <directory-name> is not given, the current working directory\n"\r
+           "  is assumed.\n"\r
+           "  If <wildcard> is given, it is treated as a set of files to\n"\r
+           "  list; otherwise, all files are listed.\n",\r
+           sftp_cmd_ls\r
+    },\r
+    {\r
+       "exit", TRUE, "bye", NULL, sftp_cmd_quit\r
+    },\r
+    {\r
+       "get", TRUE, "download a file from the server to your local machine",\r
+           " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"\r
+           "  Downloads a file on the server and stores it locally under\n"\r
+           "  the same name, or under a different one if you supply the\n"\r
+           "  argument <local-filename>.\n"\r
+           "  If -r specified, recursively fetch a directory.\n",\r
+           sftp_cmd_get\r
+    },\r
+    {\r
+       "help", TRUE, "give help",\r
+           " [ <command> [ <command> ... ] ]\n"\r
+           "  Give general help if no commands are specified.\n"\r
+           "  If one or more commands are specified, give specific help on\n"\r
+           "  those particular commands.\n",\r
+           sftp_cmd_help\r
+    },\r
+    {\r
+       "lcd", TRUE, "change local working directory",\r
+           " <local-directory-name>\n"\r
+           "  Change the local working directory of the PSFTP program (the\n"\r
+           "  default location where the \"get\" command will save files).\n",\r
+           sftp_cmd_lcd\r
+    },\r
+    {\r
+       "lpwd", TRUE, "print local working directory",\r
+           "\n"\r
+           "  Print the local working directory of the PSFTP program (the\n"\r
+           "  default location where the \"get\" command will save files).\n",\r
+           sftp_cmd_lpwd\r
+    },\r
+    {\r
+       "ls", TRUE, "dir", NULL,\r
+           sftp_cmd_ls\r
+    },\r
+    {\r
+       "mget", TRUE, "download multiple files at once",\r
+           " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"\r
+           "  Downloads many files from the server, storing each one under\n"\r
+           "  the same name it has on the server side. You can use wildcards\n"\r
+           "  such as \"*.c\" to specify lots of files at once.\n"\r
+           "  If -r specified, recursively fetch files and directories.\n",\r
+           sftp_cmd_mget\r
+    },\r
+    {\r
+       "mkdir", TRUE, "create directories on the remote server",\r
+           " <directory-name> [ <directory-name>... ]\n"\r
+           "  Creates directories with the given names on the server.\n",\r
+           sftp_cmd_mkdir\r
+    },\r
+    {\r
+       "mput", TRUE, "upload multiple files at once",\r
+           " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"\r
+           "  Uploads many files to the server, storing each one under the\n"\r
+           "  same name it has on the client side. You can use wildcards\n"\r
+           "  such as \"*.c\" to specify lots of files at once.\n"\r
+           "  If -r specified, recursively store files and directories.\n",\r
+           sftp_cmd_mput\r
+    },\r
+    {\r
+       "mv", TRUE, "move or rename file(s) on the remote server",\r
+           " <source> [ <source>... ] <destination>\n"\r
+           "  Moves or renames <source>(s) on the server to <destination>,\n"\r
+           "  also on the server.\n"\r
+           "  If <destination> specifies an existing directory, then <source>\n"\r
+           "  may be a wildcard, and multiple <source>s may be given; all\n"\r
+           "  source files are moved into <destination>.\n"\r
+           "  Otherwise, <source> must specify a single file, which is moved\n"\r
+           "  or renamed so that it is accessible under the name <destination>.\n",\r
+           sftp_cmd_mv\r
+    },\r
+    {\r
+       "open", TRUE, "connect to a host",\r
+           " [<user>@]<hostname> [<port>]\n"\r
+           "  Establishes an SFTP connection to a given host. Only usable\n"\r
+           "  when you are not already connected to a server.\n",\r
+           sftp_cmd_open\r
+    },\r
+    {\r
+       "put", TRUE, "upload a file from your local machine to the server",\r
+           " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"\r
+           "  Uploads a file to the server and stores it there under\n"\r
+           "  the same name, or under a different one if you supply the\n"\r
+           "  argument <remote-filename>.\n"\r
+           "  If -r specified, recursively store a directory.\n",\r
+           sftp_cmd_put\r
+    },\r
+    {\r
+       "pwd", TRUE, "print your remote working directory",\r
+           "\n"\r
+           "  Print the current remote working directory for your SFTP session.\n",\r
+           sftp_cmd_pwd\r
+    },\r
+    {\r
+       "quit", TRUE, "bye", NULL,\r
+           sftp_cmd_quit\r
+    },\r
+    {\r
+       "reget", TRUE, "continue downloading files",\r
+           " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"\r
+           "  Works exactly like the \"get\" command, but the local file\n"\r
+           "  must already exist. The download will begin at the end of the\n"\r
+           "  file. This is for resuming a download that was interrupted.\n"\r
+           "  If -r specified, resume interrupted \"get -r\".\n",\r
+           sftp_cmd_reget\r
+    },\r
+    {\r
+       "ren", TRUE, "mv", NULL,\r
+           sftp_cmd_mv\r
+    },\r
+    {\r
+       "rename", FALSE, "mv", NULL,\r
+           sftp_cmd_mv\r
+    },\r
+    {\r
+       "reput", TRUE, "continue uploading files",\r
+           " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"\r
+           "  Works exactly like the \"put\" command, but the remote file\n"\r
+           "  must already exist. The upload will begin at the end of the\n"\r
+           "  file. This is for resuming an upload that was interrupted.\n"\r
+           "  If -r specified, resume interrupted \"put -r\".\n",\r
+           sftp_cmd_reput\r
+    },\r
+    {\r
+       "rm", TRUE, "del", NULL,\r
+           sftp_cmd_rm\r
+    },\r
+    {\r
+       "rmdir", TRUE, "remove directories on the remote server",\r
+           " <directory-name> [ <directory-name>... ]\n"\r
+           "  Removes the directory with the given name on the server.\n"\r
+           "  The directory will not be removed unless it is empty.\n"\r
+           "  Wildcards may be used to specify multiple directories.\n",\r
+           sftp_cmd_rmdir\r
+    }\r
+};\r
+\r
+const struct sftp_cmd_lookup *lookup_command(char *name)\r
+{\r
+    int i, j, k, cmp;\r
+\r
+    i = -1;\r
+    j = sizeof(sftp_lookup) / sizeof(*sftp_lookup);\r
+    while (j - i > 1) {\r
+       k = (j + i) / 2;\r
+       cmp = strcmp(name, sftp_lookup[k].name);\r
+       if (cmp < 0)\r
+           j = k;\r
+       else if (cmp > 0)\r
+           i = k;\r
+       else {\r
+           return &sftp_lookup[k];\r
+       }\r
+    }\r
+    return NULL;\r
+}\r
+\r
+static int sftp_cmd_help(struct sftp_command *cmd)\r
+{\r
+    int i;\r
+    if (cmd->nwords == 1) {\r
+       /*\r
+        * Give short help on each command.\r
+        */\r
+       int maxlen;\r
+       maxlen = 0;\r
+       for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) {\r
+           int len;\r
+           if (!sftp_lookup[i].listed)\r
+               continue;\r
+           len = strlen(sftp_lookup[i].name);\r
+           if (maxlen < len)\r
+               maxlen = len;\r
+       }\r
+       for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) {\r
+           const struct sftp_cmd_lookup *lookup;\r
+           if (!sftp_lookup[i].listed)\r
+               continue;\r
+           lookup = &sftp_lookup[i];\r
+           printf("%-*s", maxlen+2, lookup->name);\r
+           if (lookup->longhelp == NULL)\r
+               lookup = lookup_command(lookup->shorthelp);\r
+           printf("%s\n", lookup->shorthelp);\r
+       }\r
+    } else {\r
+       /*\r
+        * Give long help on specific commands.\r
+        */\r
+       for (i = 1; i < cmd->nwords; i++) {\r
+           const struct sftp_cmd_lookup *lookup;\r
+           lookup = lookup_command(cmd->words[i]);\r
+           if (!lookup) {\r
+               printf("help: %s: command not found\n", cmd->words[i]);\r
+           } else {\r
+               printf("%s", lookup->name);\r
+               if (lookup->longhelp == NULL)\r
+                   lookup = lookup_command(lookup->shorthelp);\r
+               printf("%s", lookup->longhelp);\r
+           }\r
+       }\r
+    }\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Command line reading and parsing.\r
+ */\r
+struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)\r
+{\r
+    char *line;\r
+    struct sftp_command *cmd;\r
+    char *p, *q, *r;\r
+    int quoting;\r
+\r
+    cmd = snew(struct sftp_command);\r
+    cmd->words = NULL;\r
+    cmd->nwords = 0;\r
+    cmd->wordssize = 0;\r
+\r
+    line = NULL;\r
+\r
+    if (fp) {\r
+       if (modeflags & 1)\r
+           printf("psftp> ");\r
+       line = fgetline(fp);\r
+    } else {\r
+       line = ssh_sftp_get_cmdline("psftp> ", back == NULL);\r
+    }\r
+\r
+    if (!line || !*line) {\r
+       cmd->obey = sftp_cmd_quit;\r
+       if ((mode == 0) || (modeflags & 1))\r
+           printf("quit\n");\r
+       return cmd;                    /* eof */\r
+    }\r
+\r
+    line[strcspn(line, "\r\n")] = '\0';\r
+\r
+    if (modeflags & 1) {\r
+       printf("%s\n", line);\r
+    }\r
+\r
+    p = line;\r
+    while (*p && (*p == ' ' || *p == '\t'))\r
+       p++;\r
+\r
+    if (*p == '!') {\r
+       /*\r
+        * Special case: the ! command. This is always parsed as\r
+        * exactly two words: one containing the !, and the second\r
+        * containing everything else on the line.\r
+        */\r
+       cmd->nwords = cmd->wordssize = 2;\r
+       cmd->words = sresize(cmd->words, cmd->wordssize, char *);\r
+       cmd->words[0] = dupstr("!");\r
+       cmd->words[1] = dupstr(p+1);\r
+    } else if (*p == '#') {\r
+       /*\r
+        * Special case: comment. Entire line is ignored.\r
+        */\r
+       cmd->nwords = cmd->wordssize = 0;\r
+    } else {\r
+\r
+       /*\r
+        * Parse the command line into words. The syntax is:\r
+        *  - double quotes are removed, but cause spaces within to be\r
+        *    treated as non-separating.\r
+        *  - a double-doublequote pair is a literal double quote, inside\r
+        *    _or_ outside quotes. Like this:\r
+        *\r
+        *      firstword "second word" "this has ""quotes"" in" and""this""\r
+        *\r
+        * becomes\r
+        *\r
+        *      >firstword<\r
+        *      >second word<\r
+        *      >this has "quotes" in<\r
+        *      >and"this"<\r
+        */\r
+       while (*p) {\r
+           /* skip whitespace */\r
+           while (*p && (*p == ' ' || *p == '\t'))\r
+               p++;\r
+           /* mark start of word */\r
+           q = r = p;                 /* q sits at start, r writes word */\r
+           quoting = 0;\r
+           while (*p) {\r
+               if (!quoting && (*p == ' ' || *p == '\t'))\r
+                   break;                     /* reached end of word */\r
+               else if (*p == '"' && p[1] == '"')\r
+                   p += 2, *r++ = '"';    /* a literal quote */\r
+               else if (*p == '"')\r
+                   p++, quoting = !quoting;\r
+               else\r
+                   *r++ = *p++;\r
+           }\r
+           if (*p)\r
+               p++;                   /* skip over the whitespace */\r
+           *r = '\0';\r
+           if (cmd->nwords >= cmd->wordssize) {\r
+               cmd->wordssize = cmd->nwords + 16;\r
+               cmd->words = sresize(cmd->words, cmd->wordssize, char *);\r
+           }\r
+           cmd->words[cmd->nwords++] = dupstr(q);\r
+       }\r
+    }\r
+\r
+    sfree(line);\r
+\r
+    /*\r
+     * Now parse the first word and assign a function.\r
+     */\r
+\r
+    if (cmd->nwords == 0)\r
+       cmd->obey = sftp_cmd_null;\r
+    else {\r
+       const struct sftp_cmd_lookup *lookup;\r
+       lookup = lookup_command(cmd->words[0]);\r
+       if (!lookup)\r
+           cmd->obey = sftp_cmd_unknown;\r
+       else\r
+           cmd->obey = lookup->obey;\r
+    }\r
+\r
+    return cmd;\r
+}\r
+\r
+static int do_sftp_init(void)\r
+{\r
+    struct sftp_packet *pktin;\r
+    struct sftp_request *req, *rreq;\r
+\r
+    /*\r
+     * Do protocol initialisation. \r
+     */\r
+    if (!fxp_init()) {\r
+       fprintf(stderr,\r
+               "Fatal: unable to initialise SFTP: %s\n", fxp_error());\r
+       return 1;                      /* failure */\r
+    }\r
+\r
+    /*\r
+     * Find out where our home directory is.\r
+     */\r
+    sftp_register(req = fxp_realpath_send("."));\r
+    rreq = sftp_find_request(pktin = sftp_recv());\r
+    assert(rreq == req);\r
+    homedir = fxp_realpath_recv(pktin, rreq);\r
+\r
+    if (!homedir) {\r
+       fprintf(stderr,\r
+               "Warning: failed to resolve home directory: %s\n",\r
+               fxp_error());\r
+       homedir = dupstr(".");\r
+    } else {\r
+       printf("Remote working directory is %s\n", homedir);\r
+    }\r
+    pwd = dupstr(homedir);\r
+    return 0;\r
+}\r
+\r
+void do_sftp_cleanup()\r
+{\r
+    char ch;\r
+    if (back) {\r
+       back->special(backhandle, TS_EOF);\r
+       sftp_recvdata(&ch, 1);\r
+       back->free(backhandle);\r
+       sftp_cleanup_request();\r
+       back = NULL;\r
+       backhandle = NULL;\r
+    }\r
+    if (pwd) {\r
+       sfree(pwd);\r
+       pwd = NULL;\r
+    }\r
+    if (homedir) {\r
+       sfree(homedir);\r
+       homedir = NULL;\r
+    }\r
+}\r
+\r
+void do_sftp(int mode, int modeflags, char *batchfile)\r
+{\r
+    FILE *fp;\r
+    int ret;\r
+\r
+    /*\r
+     * Batch mode?\r
+     */\r
+    if (mode == 0) {\r
+\r
+        /* ------------------------------------------------------------------\r
+         * Now we're ready to do Real Stuff.\r
+         */\r
+        while (1) {\r
+           struct sftp_command *cmd;\r
+           cmd = sftp_getcmd(NULL, 0, 0);\r
+           if (!cmd)\r
+               break;\r
+           ret = cmd->obey(cmd);\r
+           if (cmd->words) {\r
+               int i;\r
+               for(i = 0; i < cmd->nwords; i++)\r
+                   sfree(cmd->words[i]);\r
+               sfree(cmd->words);\r
+           }\r
+           sfree(cmd);\r
+           if (ret < 0)\r
+               break;\r
+       }\r
+    } else {\r
+        fp = fopen(batchfile, "r");\r
+        if (!fp) {\r
+           printf("Fatal: unable to open %s\n", batchfile);\r
+           return;\r
+        }\r
+        while (1) {\r
+           struct sftp_command *cmd;\r
+           cmd = sftp_getcmd(fp, mode, modeflags);\r
+           if (!cmd)\r
+               break;\r
+           ret = cmd->obey(cmd);\r
+           if (ret < 0)\r
+               break;\r
+           if (ret == 0) {\r
+               if (!(modeflags & 2))\r
+                   break;\r
+           }\r
+        }\r
+       fclose(fp);\r
+\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Dirty bits: integration with PuTTY.\r
+ */\r
+\r
+static int verbose = 0;\r
+\r
+/*\r
+ *  Print an error message and perform a fatal exit.\r
+ */\r
+void fatalbox(char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    str2 = dupcat("Fatal: ", str, "\n", NULL);\r
+    sfree(str);\r
+    va_end(ap);\r
+    fputs(str2, stderr);\r
+    sfree(str2);\r
+\r
+    cleanup_exit(1);\r
+}\r
+void modalfatalbox(char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    str2 = dupcat("Fatal: ", str, "\n", NULL);\r
+    sfree(str);\r
+    va_end(ap);\r
+    fputs(str2, stderr);\r
+    sfree(str2);\r
+\r
+    cleanup_exit(1);\r
+}\r
+void connection_fatal(void *frontend, char *fmt, ...)\r
+{\r
+    char *str, *str2;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    str = dupvprintf(fmt, ap);\r
+    str2 = dupcat("Fatal: ", str, "\n", NULL);\r
+    sfree(str);\r
+    va_end(ap);\r
+    fputs(str2, stderr);\r
+    sfree(str2);\r
+\r
+    cleanup_exit(1);\r
+}\r
+\r
+void ldisc_send(void *handle, char *buf, int len, int interactive)\r
+{\r
+    /*\r
+     * This is only here because of the calls to ldisc_send(NULL,\r
+     * 0) in ssh.c. Nothing in PSFTP actually needs to use the\r
+     * ldisc as an ldisc. So if we get called with any real data, I\r
+     * want to know about it.\r
+     */\r
+    assert(len == 0);\r
+}\r
+\r
+/*\r
+ * In psftp, all agent requests should be synchronous, so this is a\r
+ * never-called stub.\r
+ */\r
+void agent_schedule_callback(void (*callback)(void *, void *, int),\r
+                            void *callback_ctx, void *data, int len)\r
+{\r
+    assert(!"We shouldn't be here");\r
+}\r
+\r
+/*\r
+ * Receive a block of data from the SSH link. Block until all data\r
+ * is available.\r
+ *\r
+ * To do this, we repeatedly call the SSH protocol module, with our\r
+ * own trap in from_backend() to catch the data that comes back. We\r
+ * do this until we have enough data.\r
+ */\r
+\r
+static unsigned char *outptr;         /* where to put the data */\r
+static unsigned outlen;                       /* how much data required */\r
+static unsigned char *pending = NULL;  /* any spare data */\r
+static unsigned pendlen = 0, pendsize = 0;     /* length and phys. size of buffer */\r
+int from_backend(void *frontend, int is_stderr, const char *data, int datalen)\r
+{\r
+    unsigned char *p = (unsigned char *) data;\r
+    unsigned len = (unsigned) datalen;\r
+\r
+    /*\r
+     * stderr data is just spouted to local stderr and otherwise\r
+     * ignored.\r
+     */\r
+    if (is_stderr) {\r
+       if (len > 0)\r
+           if (fwrite(data, 1, len, stderr) < len)\r
+               /* oh well */;\r
+       return 0;\r
+    }\r
+\r
+    /*\r
+     * If this is before the real session begins, just return.\r
+     */\r
+    if (!outptr)\r
+       return 0;\r
+\r
+    if ((outlen > 0) && (len > 0)) {\r
+       unsigned used = outlen;\r
+       if (used > len)\r
+           used = len;\r
+       memcpy(outptr, p, used);\r
+       outptr += used;\r
+       outlen -= used;\r
+       p += used;\r
+       len -= used;\r
+    }\r
+\r
+    if (len > 0) {\r
+       if (pendsize < pendlen + len) {\r
+           pendsize = pendlen + len + 4096;\r
+           pending = sresize(pending, pendsize, unsigned char);\r
+       }\r
+       memcpy(pending + pendlen, p, len);\r
+       pendlen += len;\r
+    }\r
+\r
+    return 0;\r
+}\r
+int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
+{\r
+    /*\r
+     * No "untrusted" output should get here (the way the code is\r
+     * currently, it's all diverted by FLAG_STDERR).\r
+     */\r
+    assert(!"Unexpected call to from_backend_untrusted()");\r
+    return 0; /* not reached */\r
+}\r
+int sftp_recvdata(char *buf, int len)\r
+{\r
+    outptr = (unsigned char *) buf;\r
+    outlen = len;\r
+\r
+    /*\r
+     * See if the pending-input block contains some of what we\r
+     * need.\r
+     */\r
+    if (pendlen > 0) {\r
+       unsigned pendused = pendlen;\r
+       if (pendused > outlen)\r
+           pendused = outlen;\r
+       memcpy(outptr, pending, pendused);\r
+       memmove(pending, pending + pendused, pendlen - pendused);\r
+       outptr += pendused;\r
+       outlen -= pendused;\r
+       pendlen -= pendused;\r
+       if (pendlen == 0) {\r
+           pendsize = 0;\r
+           sfree(pending);\r
+           pending = NULL;\r
+       }\r
+       if (outlen == 0)\r
+           return 1;\r
+    }\r
+\r
+    while (outlen > 0) {\r
+       if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)\r
+           return 0;                  /* doom */\r
+    }\r
+\r
+    return 1;\r
+}\r
+int sftp_senddata(char *buf, int len)\r
+{\r
+    back->send(backhandle, buf, len);\r
+    return 1;\r
+}\r
+\r
+/*\r
+ *  Short description of parameters.\r
+ */\r
+static void usage(void)\r
+{\r
+    printf("PuTTY Secure File Transfer (SFTP) client\n");\r
+    printf("%s\n", ver);\r
+    printf("Usage: psftp [options] [user@]host\n");\r
+    printf("Options:\n");\r
+    printf("  -V        print version information and exit\n");\r
+    printf("  -pgpfp    print PGP key fingerprints and exit\n");\r
+    printf("  -b file   use specified batchfile\n");\r
+    printf("  -bc       output batchfile commands\n");\r
+    printf("  -be       don't stop batchfile processing if errors\n");\r
+    printf("  -v        show verbose messages\n");\r
+    printf("  -load sessname  Load settings from saved session\n");\r
+    printf("  -l user   connect with specified username\n");\r
+    printf("  -P port   connect to specified port\n");\r
+    printf("  -pw passw login with specified password\n");\r
+    printf("  -1 -2     force use of particular SSH protocol version\n");\r
+    printf("  -4 -6     force use of IPv4 or IPv6\n");\r
+    printf("  -C        enable compression\n");\r
+    printf("  -i key    private key file for authentication\n");\r
+    printf("  -noagent  disable use of Pageant\n");\r
+    printf("  -agent    enable use of Pageant\n");\r
+    printf("  -batch    disable all interactive prompts\n");\r
+    cleanup_exit(1);\r
+}\r
+\r
+static void version(void)\r
+{\r
+  printf("psftp: %s\n", ver);\r
+  cleanup_exit(1);\r
+}\r
+\r
+/*\r
+ * Connect to a host.\r
+ */\r
+static int psftp_connect(char *userhost, char *user, int portnumber)\r
+{\r
+    char *host, *realhost;\r
+    const char *err;\r
+    void *logctx;\r
+\r
+    /* Separate host and username */\r
+    host = userhost;\r
+    host = strrchr(host, '@');\r
+    if (host == NULL) {\r
+       host = userhost;\r
+    } else {\r
+       *host++ = '\0';\r
+       if (user) {\r
+           printf("psftp: multiple usernames specified; using \"%s\"\n",\r
+                  user);\r
+       } else\r
+           user = userhost;\r
+    }\r
+\r
+    /*\r
+     * If we haven't loaded session details already (e.g., from -load),\r
+     * try looking for a session called "host".\r
+     */\r
+    if (!loaded_session) {\r
+       /* Try to load settings for `host' into a temporary config */\r
+       Config cfg2;\r
+       cfg2.host[0] = '\0';\r
+       do_defaults(host, &cfg2);\r
+       if (cfg2.host[0] != '\0') {\r
+           /* Settings present and include hostname */\r
+           /* Re-load data into the real config. */\r
+           do_defaults(host, &cfg);\r
+       } else {\r
+           /* Session doesn't exist or mention a hostname. */\r
+           /* Use `host' as a bare hostname. */\r
+           strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
+           cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+       }\r
+    } else {\r
+       /* Patch in hostname `host' to session details. */\r
+       strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
+       cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+    }\r
+\r
+    /*\r
+     * Force use of SSH. (If they got the protocol wrong we assume the\r
+     * port is useless too.)\r
+     */\r
+    if (cfg.protocol != PROT_SSH) {\r
+        cfg.protocol = PROT_SSH;\r
+        cfg.port = 22;\r
+    }\r
+\r
+    /*\r
+     * If saved session / Default Settings says SSH-1 (`1 only' or `1'),\r
+     * then change it to SSH-2, on the grounds that that's more likely to\r
+     * work for SFTP. (Can be overridden with `-1' option.)\r
+     * But if it says `2 only' or `2', respect which.\r
+     */\r
+    if (cfg.sshprot != 2 && cfg.sshprot != 3)\r
+       cfg.sshprot = 2;\r
+\r
+    /*\r
+     * Enact command-line overrides.\r
+     */\r
+    cmdline_run_saved(&cfg);\r
+\r
+    /*\r
+     * Trim leading whitespace off the hostname if it's there.\r
+     */\r
+    {\r
+       int space = strspn(cfg.host, " \t");\r
+       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
+    }\r
+\r
+    /* See if host is of the form user@host */\r
+    if (cfg.host[0] != '\0') {\r
+       char *atsign = strrchr(cfg.host, '@');\r
+       /* Make sure we're not overflowing the user field */\r
+       if (atsign) {\r
+           if (atsign - cfg.host < sizeof cfg.username) {\r
+               strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
+               cfg.username[atsign - cfg.host] = '\0';\r
+           }\r
+           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Trim a colon suffix off the hostname if it's there.\r
+     */\r
+    cfg.host[strcspn(cfg.host, ":")] = '\0';\r
+\r
+    /*\r
+     * Remove any remaining whitespace from the hostname.\r
+     */\r
+    {\r
+       int p1 = 0, p2 = 0;\r
+       while (cfg.host[p2] != '\0') {\r
+           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
+               cfg.host[p1] = cfg.host[p2];\r
+               p1++;\r
+           }\r
+           p2++;\r
+       }\r
+       cfg.host[p1] = '\0';\r
+    }\r
+\r
+    /* Set username */\r
+    if (user != NULL && user[0] != '\0') {\r
+       strncpy(cfg.username, user, sizeof(cfg.username) - 1);\r
+       cfg.username[sizeof(cfg.username) - 1] = '\0';\r
+    }\r
+\r
+    if (portnumber)\r
+       cfg.port = portnumber;\r
+\r
+    /*\r
+     * Disable scary things which shouldn't be enabled for simple\r
+     * things like SCP and SFTP: agent forwarding, port forwarding,\r
+     * X forwarding.\r
+     */\r
+    cfg.x11_forward = 0;\r
+    cfg.agentfwd = 0;\r
+    cfg.portfwd[0] = cfg.portfwd[1] = '\0';\r
+    cfg.ssh_simple = TRUE;\r
+\r
+    /* Set up subsystem name. */\r
+    strcpy(cfg.remote_cmd, "sftp");\r
+    cfg.ssh_subsys = TRUE;\r
+    cfg.nopty = TRUE;\r
+\r
+    /*\r
+     * Set up fallback option, for SSH-1 servers or servers with the\r
+     * sftp subsystem not enabled but the server binary installed\r
+     * in the usual place. We only support fallback on Unix\r
+     * systems, and we use a kludgy piece of shellery which should\r
+     * try to find sftp-server in various places (the obvious\r
+     * systemwide spots /usr/lib and /usr/local/lib, and then the\r
+     * user's PATH) and finally give up.\r
+     * \r
+     *   test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\r
+     *   test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\r
+     *   exec sftp-server\r
+     * \r
+     * the idea being that this will attempt to use either of the\r
+     * obvious pathnames and then give up, and when it does give up\r
+     * it will print the preferred pathname in the error messages.\r
+     */\r
+    cfg.remote_cmd_ptr2 =\r
+       "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"\r
+       "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"\r
+       "exec sftp-server";\r
+    cfg.ssh_subsys2 = FALSE;\r
+\r
+    back = &ssh_backend;\r
+\r
+    err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,\r
+                    0, cfg.tcp_keepalives);\r
+    if (err != NULL) {\r
+       fprintf(stderr, "ssh_init: %s\n", err);\r
+       return 1;\r
+    }\r
+    logctx = log_init(NULL, &cfg);\r
+    back->provide_logctx(backhandle, logctx);\r
+    console_provide_logctx(logctx);\r
+    while (!back->sendok(backhandle)) {\r
+       if (back->exitcode(backhandle) >= 0)\r
+           return 1;\r
+       if (ssh_sftp_loop_iteration() < 0) {\r
+           fprintf(stderr, "ssh_init: error during SSH connection setup\n");\r
+           return 1;\r
+       }\r
+    }\r
+    if (verbose && realhost != NULL)\r
+       printf("Connected to %s\n", realhost);\r
+    if (realhost != NULL)\r
+       sfree(realhost);\r
+    return 0;\r
+}\r
+\r
+void cmdline_error(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "psftp: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fprintf(stderr, "\n       try typing \"psftp -h\" for help\n");\r
+    exit(1);\r
+}\r
+\r
+/*\r
+ * Main program. Parse arguments etc.\r
+ */\r
+int psftp_main(int argc, char *argv[])\r
+{\r
+    int i;\r
+    int portnumber = 0;\r
+    char *userhost, *user;\r
+    int mode = 0;\r
+    int modeflags = 0;\r
+    char *batchfile = NULL;\r
+\r
+    flags = FLAG_STDERR | FLAG_INTERACTIVE\r
+#ifdef FLAG_SYNCAGENT\r
+       | FLAG_SYNCAGENT\r
+#endif\r
+       ;\r
+    cmdline_tooltype = TOOLTYPE_FILETRANSFER;\r
+    sk_init();\r
+\r
+    userhost = user = NULL;\r
+\r
+    /* Load Default Settings before doing anything else. */\r
+    do_defaults(NULL, &cfg);\r
+    loaded_session = FALSE;\r
+\r
+    for (i = 1; i < argc; i++) {\r
+       int ret;\r
+       if (argv[i][0] != '-') {\r
+            if (userhost)\r
+                usage();\r
+            else\r
+                userhost = dupstr(argv[i]);\r
+           continue;\r
+       }\r
+       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg);\r
+       if (ret == -2) {\r
+           cmdline_error("option \"%s\" requires an argument", argv[i]);\r
+       } else if (ret == 2) {\r
+           i++;               /* skip next argument */\r
+       } else if (ret == 1) {\r
+           /* We have our own verbosity in addition to `flags'. */\r
+           if (flags & FLAG_VERBOSE)\r
+               verbose = 1;\r
+       } else if (strcmp(argv[i], "-h") == 0 ||\r
+                  strcmp(argv[i], "-?") == 0) {\r
+           usage();\r
+        } else if (strcmp(argv[i], "-pgpfp") == 0) {\r
+            pgp_fingerprints();\r
+            return 1;\r
+       } else if (strcmp(argv[i], "-V") == 0) {\r
+           version();\r
+       } else if (strcmp(argv[i], "-batch") == 0) {\r
+           console_batch_mode = 1;\r
+       } else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) {\r
+           mode = 1;\r
+           batchfile = argv[++i];\r
+       } else if (strcmp(argv[i], "-bc") == 0) {\r
+           modeflags = modeflags | 1;\r
+       } else if (strcmp(argv[i], "-be") == 0) {\r
+           modeflags = modeflags | 2;\r
+       } else if (strcmp(argv[i], "--") == 0) {\r
+           i++;\r
+           break;\r
+       } else {\r
+           cmdline_error("unknown option \"%s\"", argv[i]);\r
+       }\r
+    }\r
+    argc -= i;\r
+    argv += i;\r
+    back = NULL;\r
+\r
+    /*\r
+     * If the loaded session provides a hostname, and a hostname has not\r
+     * otherwise been specified, pop it in `userhost' so that\r
+     * `psftp -load sessname' is sufficient to start a session.\r
+     */\r
+    if (!userhost && cfg.host[0] != '\0') {\r
+       userhost = dupstr(cfg.host);\r
+    }\r
+\r
+    /*\r
+     * If a user@host string has already been provided, connect to\r
+     * it now.\r
+     */\r
+    if (userhost) {\r
+       int ret;\r
+       ret = psftp_connect(userhost, user, portnumber);\r
+       sfree(userhost);\r
+       if (ret)\r
+           return 1;\r
+       if (do_sftp_init())\r
+           return 1;\r
+    } else {\r
+       printf("psftp: no hostname specified; use \"open host.name\""\r
+              " to connect\n");\r
+    }\r
+\r
+    do_sftp(mode, modeflags, batchfile);\r
+\r
+    if (back != NULL && back->connected(backhandle)) {\r
+       char ch;\r
+       back->special(backhandle, TS_EOF);\r
+       sftp_recvdata(&ch, 1);\r
+    }\r
+    do_sftp_cleanup();\r
+    random_save_seed();\r
+    cmdline_cleanup();\r
+    console_provide_logctx(NULL);\r
+    sk_cleanup();\r
+\r
+    return 0;\r
+}\r
diff --git a/putty/PSFTP.H b/putty/PSFTP.H
new file mode 100644 (file)
index 0000000..3e13887
--- /dev/null
@@ -0,0 +1,178 @@
+/*\r
+ * psftp.h: interface between psftp.c / scp.c and each\r
+ * platform-specific SFTP module.\r
+ */\r
+\r
+#include "int64.h"\r
+\r
+#ifndef PUTTY_PSFTP_H\r
+#define PUTTY_PSFTP_H\r
+\r
+/*\r
+ * psftp_getcwd returns the local current directory. The returned\r
+ * string must be freed by the caller.\r
+ */\r
+char *psftp_getcwd(void);\r
+\r
+/*\r
+ * psftp_lcd changes the local current directory. The return value\r
+ * is NULL on success, or else an error message which must be freed\r
+ * by the caller.\r
+ */\r
+char *psftp_lcd(char *newdir);\r
+\r
+/*\r
+ * Retrieve file times on a local file. Must return two unsigned\r
+ * longs in POSIX time_t format.\r
+ */\r
+void get_file_times(char *filename, unsigned long *mtime,\r
+                   unsigned long *atime);\r
+\r
+/*\r
+ * One iteration of the PSFTP event loop: wait for network data and\r
+ * process it, once.\r
+ */\r
+int ssh_sftp_loop_iteration(void);\r
+\r
+/*\r
+ * Read a command line for PSFTP from standard input. Caller must\r
+ * free.\r
+ * \r
+ * If `backend_required' is TRUE, should also listen for activity\r
+ * at the backend (rekeys, clientalives, unexpected closures etc)\r
+ * and respond as necessary, and if the backend closes it should\r
+ * treat this as a failure condition. If `backend_required' is\r
+ * FALSE, a back end is not (intentionally) active at all (e.g.\r
+ * psftp before an `open' command).\r
+ */\r
+char *ssh_sftp_get_cmdline(char *prompt, int backend_required);\r
+\r
+/*\r
+ * The main program in psftp.c. Called from main() in the platform-\r
+ * specific code, after doing any platform-specific initialisation.\r
+ */\r
+int psftp_main(int argc, char *argv[]);\r
+\r
+/*\r
+ * These functions are used by PSCP to transmit progress updates\r
+ * and error information to a GUI window managing it. This will\r
+ * probably only ever be supported on Windows, so these functions\r
+ * can safely be stubs on all other platforms.\r
+ */\r
+void gui_update_stats(char *name, unsigned long size,\r
+                     int percentage, unsigned long elapsed,\r
+                     unsigned long done, unsigned long eta,\r
+                     unsigned long ratebs);\r
+void gui_send_errcount(int list, int errs);\r
+void gui_send_char(int is_stderr, int c);\r
+void gui_enable(char *arg);\r
+\r
+/*\r
+ * It's likely that a given platform's implementation of file\r
+ * transfer utilities is going to want to do things with them that\r
+ * aren't present in stdio. Hence we supply an alternative\r
+ * abstraction for file access functions.\r
+ * \r
+ * This abstraction tells you the size and access times when you\r
+ * open an existing file (platforms may choose the meaning of the\r
+ * file times if it's not clear; whatever they choose will be what\r
+ * PSCP sends to the server as mtime and atime), and lets you set\r
+ * the times when saving a new file.\r
+ * \r
+ * On the other hand, the abstraction is pretty simple: it supports\r
+ * only opening a file and reading it, or creating a file and writing\r
+ * it. None of this read-and-write, seeking-back-and-forth stuff.\r
+ */\r
+typedef struct RFile RFile;\r
+typedef struct WFile WFile;\r
+/* Output params size, mtime and atime can all be NULL if desired */\r
+RFile *open_existing_file(char *name, uint64 *size,\r
+                         unsigned long *mtime, unsigned long *atime);\r
+WFile *open_existing_wfile(char *name, uint64 *size);\r
+/* Returns <0 on error, 0 on eof, or number of bytes read, as usual */\r
+int read_from_file(RFile *f, void *buffer, int length);\r
+/* Closes and frees the RFile */\r
+void close_rfile(RFile *f);\r
+WFile *open_new_file(char *name);\r
+/* Returns <0 on error, 0 on eof, or number of bytes written, as usual */\r
+int write_to_file(WFile *f, void *buffer, int length);\r
+void set_file_times(WFile *f, unsigned long mtime, unsigned long atime);\r
+/* Closes and frees the WFile */\r
+void close_wfile(WFile *f);\r
+/* Seek offset bytes through file */\r
+enum { FROM_START, FROM_CURRENT, FROM_END };\r
+int seek_file(WFile *f, uint64 offset, int whence);\r
+/* Get file position */\r
+uint64 get_file_posn(WFile *f);\r
+/*\r
+ * Determine the type of a file: nonexistent, file, directory or\r
+ * weird. `weird' covers anything else - named pipes, Unix sockets,\r
+ * device files, fish, badgers, you name it. Things marked `weird'\r
+ * will be skipped over in recursive file transfers, so the only\r
+ * real reason for not lumping them in with `nonexistent' is that\r
+ * it allows a slightly more sane error message.\r
+ */\r
+enum {\r
+    FILE_TYPE_NONEXISTENT, FILE_TYPE_FILE, FILE_TYPE_DIRECTORY, FILE_TYPE_WEIRD\r
+};\r
+int file_type(char *name);\r
+\r
+/*\r
+ * Read all the file names out of a directory.\r
+ */\r
+typedef struct DirHandle DirHandle;\r
+DirHandle *open_directory(char *name);\r
+/* The string returned from this will need freeing if not NULL */\r
+char *read_filename(DirHandle *dir);\r
+void close_directory(DirHandle *dir);\r
+\r
+/*\r
+ * Test a filespec to see whether it's a local wildcard or not.\r
+ * Return values:\r
+ * \r
+ *  - WCTYPE_WILDCARD (this is a wildcard).\r
+ *  - WCTYPE_FILENAME (this is a single file name).\r
+ *  - WCTYPE_NONEXISTENT (whichever it was, nothing of that name exists).\r
+ * \r
+ * Some platforms may choose not to support local wildcards when\r
+ * they come from the command line; in this case they simply never\r
+ * return WCTYPE_WILDCARD, but still test the file's existence.\r
+ * (However, all platforms will probably want to support wildcards\r
+ * inside the PSFTP CLI.)\r
+ */\r
+enum {\r
+    WCTYPE_NONEXISTENT, WCTYPE_FILENAME, WCTYPE_WILDCARD\r
+};\r
+int test_wildcard(char *name, int cmdline);\r
+\r
+/*\r
+ * Actually return matching file names for a local wildcard.\r
+ */\r
+typedef struct WildcardMatcher WildcardMatcher;\r
+WildcardMatcher *begin_wildcard_matching(char *name);\r
+/* The string returned from this will need freeing if not NULL */\r
+char *wildcard_get_filename(WildcardMatcher *dir);\r
+void finish_wildcard_matching(WildcardMatcher *dir);\r
+\r
+/*\r
+ * Vet a filename returned from the remote host, to ensure it isn't\r
+ * in some way malicious. The idea is that this function is applied\r
+ * to filenames returned from FXP_READDIR, which means we can panic\r
+ * if we see _anything_ resembling a directory separator.\r
+ * \r
+ * Returns TRUE if the filename is kosher, FALSE if dangerous.\r
+ */\r
+int vet_filename(char *name);\r
+\r
+/*\r
+ * Create a directory. Returns 0 on error, !=0 on success.\r
+ */\r
+int create_directory(char *name);\r
+\r
+/*\r
+ * Concatenate a directory name and a file name. The way this is\r
+ * done will depend on the OS.\r
+ */\r
+char *dir_file_cat(char *dir, char *file);\r
+\r
+#endif /* PUTTY_PSFTP_H */\r
diff --git a/putty/PUTTY.H b/putty/PUTTY.H
new file mode 100644 (file)
index 0000000..e2baee8
--- /dev/null
@@ -0,0 +1,1306 @@
+#ifndef PUTTY_PUTTY_H\r
+#define PUTTY_PUTTY_H\r
+\r
+#include <stddef.h>                   /* for wchar_t */\r
+\r
+/*\r
+ * Global variables. Most modules declare these `extern', but\r
+ * window.c will do `#define PUTTY_DO_GLOBALS' before including this\r
+ * module, and so will get them properly defined.\r
+ */\r
+#ifndef GLOBAL\r
+#ifdef PUTTY_DO_GLOBALS\r
+#define GLOBAL\r
+#else\r
+#define GLOBAL extern\r
+#endif\r
+#endif\r
+\r
+#ifndef DONE_TYPEDEFS\r
+#define DONE_TYPEDEFS\r
+typedef struct config_tag Config;\r
+typedef struct backend_tag Backend;\r
+typedef struct terminal_tag Terminal;\r
+#endif\r
+\r
+#include "puttyps.h"\r
+#include "network.h"\r
+#include "misc.h"\r
+\r
+/*\r
+ * Fingerprints of the PGP master keys that can be used to establish a trust\r
+ * path between an executable and other files.\r
+ */\r
+#define PGP_RSA_MASTER_KEY_FP \\r
+    "8F 15 97 DA 25 30 AB 0D  88 D1 92 54 11 CF 0C 4C"\r
+#define PGP_DSA_MASTER_KEY_FP \\r
+    "313C 3E76 4B74 C2C5 F2AE  83A8 4F5E 6DF5 6A93 B34E"\r
+\r
+/* Three attribute types: \r
+ * The ATTRs (normal attributes) are stored with the characters in\r
+ * the main display arrays\r
+ *\r
+ * The TATTRs (temporary attributes) are generated on the fly, they\r
+ * can overlap with characters but not with normal attributes.\r
+ *\r
+ * The LATTRs (line attributes) are an entirely disjoint space of\r
+ * flags.\r
+ * \r
+ * The DATTRs (display attributes) are internal to terminal.c (but\r
+ * defined here because their values have to match the others\r
+ * here); they reuse the TATTR_* space but are always masked off\r
+ * before sending to the front end.\r
+ *\r
+ * ATTR_INVALID is an illegal colour combination.\r
+ */\r
+\r
+#define TATTR_ACTCURS      0x40000000UL      /* active cursor (block) */\r
+#define TATTR_PASCURS      0x20000000UL      /* passive cursor (box) */\r
+#define TATTR_RIGHTCURS            0x10000000UL      /* cursor-on-RHS */\r
+#define TATTR_COMBINING            0x80000000UL      /* combining characters */\r
+\r
+#define DATTR_STARTRUN      0x80000000UL   /* start of redraw run */\r
+\r
+#define TDATTR_MASK         0xF0000000UL\r
+#define TATTR_MASK (TDATTR_MASK)\r
+#define DATTR_MASK (TDATTR_MASK)\r
+\r
+#define LATTR_NORM   0x00000000UL\r
+#define LATTR_WIDE   0x00000001UL\r
+#define LATTR_TOP    0x00000002UL\r
+#define LATTR_BOT    0x00000003UL\r
+#define LATTR_MODE   0x00000003UL\r
+#define LATTR_WRAPPED 0x00000010UL     /* this line wraps to next */\r
+#define LATTR_WRAPPED2 0x00000020UL    /* with WRAPPED: CJK wide character\r
+                                         wrapped to next line, so last\r
+                                         single-width cell is empty */\r
+\r
+#define ATTR_INVALID 0x03FFFFU\r
+\r
+/* Like Linux use the F000 page for direct to font. */\r
+#define CSET_OEMCP   0x0000F000UL      /* OEM Codepage DTF */\r
+#define CSET_ACP     0x0000F100UL      /* Ansi Codepage DTF */\r
+\r
+/* These are internal use overlapping with the UTF-16 surrogates */\r
+#define CSET_ASCII   0x0000D800UL      /* normal ASCII charset ESC ( B */\r
+#define CSET_LINEDRW 0x0000D900UL      /* line drawing charset ESC ( 0 */\r
+#define CSET_SCOACS  0x0000DA00UL      /* SCO Alternate charset */\r
+#define CSET_GBCHR   0x0000DB00UL      /* UK variant   charset ESC ( A */\r
+#define CSET_MASK    0xFFFFFF00UL      /* Character set mask */\r
+\r
+#define DIRECT_CHAR(c) ((c&0xFFFFFC00)==0xD800)\r
+#define DIRECT_FONT(c) ((c&0xFFFFFE00)==0xF000)\r
+\r
+#define UCSERR      (CSET_LINEDRW|'a') /* UCS Format error character. */\r
+/*\r
+ * UCSWIDE is a special value used in the terminal data to signify\r
+ * the character cell containing the right-hand half of a CJK wide\r
+ * character. We use 0xDFFF because it's part of the surrogate\r
+ * range and hence won't be used for anything else (it's impossible\r
+ * to input it via UTF-8 because our UTF-8 decoder correctly\r
+ * rejects surrogates).\r
+ */\r
+#define UCSWIDE             0xDFFF\r
+\r
+#define ATTR_NARROW  0x800000U\r
+#define ATTR_WIDE    0x400000U\r
+#define ATTR_BOLD    0x040000U\r
+#define ATTR_UNDER   0x080000U\r
+#define ATTR_REVERSE 0x100000U\r
+#define ATTR_BLINK   0x200000U\r
+#define ATTR_FGMASK  0x0001FFU\r
+#define ATTR_BGMASK  0x03FE00U\r
+#define ATTR_COLOURS 0x03FFFFU\r
+#define ATTR_FGSHIFT 0\r
+#define ATTR_BGSHIFT 9\r
+\r
+/*\r
+ * The definitive list of colour numbers stored in terminal\r
+ * attribute words is kept here. It is:\r
+ * \r
+ *  - 0-7 are ANSI colours (KRGYBMCW).\r
+ *  - 8-15 are the bold versions of those colours.\r
+ *  - 16-255 are the remains of the xterm 256-colour mode (a\r
+ *    216-colour cube with R at most significant and B at least,\r
+ *    followed by a uniform series of grey shades running between\r
+ *    black and white but not including either on grounds of\r
+ *    redundancy).\r
+ *  - 256 is default foreground\r
+ *  - 257 is default bold foreground\r
+ *  - 258 is default background\r
+ *  - 259 is default bold background\r
+ *  - 260 is cursor foreground\r
+ *  - 261 is cursor background\r
+ */\r
+\r
+#define ATTR_DEFFG   (256 << ATTR_FGSHIFT)\r
+#define ATTR_DEFBG   (258 << ATTR_BGSHIFT)\r
+#define ATTR_DEFAULT (ATTR_DEFFG | ATTR_DEFBG)\r
+\r
+struct sesslist {\r
+    int nsessions;\r
+    char **sessions;\r
+    char *buffer;                     /* so memory can be freed later */\r
+};\r
+\r
+struct unicode_data {\r
+    char **uni_tbl;\r
+    int dbcs_screenfont;\r
+    int font_codepage;\r
+    int line_codepage;\r
+    wchar_t unitab_scoacs[256];\r
+    wchar_t unitab_line[256];\r
+    wchar_t unitab_font[256];\r
+    wchar_t unitab_xterm[256];\r
+    wchar_t unitab_oemcp[256];\r
+    unsigned char unitab_ctrl[256];\r
+};\r
+\r
+#define LGXF_OVR  1                   /* existing logfile overwrite */\r
+#define LGXF_APN  0                   /* existing logfile append */\r
+#define LGXF_ASK -1                   /* existing logfile ask */\r
+#define LGTYP_NONE  0                 /* logmode: no logging */\r
+#define LGTYP_ASCII 1                 /* logmode: pure ascii */\r
+#define LGTYP_DEBUG 2                 /* logmode: all chars of traffic */\r
+#define LGTYP_PACKETS 3                       /* logmode: SSH data packets */\r
+#define LGTYP_SSHRAW 4                /* logmode: SSH raw data */\r
+\r
+typedef enum {\r
+    /* Actual special commands. Originally Telnet, but some codes have\r
+     * been re-used for similar specials in other protocols. */\r
+    TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,\r
+    TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING,\r
+    TS_EOL,\r
+    /* Special command for SSH. */\r
+    TS_REKEY,\r
+    /* POSIX-style signals. (not Telnet) */\r
+    TS_SIGABRT, TS_SIGALRM, TS_SIGFPE,  TS_SIGHUP,  TS_SIGILL,\r
+    TS_SIGINT,  TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV,\r
+    TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2,\r
+    /* Pseudo-specials used for constructing the specials menu. */\r
+    TS_SEP,        /* Separator */\r
+    TS_SUBMENU,            /* Start a new submenu with specified name */\r
+    TS_EXITMENU            /* Exit current submenu or end of specials */\r
+} Telnet_Special;\r
+\r
+struct telnet_special {\r
+    const char *name;\r
+    int code;\r
+};\r
+\r
+typedef enum {\r
+    MBT_NOTHING,\r
+    MBT_LEFT, MBT_MIDDLE, MBT_RIGHT,   /* `raw' button designations */\r
+    MBT_SELECT, MBT_EXTEND, MBT_PASTE, /* `cooked' button designations */\r
+    MBT_WHEEL_UP, MBT_WHEEL_DOWN       /* mouse wheel */\r
+} Mouse_Button;\r
+\r
+typedef enum {\r
+    MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE\r
+} Mouse_Action;\r
+\r
+/* Keyboard modifiers -- keys the user is actually holding down */\r
+\r
+#define PKM_SHIFT      0x01\r
+#define PKM_CONTROL    0x02\r
+#define PKM_META       0x04\r
+#define PKM_ALT                0x08\r
+\r
+/* Keyboard flags that aren't really modifiers */\r
+#define PKF_CAPSLOCK   0x10\r
+#define PKF_NUMLOCK    0x20\r
+#define PKF_REPEAT     0x40\r
+\r
+/* Stand-alone keysyms for function keys */\r
+\r
+typedef enum {\r
+    PK_NULL,           /* No symbol for this key */\r
+    /* Main keypad keys */\r
+    PK_ESCAPE, PK_TAB, PK_BACKSPACE, PK_RETURN, PK_COMPOSE,\r
+    /* Editing keys */\r
+    PK_HOME, PK_INSERT, PK_DELETE, PK_END, PK_PAGEUP, PK_PAGEDOWN,\r
+    /* Cursor keys */\r
+    PK_UP, PK_DOWN, PK_RIGHT, PK_LEFT, PK_REST,\r
+    /* Numeric keypad */                       /* Real one looks like: */\r
+    PK_PF1, PK_PF2, PK_PF3, PK_PF4,            /* PF1 PF2 PF3 PF4 */\r
+    PK_KPCOMMA, PK_KPMINUS, PK_KPDECIMAL,      /*  7   8   9   -  */\r
+    PK_KP0, PK_KP1, PK_KP2, PK_KP3, PK_KP4,    /*  4   5   6   ,  */\r
+    PK_KP5, PK_KP6, PK_KP7, PK_KP8, PK_KP9,    /*  1   2   3  en- */\r
+    PK_KPBIGPLUS, PK_KPENTER,                  /*    0     .  ter */\r
+    /* Top row */\r
+    PK_F1,  PK_F2,  PK_F3,  PK_F4,  PK_F5,\r
+    PK_F6,  PK_F7,  PK_F8,  PK_F9,  PK_F10,\r
+    PK_F11, PK_F12, PK_F13, PK_F14, PK_F15,\r
+    PK_F16, PK_F17, PK_F18, PK_F19, PK_F20,\r
+    PK_PAUSE\r
+} Key_Sym;\r
+\r
+#define PK_ISEDITING(k)        ((k) >= PK_HOME && (k) <= PK_PAGEDOWN)\r
+#define PK_ISCURSOR(k) ((k) >= PK_UP && (k) <= PK_REST)\r
+#define PK_ISKEYPAD(k) ((k) >= PK_PF1 && (k) <= PK_KPENTER)\r
+#define PK_ISFKEY(k)   ((k) >= PK_F1 && (k) <= PK_F20)\r
+\r
+enum {\r
+    VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN, VT_UNICODE\r
+};\r
+\r
+enum {\r
+    /*\r
+     * SSH-2 key exchange algorithms\r
+     */\r
+    KEX_WARN,\r
+    KEX_DHGROUP1,\r
+    KEX_DHGROUP14,\r
+    KEX_DHGEX,\r
+    KEX_RSA,\r
+    KEX_MAX\r
+};\r
+\r
+enum {\r
+    /*\r
+     * SSH ciphers (both SSH-1 and SSH-2)\r
+     */\r
+    CIPHER_WARN,                      /* pseudo 'cipher' */\r
+    CIPHER_3DES,\r
+    CIPHER_BLOWFISH,\r
+    CIPHER_AES,                               /* (SSH-2 only) */\r
+    CIPHER_DES,\r
+    CIPHER_ARCFOUR,\r
+    CIPHER_MAX                        /* no. ciphers (inc warn) */\r
+};\r
+\r
+enum {\r
+    /*\r
+     * Several different bits of the PuTTY configuration seem to be\r
+     * three-way settings whose values are `always yes', `always\r
+     * no', and `decide by some more complex automated means'. This\r
+     * is true of line discipline options (local echo and line\r
+     * editing), proxy DNS, Close On Exit, and SSH server bug\r
+     * workarounds. Accordingly I supply a single enum here to deal\r
+     * with them all.\r
+     */\r
+    FORCE_ON, FORCE_OFF, AUTO\r
+};\r
+\r
+enum {\r
+    /*\r
+     * Proxy types.\r
+     */\r
+    PROXY_NONE, PROXY_SOCKS4, PROXY_SOCKS5,\r
+    PROXY_HTTP, PROXY_TELNET, PROXY_CMD\r
+};\r
+\r
+enum {\r
+    /*\r
+     * Line discipline options which the backend might try to control.\r
+     */\r
+    LD_EDIT,                          /* local line editing */\r
+    LD_ECHO                           /* local echo */\r
+};\r
+\r
+enum {\r
+    /* Actions on remote window title query */\r
+    TITLE_NONE, TITLE_EMPTY, TITLE_REAL\r
+};\r
+\r
+enum {\r
+    /* Protocol back ends. (cfg.protocol) */\r
+    PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH,\r
+    /* PROT_SERIAL is supported on a subset of platforms, but it doesn't\r
+     * hurt to define it globally. */\r
+    PROT_SERIAL\r
+};\r
+\r
+enum {\r
+    /* Bell settings (cfg.beep) */\r
+    BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER\r
+};\r
+\r
+enum {\r
+    /* Taskbar flashing indication on bell (cfg.beep_ind) */\r
+    B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY\r
+};\r
+\r
+enum {\r
+    /* Resize actions (cfg.resize_action) */\r
+    RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER\r
+};\r
+\r
+enum {\r
+    /* Function key types (cfg.funky_type) */\r
+    FUNKY_TILDE,\r
+    FUNKY_LINUX,\r
+    FUNKY_XTERM,\r
+    FUNKY_VT400,\r
+    FUNKY_VT100P,\r
+    FUNKY_SCO\r
+};\r
+\r
+enum {\r
+    FQ_DEFAULT, FQ_ANTIALIASED, FQ_NONANTIALIASED, FQ_CLEARTYPE\r
+};\r
+\r
+enum {\r
+    SER_PAR_NONE, SER_PAR_ODD, SER_PAR_EVEN, SER_PAR_MARK, SER_PAR_SPACE\r
+};\r
+\r
+enum {\r
+    SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR\r
+};\r
+\r
+/*\r
+ * Tables of string <-> enum value mappings used in settings.c.\r
+ * Defined here so that backends can export their GSS library tables\r
+ * to the cross-platform settings code.\r
+ */\r
+struct keyvalwhere {\r
+    /*\r
+     * Two fields which define a string and enum value to be\r
+     * equivalent to each other.\r
+     */\r
+    char *s;\r
+    int v;\r
+\r
+    /*\r
+     * The next pair of fields are used by gprefs() in settings.c to\r
+     * arrange that when it reads a list of strings representing a\r
+     * preference list and translates it into the corresponding list\r
+     * of integers, strings not appearing in the list are entered in a\r
+     * configurable position rather than uniformly at the end.\r
+     */\r
+\r
+    /*\r
+     * 'vrel' indicates which other value in the list to place this\r
+     * element relative to. It should be a value that has occurred in\r
+     * a 'v' field of some other element of the array, or -1 to\r
+     * indicate that we simply place relative to one or other end of\r
+     * the list.\r
+     *\r
+     * gprefs will try to process the elements in an order which makes\r
+     * this field work (i.e. so that the element referenced has been\r
+     * added before processing this one).\r
+     */\r
+    int vrel;\r
+\r
+    /*\r
+     * 'where' indicates whether to place the new value before or\r
+     * after the one referred to by vrel. -1 means before; +1 means\r
+     * after.\r
+     *\r
+     * When vrel is -1, this also implicitly indicates which end of\r
+     * the array to use. So vrel=-1, where=-1 means to place _before_\r
+     * some end of the list (hence, at the last element); vrel=-1,\r
+     * where=+1 means to place _after_ an end (hence, at the first).\r
+     */\r
+    int where;\r
+};\r
+\r
+#ifndef NO_GSSAPI\r
+extern const int ngsslibs;\r
+extern const char *const gsslibnames[]; /* for displaying in configuration */\r
+extern const struct keyvalwhere gsslibkeywords[]; /* for settings.c */\r
+#endif\r
+\r
+extern const char *const ttymodes[];\r
+\r
+enum {\r
+    /*\r
+     * Network address types. Used for specifying choice of IPv4/v6\r
+     * in config; also used in proxy.c to indicate whether a given\r
+     * host name has already been resolved or will be resolved at\r
+     * the proxy end.\r
+     */\r
+    ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME\r
+};\r
+\r
+struct backend_tag {\r
+    const char *(*init) (void *frontend_handle, void **backend_handle,\r
+                        Config *cfg,\r
+                        char *host, int port, char **realhost, int nodelay,\r
+                        int keepalive);\r
+    void (*free) (void *handle);\r
+    /* back->reconfig() passes in a replacement configuration. */\r
+    void (*reconfig) (void *handle, Config *cfg);\r
+    /* back->send() returns the current amount of buffered data. */\r
+    int (*send) (void *handle, char *buf, int len);\r
+    /* back->sendbuffer() does the same thing but without attempting a send */\r
+    int (*sendbuffer) (void *handle);\r
+    void (*size) (void *handle, int width, int height);\r
+    void (*special) (void *handle, Telnet_Special code);\r
+    const struct telnet_special *(*get_specials) (void *handle);\r
+    int (*connected) (void *handle);\r
+    int (*exitcode) (void *handle);\r
+    /* If back->sendok() returns FALSE, data sent to it from the frontend\r
+     * may be lost. */\r
+    int (*sendok) (void *handle);\r
+    int (*ldisc) (void *handle, int);\r
+    void (*provide_ldisc) (void *handle, void *ldisc);\r
+    void (*provide_logctx) (void *handle, void *logctx);\r
+    /*\r
+     * back->unthrottle() tells the back end that the front end\r
+     * buffer is clearing.\r
+     */\r
+    void (*unthrottle) (void *handle, int);\r
+    int (*cfg_info) (void *handle);\r
+    char *name;\r
+    int protocol;\r
+    int default_port;\r
+};\r
+\r
+extern Backend *backends[];\r
+\r
+/*\r
+ * Suggested default protocol provided by the backend link module.\r
+ * The application is free to ignore this.\r
+ */\r
+extern const int be_default_protocol;\r
+\r
+/*\r
+ * Name of this particular application, for use in the config box\r
+ * and other pieces of text.\r
+ */\r
+extern const char *const appname;\r
+\r
+/*\r
+ * IMPORTANT POLICY POINT: everything in this structure which wants\r
+ * to be treated like an integer must be an actual, honest-to-\r
+ * goodness `int'. No enum-typed variables. This is because parts\r
+ * of the code will want to pass around `int *' pointers to them\r
+ * and we can't run the risk of porting to some system on which the\r
+ * enum comes out as a different size from int.\r
+ */\r
+struct config_tag {\r
+    /* Basic options */\r
+    char host[512];\r
+    int port;\r
+    int protocol;\r
+    int addressfamily;\r
+    int close_on_exit;\r
+    int warn_on_close;\r
+    int ping_interval;                /* in seconds */\r
+    int tcp_nodelay;\r
+    int tcp_keepalives;\r
+    char loghost[512];  /* logical host being contacted, for host key check */\r
+    /* Proxy options */\r
+    char proxy_exclude_list[512];\r
+    int proxy_dns;\r
+    int even_proxy_localhost;\r
+    int proxy_type;\r
+    char proxy_host[512];\r
+    int proxy_port;\r
+    char proxy_username[128];\r
+    char proxy_password[128];\r
+    char proxy_telnet_command[512];\r
+    /* SSH options */\r
+    char remote_cmd[512];\r
+    char *remote_cmd_ptr;             /* might point to a larger command\r
+                                       * but never for loading/saving */\r
+    char *remote_cmd_ptr2;            /* might point to a larger command\r
+                                       * but never for loading/saving */\r
+    int nopty;\r
+    int compression;\r
+    int ssh_kexlist[KEX_MAX];\r
+    int ssh_rekey_time;                       /* in minutes */\r
+    char ssh_rekey_data[16];\r
+    int tryagent;\r
+    int agentfwd;\r
+    int change_username;              /* allow username switching in SSH-2 */\r
+    int ssh_cipherlist[CIPHER_MAX];\r
+    Filename keyfile;\r
+    int sshprot;                      /* use v1 or v2 when both available */\r
+    int ssh2_des_cbc;                 /* "des-cbc" unrecommended SSH-2 cipher */\r
+    int ssh_no_userauth;              /* bypass "ssh-userauth" (SSH-2 only) */\r
+    int ssh_show_banner;              /* show USERAUTH_BANNERs (SSH-2 only) */\r
+    int try_tis_auth;\r
+    int try_ki_auth;\r
+    int try_gssapi_auth;               /* attempt gssapi auth */\r
+    int gssapifwd;                     /* forward tgt via gss */\r
+    int ssh_gsslist[4];                       /* preference order for local GSS libs */\r
+    Filename ssh_gss_custom;\r
+    int ssh_subsys;                   /* run a subsystem rather than a command */\r
+    int ssh_subsys2;                  /* fallback to go with remote_cmd_ptr2 */\r
+    int ssh_no_shell;                 /* avoid running a shell */\r
+    char ssh_nc_host[512];            /* host to connect to in `nc' mode */\r
+    int ssh_nc_port;                  /* port to connect to in `nc' mode */\r
+    /* Telnet options */\r
+    char termtype[32];\r
+    char termspeed[32];\r
+    char ttymodes[768];                       /* MODE\tVvalue\0MODE\tA\0\0 */\r
+    char environmt[1024];             /* VAR\tvalue\0VAR\tvalue\0\0 */\r
+    char username[100];\r
+    int username_from_env;\r
+    char localusername[100];\r
+    int rfc_environ;\r
+    int passive_telnet;\r
+    /* Serial port options */\r
+    char serline[256];\r
+    int serspeed;\r
+    int serdatabits, serstopbits;\r
+    int serparity;\r
+    int serflow;\r
+    /* Keyboard options */\r
+    int bksp_is_delete;\r
+    int rxvt_homeend;\r
+    int funky_type;\r
+    int no_applic_c;                  /* totally disable app cursor keys */\r
+    int no_applic_k;                  /* totally disable app keypad */\r
+    int no_mouse_rep;                 /* totally disable mouse reporting */\r
+    int no_remote_resize;             /* disable remote resizing */\r
+    int no_alt_screen;                /* disable alternate screen */\r
+    int no_remote_wintitle;           /* disable remote retitling */\r
+    int no_dbackspace;                /* disable destructive backspace */\r
+    int no_remote_charset;            /* disable remote charset config */\r
+    int remote_qtitle_action;         /* remote win title query action */\r
+    int app_cursor;\r
+    int app_keypad;\r
+    int nethack_keypad;\r
+    int telnet_keyboard;\r
+    int telnet_newline;\r
+    int alt_f4;                               /* is it special? */\r
+    int alt_space;                    /* is it special? */\r
+    int alt_only;                     /* is it special? */\r
+    int localecho;\r
+    int localedit;\r
+    int alwaysontop;\r
+    int fullscreenonaltenter;\r
+    int scroll_on_key;\r
+    int scroll_on_disp;\r
+    int erase_to_scrollback;\r
+    int compose_key;\r
+    int ctrlaltkeys;\r
+    char wintitle[256];                       /* initial window title */\r
+    /* Terminal options */\r
+    int savelines;\r
+    int dec_om;\r
+    int wrap_mode;\r
+    int lfhascr;\r
+    int cursor_type;                  /* 0=block 1=underline 2=vertical */\r
+    int blink_cur;\r
+    int beep;\r
+    int beep_ind;\r
+    int bellovl;                      /* bell overload protection active? */\r
+    int bellovl_n;                    /* number of bells to cause overload */\r
+    int bellovl_t;                    /* time interval for overload (seconds) */\r
+    int bellovl_s;                    /* period of silence to re-enable bell (s) */\r
+    Filename bell_wavefile;\r
+    int scrollbar;\r
+    int scrollbar_in_fullscreen;\r
+    int resize_action;\r
+    int bce;\r
+    int blinktext;\r
+    int win_name_always;\r
+    int width, height;\r
+    FontSpec font;\r
+    int font_quality;\r
+    Filename logfilename;\r
+    int logtype;\r
+    int logxfovr;\r
+    int logflush;\r
+    int logomitpass;\r
+    int logomitdata;\r
+    int hide_mouseptr;\r
+    int sunken_edge;\r
+    int window_border;\r
+    char answerback[256];\r
+    char printer[128];\r
+    int arabicshaping;\r
+    int bidi;\r
+    /* Colour options */\r
+    int ansi_colour;\r
+    int xterm_256_colour;\r
+    int system_colour;\r
+    int try_palette;\r
+    int bold_colour;\r
+    unsigned char colours[22][3];\r
+    /* Selection options */\r
+    int mouse_is_xterm;\r
+    int rect_select;\r
+    int rawcnp;\r
+    int rtf_paste;\r
+    int mouse_override;\r
+    short wordness[256];\r
+    /* translations */\r
+    int vtmode;\r
+    char line_codepage[128];\r
+    int cjk_ambig_wide;\r
+    int utf8_override;\r
+    int xlat_capslockcyr;\r
+    /* X11 forwarding */\r
+    int x11_forward;\r
+    char x11_display[128];\r
+    int x11_auth;\r
+    Filename xauthfile;\r
+    /* port forwarding */\r
+    int lport_acceptall; /* accept conns from hosts other than localhost */\r
+    int rport_acceptall; /* same for remote forwarded ports (SSH-2 only) */\r
+    /*\r
+     * The port forwarding string contains a number of\r
+     * NUL-terminated substrings, terminated in turn by an empty\r
+     * string (i.e. a second NUL immediately after the previous\r
+     * one). Each string can be of one of the following forms:\r
+     * \r
+     *   [LR]localport\thost:port\r
+     *   [LR]localaddr:localport\thost:port\r
+     *   Dlocalport\r
+     *   Dlocaladdr:localport\r
+     */\r
+    char portfwd[1024];\r
+    /* SSH bug compatibility modes */\r
+    int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,\r
+       sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,\r
+       sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2,\r
+       sshbug_ignore2;\r
+    /*\r
+     * ssh_simple means that we promise never to open any channel other\r
+     * than the main one, which means it can safely use a very large\r
+     * window in SSH-2.\r
+     */\r
+    int ssh_simple;\r
+    /* Options for pterm. Should split out into platform-dependent part. */\r
+    int stamp_utmp;\r
+    int login_shell;\r
+    int scrollbar_on_left;\r
+    int shadowbold;\r
+    FontSpec boldfont;\r
+    FontSpec widefont;\r
+    FontSpec wideboldfont;\r
+    int shadowboldoffset;\r
+    int crhaslf;\r
+    char winclass[256];\r
+};\r
+\r
+/*\r
+ * Some global flags denoting the type of application.\r
+ * \r
+ * FLAG_VERBOSE is set when the user requests verbose details.\r
+ * \r
+ * FLAG_STDERR is set in command-line applications (which have a\r
+ * functioning stderr that it makes sense to write to) and not in\r
+ * GUI applications (which don't).\r
+ * \r
+ * FLAG_INTERACTIVE is set when a full interactive shell session is\r
+ * being run, _either_ because no remote command has been provided\r
+ * _or_ because the application is GUI and can't run non-\r
+ * interactively.\r
+ * \r
+ * These flags describe the type of _application_ - they wouldn't\r
+ * vary between individual sessions - and so it's OK to have this\r
+ * variable be GLOBAL.\r
+ * \r
+ * Note that additional flags may be defined in platform-specific\r
+ * headers. It's probably best if those ones start from 0x1000, to\r
+ * avoid collision.\r
+ */\r
+#define FLAG_VERBOSE     0x0001\r
+#define FLAG_STDERR      0x0002\r
+#define FLAG_INTERACTIVE 0x0004\r
+GLOBAL int flags;\r
+\r
+/*\r
+ * Likewise, these two variables are set up when the application\r
+ * initialises, and inform all default-settings accesses after\r
+ * that.\r
+ */\r
+GLOBAL int default_protocol;\r
+GLOBAL int default_port;\r
+\r
+/*\r
+ * This is set TRUE by cmdline.c iff a session is loaded with "-load".\r
+ */\r
+GLOBAL int loaded_session;\r
+/*\r
+ * This is set to the name of the loaded session.\r
+ */\r
+GLOBAL char *cmdline_session_name;\r
+\r
+struct RSAKey;                        /* be a little careful of scope */\r
+\r
+/*\r
+ * Mechanism for getting text strings such as usernames and passwords\r
+ * from the front-end.\r
+ * The fields are mostly modelled after SSH's keyboard-interactive auth.\r
+ * FIXME We should probably mandate a character set/encoding (probably UTF-8).\r
+ *\r
+ * Since many of the pieces of text involved may be chosen by the server,\r
+ * the caller must take care to ensure that the server can't spoof locally-\r
+ * generated prompts such as key passphrase prompts. Some ground rules:\r
+ *  - If the front-end needs to truncate a string, it should lop off the\r
+ *    end.\r
+ *  - The front-end should filter out any dangerous characters and\r
+ *    generally not trust the strings. (But \n is required to behave\r
+ *    vaguely sensibly, at least in `instruction', and ideally in\r
+ *    `prompt[]' too.)\r
+ */\r
+typedef struct {\r
+    char *prompt;\r
+    int echo;\r
+    char *result;      /* allocated/freed by caller */\r
+    size_t result_len;\r
+} prompt_t;\r
+typedef struct {\r
+    /*\r
+     * Indicates whether the information entered is to be used locally\r
+     * (for instance a key passphrase prompt), or is destined for the wire.\r
+     * This is a hint only; the front-end is at liberty not to use this\r
+     * information (so the caller should ensure that the supplied text is\r
+     * sufficient).\r
+     */\r
+    int to_server;\r
+    char *name;                /* Short description, perhaps for dialog box title */\r
+    int name_reqd;     /* Display of `name' required or optional? */\r
+    char *instruction; /* Long description, maybe with embedded newlines */\r
+    int instr_reqd;    /* Display of `instruction' required or optional? */\r
+    size_t n_prompts;   /* May be zero (in which case display the foregoing,\r
+                         * if any, and return success) */\r
+    prompt_t **prompts;\r
+    void *frontend;\r
+    void *data;                /* slot for housekeeping data, managed by\r
+                        * get_userpass_input(); initially NULL */\r
+} prompts_t;\r
+prompts_t *new_prompts(void *frontend);\r
+void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len);\r
+/* Burn the evidence. (Assumes _all_ strings want free()ing.) */\r
+void free_prompts(prompts_t *p);\r
+\r
+/*\r
+ * Exports from the front end.\r
+ */\r
+void request_resize(void *frontend, int, int);\r
+void do_text(Context, int, int, wchar_t *, int, unsigned long, int);\r
+void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int);\r
+int char_width(Context ctx, int uc);\r
+#ifdef OPTIMISE_SCROLL\r
+void do_scroll(Context, int, int, int);\r
+#endif\r
+void set_title(void *frontend, char *);\r
+void set_icon(void *frontend, char *);\r
+void set_sbar(void *frontend, int, int, int);\r
+Context get_ctx(void *frontend);\r
+void free_ctx(Context);\r
+void palette_set(void *frontend, int, int, int, int);\r
+void palette_reset(void *frontend);\r
+void write_aclip(void *frontend, char *, int, int);\r
+void write_clip(void *frontend, wchar_t *, int *, int, int);\r
+void get_clip(void *frontend, wchar_t **, int *);\r
+void optimised_move(void *frontend, int, int, int);\r
+void set_raw_mouse_mode(void *frontend, int);\r
+void connection_fatal(void *frontend, char *, ...);\r
+void fatalbox(char *, ...);\r
+void modalfatalbox(char *, ...);\r
+#ifdef macintosh\r
+#pragma noreturn(fatalbox)\r
+#pragma noreturn(modalfatalbox)\r
+#endif\r
+void do_beep(void *frontend, int);\r
+void begin_session(void *frontend);\r
+void sys_cursor(void *frontend, int x, int y);\r
+void request_paste(void *frontend);\r
+void frontend_keypress(void *frontend);\r
+void ldisc_update(void *frontend, int echo, int edit);\r
+/* It's the backend's responsibility to invoke this at the start of a\r
+ * connection, if necessary; it can also invoke it later if the set of\r
+ * special commands changes. It does not need to invoke it at session\r
+ * shutdown. */\r
+void update_specials_menu(void *frontend);\r
+int from_backend(void *frontend, int is_stderr, const char *data, int len);\r
+int from_backend_untrusted(void *frontend, const char *data, int len);\r
+void notify_remote_exit(void *frontend);\r
+/* Get a sensible value for a tty mode. NULL return = don't set.\r
+ * Otherwise, returned value should be freed by caller. */\r
+char *get_ttymode(void *frontend, const char *mode);\r
+/*\r
+ * >0 = `got all results, carry on'\r
+ * 0  = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?)\r
+ * <0 = `please call back later with more in/inlen'\r
+ */\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen);\r
+#define OPTIMISE_IS_SCROLL 1\r
+\r
+void set_iconic(void *frontend, int iconic);\r
+void move_window(void *frontend, int x, int y);\r
+void set_zorder(void *frontend, int top);\r
+void refresh_window(void *frontend);\r
+void set_zoomed(void *frontend, int zoomed);\r
+int is_iconic(void *frontend);\r
+void get_window_pos(void *frontend, int *x, int *y);\r
+void get_window_pixels(void *frontend, int *x, int *y);\r
+char *get_window_title(void *frontend, int icon);\r
+/* Hint from backend to frontend about time-consuming operations.\r
+ * Initial state is assumed to be BUSY_NOT. */\r
+enum {\r
+    BUSY_NOT,      /* Not busy, all user interaction OK */\r
+    BUSY_WAITING,   /* Waiting for something; local event loops still running\r
+                      so some local interaction (e.g. menus) OK, but network\r
+                      stuff is suspended */\r
+    BUSY_CPU       /* Locally busy (e.g. crypto); user interaction suspended */\r
+};\r
+void set_busy_status(void *frontend, int status);\r
+\r
+void cleanup_exit(int);\r
+\r
+/*\r
+ * Exports from noise.c.\r
+ */\r
+void noise_get_heavy(void (*func) (void *, int));\r
+void noise_get_light(void (*func) (void *, int));\r
+void noise_regular(void);\r
+void noise_ultralight(unsigned long data);\r
+void random_save_seed(void);\r
+void random_destroy_seed(void);\r
+\r
+/*\r
+ * Exports from settings.c.\r
+ */\r
+Backend *backend_from_name(const char *name);\r
+Backend *backend_from_proto(int proto);\r
+int get_remote_username(Config *cfg, char *user, size_t len);\r
+char *save_settings(char *section, Config * cfg);\r
+void save_open_settings(void *sesskey, Config *cfg);\r
+void load_settings(char *section, Config * cfg);\r
+void load_open_settings(void *sesskey, Config *cfg);\r
+void get_sesslist(struct sesslist *, int allocate);\r
+void do_defaults(char *, Config *);\r
+void registry_cleanup(void);\r
+\r
+/*\r
+ * Functions used by settings.c to provide platform-specific\r
+ * default settings.\r
+ * \r
+ * (The integer one is expected to return `def' if it has no clear\r
+ * opinion of its own. This is because there's no integer value\r
+ * which I can reliably set aside to indicate `nil'. The string\r
+ * function is perfectly all right returning NULL, of course. The\r
+ * Filename and FontSpec functions are _not allowed_ to fail to\r
+ * return, since these defaults _must_ be per-platform.)\r
+ */\r
+char *platform_default_s(const char *name);\r
+int platform_default_i(const char *name, int def);\r
+Filename platform_default_filename(const char *name);\r
+FontSpec platform_default_fontspec(const char *name);\r
+\r
+/*\r
+ * Exports from terminal.c.\r
+ */\r
+\r
+Terminal *term_init(Config *, struct unicode_data *, void *);\r
+void term_free(Terminal *);\r
+void term_size(Terminal *, int, int, int);\r
+void term_paint(Terminal *, Context, int, int, int, int, int);\r
+void term_scroll(Terminal *, int, int);\r
+void term_scroll_to_selection(Terminal *, int);\r
+void term_pwron(Terminal *, int);\r
+void term_clrsb(Terminal *);\r
+void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,\r
+               int,int,int,int,int);\r
+void term_key(Terminal *, Key_Sym, wchar_t *, size_t, unsigned int,\r
+             unsigned int);\r
+void term_deselect(Terminal *);\r
+void term_update(Terminal *);\r
+void term_invalidate(Terminal *);\r
+void term_blink(Terminal *, int set_cursor);\r
+void term_do_paste(Terminal *);\r
+int term_paste_pending(Terminal *);\r
+void term_paste(Terminal *);\r
+void term_nopaste(Terminal *);\r
+int term_ldisc(Terminal *, int option);\r
+void term_copyall(Terminal *);\r
+void term_reconfig(Terminal *, Config *);\r
+void term_seen_key_event(Terminal *); \r
+int term_data(Terminal *, int is_stderr, const char *data, int len);\r
+int term_data_untrusted(Terminal *, const char *data, int len);\r
+void term_provide_resize_fn(Terminal *term,\r
+                           void (*resize_fn)(void *, int, int),\r
+                           void *resize_ctx);\r
+void term_provide_logctx(Terminal *term, void *logctx);\r
+void term_set_focus(Terminal *term, int has_focus);\r
+char *term_get_ttymode(Terminal *term, const char *mode);\r
+int term_get_userpass_input(Terminal *term, prompts_t *p,\r
+                           unsigned char *in, int inlen);\r
+\r
+int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);\r
+\r
+/*\r
+ * Exports from logging.c.\r
+ */\r
+void *log_init(void *frontend, Config *cfg);\r
+void log_free(void *logctx);\r
+void log_reconfig(void *logctx, Config *cfg);\r
+void logfopen(void *logctx);\r
+void logfclose(void *logctx);\r
+void logtraffic(void *logctx, unsigned char c, int logmode);\r
+void logflush(void *logctx);\r
+void log_eventlog(void *logctx, const char *string);\r
+enum { PKT_INCOMING, PKT_OUTGOING };\r
+enum { PKTLOG_EMIT, PKTLOG_BLANK, PKTLOG_OMIT };\r
+struct logblank_t {\r
+    int offset;\r
+    int len;\r
+    int type;\r
+};\r
+void log_packet(void *logctx, int direction, int type,\r
+               char *texttype, const void *data, int len,\r
+               int n_blanks, const struct logblank_t *blanks,\r
+               const unsigned long *sequence);\r
+\r
+/*\r
+ * Exports from testback.c\r
+ */\r
+\r
+extern Backend null_backend;\r
+extern Backend loop_backend;\r
+\r
+/*\r
+ * Exports from raw.c.\r
+ */\r
+\r
+extern Backend raw_backend;\r
+\r
+/*\r
+ * Exports from rlogin.c.\r
+ */\r
+\r
+extern Backend rlogin_backend;\r
+\r
+/*\r
+ * Exports from telnet.c.\r
+ */\r
+\r
+extern Backend telnet_backend;\r
+\r
+/*\r
+ * Exports from ssh.c.\r
+ */\r
+extern Backend ssh_backend;\r
+\r
+/*\r
+ * Exports from ldisc.c.\r
+ */\r
+void *ldisc_create(Config *, Terminal *, Backend *, void *, void *);\r
+void ldisc_free(void *);\r
+void ldisc_send(void *handle, char *buf, int len, int interactive);\r
+\r
+/*\r
+ * Exports from ldiscucs.c.\r
+ */\r
+void lpage_send(void *, int codepage, char *buf, int len, int interactive);\r
+void luni_send(void *, wchar_t * widebuf, int len, int interactive);\r
+\r
+/*\r
+ * Exports from sshrand.c.\r
+ */\r
+\r
+void random_add_noise(void *noise, int length);\r
+int random_byte(void);\r
+void random_get_savedata(void **data, int *len);\r
+extern int random_active;\r
+/* The random number subsystem is activated if at least one other entity\r
+ * within the program expresses an interest in it. So each SSH session\r
+ * calls random_ref on startup and random_unref on shutdown. */\r
+void random_ref(void);\r
+void random_unref(void);\r
+\r
+/*\r
+ * Exports from pinger.c.\r
+ */\r
+typedef struct pinger_tag *Pinger;\r
+Pinger pinger_new(Config *cfg, Backend *back, void *backhandle);\r
+void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg);\r
+void pinger_free(Pinger);\r
+\r
+/*\r
+ * Exports from misc.c.\r
+ */\r
+\r
+#include "misc.h"\r
+int cfg_launchable(const Config *cfg);\r
+char const *cfg_dest(const Config *cfg);\r
+\r
+/*\r
+ * Exports from sercfg.c.\r
+ */\r
+void ser_setup_config_box(struct controlbox *b, int midsession,\r
+                         int parity_mask, int flow_mask);\r
+\r
+/*\r
+ * Exports from version.c.\r
+ */\r
+extern char ver[];\r
+\r
+/*\r
+ * Exports from unicode.c.\r
+ */\r
+#ifndef CP_UTF8\r
+#define CP_UTF8 65001\r
+#endif\r
+/* void init_ucs(void); -- this is now in platform-specific headers */\r
+int is_dbcs_leadbyte(int codepage, char byte);\r
+int mb_to_wc(int codepage, int flags, char *mbstr, int mblen,\r
+            wchar_t *wcstr, int wclen);\r
+int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen,\r
+            char *mbstr, int mblen, char *defchr, int *defused,\r
+            struct unicode_data *ucsdata);\r
+wchar_t xlat_uskbd2cyrllic(int ch);\r
+int check_compose(int first, int second);\r
+int decode_codepage(char *cp_name);\r
+const char *cp_enumerate (int index);\r
+const char *cp_name(int codepage);\r
+void get_unitab(int codepage, wchar_t * unitab, int ftype);\r
+\r
+/*\r
+ * Exports from wcwidth.c\r
+ */\r
+int mk_wcwidth(wchar_t ucs);\r
+int mk_wcswidth(const wchar_t *pwcs, size_t n);\r
+int mk_wcwidth_cjk(wchar_t ucs);\r
+int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n);\r
+\r
+/*\r
+ * Exports from mscrypto.c\r
+ */\r
+#ifdef MSCRYPTOAPI\r
+int crypto_startup();\r
+void crypto_wrapup();\r
+#endif\r
+\r
+/*\r
+ * Exports from pageantc.c.\r
+ * \r
+ * agent_query returns 1 for here's-a-response, and 0 for query-in-\r
+ * progress. In the latter case there will be a call to `callback'\r
+ * at some future point, passing callback_ctx as the first\r
+ * parameter and the actual reply data as the second and third.\r
+ * \r
+ * The response may be a NULL pointer (in either of the synchronous\r
+ * or asynchronous cases), which indicates failure to receive a\r
+ * response.\r
+ */\r
+int agent_query(void *in, int inlen, void **out, int *outlen,\r
+               void (*callback)(void *, void *, int), void *callback_ctx);\r
+int agent_exists(void);\r
+\r
+/*\r
+ * Exports from wildcard.c\r
+ */\r
+const char *wc_error(int value);\r
+int wc_match(const char *wildcard, const char *target);\r
+int wc_unescape(char *output, const char *wildcard);\r
+\r
+/*\r
+ * Exports from frontend (windlg.c etc)\r
+ */\r
+void logevent(void *frontend, const char *);\r
+void pgp_fingerprints(void);\r
+/*\r
+ * verify_ssh_host_key() can return one of three values:\r
+ * \r
+ *  - +1 means `key was OK' (either already known or the user just\r
+ *    approved it) `so continue with the connection'\r
+ * \r
+ *  - 0 means `key was not OK, abandon the connection'\r
+ * \r
+ *  - -1 means `I've initiated enquiries, please wait to be called\r
+ *    back via the provided function with a result that's either 0\r
+ *    or +1'.\r
+ */\r
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
+                        char *keystr, char *fingerprint,\r
+                        void (*callback)(void *ctx, int result), void *ctx);\r
+/*\r
+ * askalg has the same set of return values as verify_ssh_host_key.\r
+ */\r
+int askalg(void *frontend, const char *algtype, const char *algname,\r
+          void (*callback)(void *ctx, int result), void *ctx);\r
+/*\r
+ * askappend can return four values:\r
+ * \r
+ *  - 2 means overwrite the log file\r
+ *  - 1 means append to the log file\r
+ *  - 0 means cancel logging for this session\r
+ *  - -1 means please wait.\r
+ */\r
+int askappend(void *frontend, Filename filename,\r
+             void (*callback)(void *ctx, int result), void *ctx);\r
+\r
+/*\r
+ * Exports from console frontends (wincons.c, uxcons.c)\r
+ * that aren't equivalents to things in windlg.c et al.\r
+ */\r
+extern int console_batch_mode;\r
+int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen);\r
+void console_provide_logctx(void *logctx);\r
+int is_interactive(void);\r
+\r
+/*\r
+ * Exports from printing.c.\r
+ */\r
+typedef struct printer_enum_tag printer_enum;\r
+typedef struct printer_job_tag printer_job;\r
+printer_enum *printer_start_enum(int *nprinters);\r
+char *printer_get_name(printer_enum *, int);\r
+void printer_finish_enum(printer_enum *);\r
+printer_job *printer_start_job(char *printer);\r
+void printer_job_data(printer_job *, void *, int);\r
+void printer_finish_job(printer_job *);\r
+\r
+/*\r
+ * Exports from cmdline.c (and also cmdline_error(), which is\r
+ * defined differently in various places and required _by_\r
+ * cmdline.c).\r
+ */\r
+int cmdline_process_param(char *, char *, int, Config *);\r
+void cmdline_run_saved(Config *);\r
+void cmdline_cleanup(void);\r
+int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen);\r
+#define TOOLTYPE_FILETRANSFER 1\r
+#define TOOLTYPE_NONNETWORK 2\r
+extern int cmdline_tooltype;\r
+\r
+void cmdline_error(char *, ...);\r
+\r
+/*\r
+ * Exports from config.c.\r
+ */\r
+struct controlbox;\r
+void setup_config_box(struct controlbox *b, int midsession,\r
+                     int protocol, int protcfginfo);\r
+\r
+/*\r
+ * Exports from minibidi.c.\r
+ */\r
+typedef struct bidi_char {\r
+    wchar_t origwc, wc;\r
+    unsigned short index;\r
+} bidi_char;\r
+int do_bidi(bidi_char *line, int count);\r
+int do_shape(bidi_char *line, bidi_char *to, int count);\r
+int is_rtl(int c);\r
+\r
+/*\r
+ * X11 auth mechanisms we know about.\r
+ */\r
+enum {\r
+    X11_NO_AUTH,\r
+    X11_MIT,                           /* MIT-MAGIC-COOKIE-1 */\r
+    X11_XDM,                          /* XDM-AUTHORIZATION-1 */\r
+    X11_NAUTHS\r
+};\r
+extern const char *const x11_authnames[];  /* declared in x11fwd.c */\r
+\r
+/*\r
+ * Miscellaneous exports from the platform-specific code.\r
+ */\r
+Filename filename_from_str(const char *string);\r
+const char *filename_to_str(const Filename *fn);\r
+int filename_equal(Filename f1, Filename f2);\r
+int filename_is_null(Filename fn);\r
+char *get_username(void);             /* return value needs freeing */\r
+char *get_random_data(int bytes);      /* used in cmdgen.c */\r
+\r
+/*\r
+ * Exports and imports from timing.c.\r
+ *\r
+ * schedule_timer() asks the front end to schedule a callback to a\r
+ * timer function in a given number of ticks. The returned value is\r
+ * the time (in ticks since an arbitrary offset) at which the\r
+ * callback can be expected. This value will also be passed as the\r
+ * `now' parameter to the callback function. Hence, you can (for\r
+ * example) schedule an event at a particular time by calling\r
+ * schedule_timer() and storing the return value in your context\r
+ * structure as the time when that event is due. The first time a\r
+ * callback function gives you that value or more as `now', you do\r
+ * the thing.\r
+ * \r
+ * expire_timer_context() drops all current timers associated with\r
+ * a given value of ctx (for when you're about to free ctx).\r
+ * \r
+ * run_timers() is called from the front end when it has reason to\r
+ * think some timers have reached their moment, or when it simply\r
+ * needs to know how long to wait next. We pass it the time we\r
+ * think it is. It returns TRUE and places the time when the next\r
+ * timer needs to go off in `next', or alternatively it returns\r
+ * FALSE if there are no timers at all pending.\r
+ * \r
+ * timer_change_notify() must be supplied by the front end; it\r
+ * notifies the front end that a new timer has been added to the\r
+ * list which is sooner than any existing ones. It provides the\r
+ * time when that timer needs to go off.\r
+ * \r
+ * *** FRONT END IMPLEMENTORS NOTE:\r
+ * \r
+ * There's an important subtlety in the front-end implementation of\r
+ * the timer interface. When a front end is given a `next' value,\r
+ * either returned from run_timers() or via timer_change_notify(),\r
+ * it should ensure that it really passes _that value_ as the `now'\r
+ * parameter to its next run_timers call. It should _not_ simply\r
+ * call GETTICKCOUNT() to get the `now' parameter when invoking\r
+ * run_timers().\r
+ * \r
+ * The reason for this is that an OS's system clock might not agree\r
+ * exactly with the timing mechanisms it supplies to wait for a\r
+ * given interval. I'll illustrate this by the simple example of\r
+ * Unix Plink, which uses timeouts to select() in a way which for\r
+ * these purposes can simply be considered to be a wait() function.\r
+ * Suppose, for the sake of argument, that this wait() function\r
+ * tends to return early by 1%. Then a possible sequence of actions\r
+ * is:\r
+ * \r
+ *  - run_timers() tells the front end that the next timer firing\r
+ *    is 10000ms from now.\r
+ *  - Front end calls wait(10000ms), but according to\r
+ *    GETTICKCOUNT() it has only waited for 9900ms.\r
+ *  - Front end calls run_timers() again, passing time T-100ms as\r
+ *    `now'.\r
+ *  - run_timers() does nothing, and says the next timer firing is\r
+ *    still 100ms from now.\r
+ *  - Front end calls wait(100ms), which only waits for 99ms.\r
+ *  - Front end calls run_timers() yet again, passing time T-1ms.\r
+ *  - run_timers() says there's still 1ms to wait.\r
+ *  - Front end calls wait(1ms).\r
+ * \r
+ * If you're _lucky_ at this point, wait(1ms) will actually wait\r
+ * for 1ms and you'll only have woken the program up three times.\r
+ * If you're unlucky, wait(1ms) might do nothing at all due to\r
+ * being below some minimum threshold, and you might find your\r
+ * program spends the whole of the last millisecond tight-looping\r
+ * between wait() and run_timers().\r
+ * \r
+ * Instead, what you should do is to _save_ the precise `next'\r
+ * value provided by run_timers() or via timer_change_notify(), and\r
+ * use that precise value as the input to the next run_timers()\r
+ * call. So:\r
+ * \r
+ *  - run_timers() tells the front end that the next timer firing\r
+ *    is at time T, 10000ms from now.\r
+ *  - Front end calls wait(10000ms).\r
+ *  - Front end then immediately calls run_timers() and passes it\r
+ *    time T, without stopping to check GETTICKCOUNT() at all.\r
+ * \r
+ * This guarantees that the program wakes up only as many times as\r
+ * there are actual timer actions to be taken, and that the timing\r
+ * mechanism will never send it into a tight loop.\r
+ * \r
+ * (It does also mean that the timer action in the above example\r
+ * will occur 100ms early, but this is not generally critical. And\r
+ * the hypothetical 1% error in wait() will be partially corrected\r
+ * for anyway when, _after_ run_timers() returns, you call\r
+ * GETTICKCOUNT() and compare the result with the returned `next'\r
+ * value to find out how long you have to make your next wait().)\r
+ */\r
+typedef void (*timer_fn_t)(void *ctx, long now);\r
+long schedule_timer(int ticks, timer_fn_t fn, void *ctx);\r
+void expire_timer_context(void *ctx);\r
+int run_timers(long now, long *next);\r
+void timer_change_notify(long next);\r
+\r
+/*\r
+ * Define no-op macros for the jump list functions, on platforms that\r
+ * don't support them. (This is a bit of a hack, and it'd be nicer to\r
+ * localise even the calls to those functions into the Windows front\r
+ * end, but it'll do for the moment.)\r
+ */\r
+#ifndef JUMPLIST_SUPPORTED\r
+#define add_session_to_jumplist(x) ((void)0)\r
+#define remove_session_from_jumplist(x) ((void)0)\r
+#endif\r
+\r
+#endif\r
diff --git a/putty/PUTTYMEM.H b/putty/PUTTYMEM.H
new file mode 100644 (file)
index 0000000..d478f79
--- /dev/null
@@ -0,0 +1,42 @@
+/*\r
+ * PuTTY memory-handling header.\r
+ */\r
+\r
+#ifndef PUTTY_PUTTYMEM_H\r
+#define PUTTY_PUTTYMEM_H\r
+\r
+#include <stddef.h>                   /* for size_t */\r
+#include <string.h>                   /* for memcpy() */\r
+\r
+\r
+/* #define MALLOC_LOG  do this if you suspect putty of leaking memory */\r
+#ifdef MALLOC_LOG\r
+#define smalloc(z) (mlog(__FILE__,__LINE__), safemalloc(z,1))\r
+#define snmalloc(z,s) (mlog(__FILE__,__LINE__), safemalloc(z,s))\r
+#define srealloc(y,z) (mlog(__FILE__,__LINE__), saferealloc(y,z,1))\r
+#define snrealloc(y,z,s) (mlog(__FILE__,__LINE__), saferealloc(y,z,s))\r
+#define sfree(z) (mlog(__FILE__,__LINE__), safefree(z))\r
+void mlog(char *, int);\r
+#else\r
+#define smalloc(z) safemalloc(z,1)\r
+#define snmalloc safemalloc\r
+#define srealloc(y,z) saferealloc(y,z,1)\r
+#define snrealloc saferealloc\r
+#define sfree safefree\r
+#endif\r
+\r
+void *safemalloc(size_t, size_t);\r
+void *saferealloc(void *, size_t, size_t);\r
+void safefree(void *);\r
+\r
+/*\r
+ * Direct use of smalloc within the code should be avoided where\r
+ * possible, in favour of these type-casting macros which ensure\r
+ * you don't mistakenly allocate enough space for one sort of\r
+ * structure and assign it to a different sort of pointer.\r
+ */\r
+#define snew(type) ((type *)snmalloc(1, sizeof(type)))\r
+#define snewn(n, type) ((type *)snmalloc((n), sizeof(type)))\r
+#define sresize(ptr, n, type) ((type *)snrealloc((ptr), (n), sizeof(type)))\r
+\r
+#endif\r
diff --git a/putty/PUTTYPS.H b/putty/PUTTYPS.H
new file mode 100644 (file)
index 0000000..454f605
--- /dev/null
@@ -0,0 +1,22 @@
+/*\r
+ * Find the platform-specific header for this platform.\r
+ */\r
+\r
+#ifndef PUTTY_PUTTYPS_H\r
+#define PUTTY_PUTTYPS_H\r
+\r
+#ifdef _WINDOWS\r
+\r
+#include "winstuff.h"\r
+\r
+#elif defined(MACOSX)\r
+\r
+#include "osx.h"\r
+\r
+#else\r
+\r
+#include "unix.h"\r
+\r
+#endif\r
+\r
+#endif\r
diff --git a/putty/PuTTY.vc90.vcproj b/putty/PuTTY.vc90.vcproj
new file mode 100644 (file)
index 0000000..78e5d54
--- /dev/null
@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="shift_jis"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9.00"\r
+       Name="PuTTY"\r
+       ProjectGUID="{AF1981EB-379B-43B8-BE66-298194297B5C}"\r
+       RootNamespace="PuTTY"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories=".;CHARSET;WINDOWS"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;WINVER=0x0500,_WIN32_WINNT=0x0500,_WIN32_IE=0x0500,NTDDI_VERSION=0x05000100,SECURITY_WIN32;_USRDLL;PUTTY_EXPORTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="1"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="4"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               LinkIncremental="2"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="2"\r
+                               EnableIntrinsicFunctions="true"\r
+                               AdditionalIncludeDirectories=".;CHARSET;WINDOWS"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0500,_WIN32_WINNT=0x0500,_WIN32_IE=0x0500,NTDDI_VERSION=0x05000100,SECURITY_WIN32;_USRDLL;PUTTY_EXPORTS"\r
+                               RuntimeLibrary="0"\r
+                               EnableFunctionLevelLinking="true"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               LinkIncremental="1"\r
+                               GenerateDebugInformation="false"\r
+                               SubSystem="2"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath=".\BE_NONE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\CMDLINE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\CPROXY.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\dllmain.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\INT64.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\LOGGING.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\MISC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PGSSAPI.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PINGER.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PORTFWD.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PROXY.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PSFTP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SETTINGS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SFTP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSH.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHAES.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHARCF.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHBLOWF.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHBN.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHCRC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHCRCDA.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHDES.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHDH.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHDSS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHGSSC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHMD5.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHPUBK.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHRAND.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHRSA.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHSH256.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHSH512.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHSHA.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHZLIB.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\TIMING.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\TREE234.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\VERSION.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WILDCARD.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINCONS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINDEFS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINGSS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINHANDL.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINMISC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINNET.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINNOISE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINNOJMP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINPGNTC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINPROXY.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINSFTP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINSTORE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINTIME.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\X11FWD.C"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/putty/PuTTY.vcproj b/putty/PuTTY.vcproj
new file mode 100644 (file)
index 0000000..76d679b
--- /dev/null
@@ -0,0 +1,393 @@
+<?xml version="1.0" encoding="shift_jis"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="PuTTY"\r
+       ProjectGUID="{AF1981EB-379B-43B8-BE66-298194297B5C}"\r
+       RootNamespace="PuTTY"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories=".;CHARSET;WINDOWS"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;WINVER=0x0500,_WIN32_WINNT=0x0500,_WIN32_IE=0x0500,NTDDI_VERSION=0x05000100,SECURITY_WIN32;_USRDLL;PUTTY_EXPORTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="1"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="4"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               LinkIncremental="2"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="2"\r
+                               EnableIntrinsicFunctions="true"\r
+                               AdditionalIncludeDirectories=".;CHARSET;WINDOWS"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0500,_WIN32_WINNT=0x0500,_WIN32_IE=0x0500,NTDDI_VERSION=0x05000100,SECURITY_WIN32;_USRDLL;PUTTY_EXPORTS"\r
+                               RuntimeLibrary="0"\r
+                               EnableFunctionLevelLinking="true"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               LinkIncremental="1"\r
+                               GenerateDebugInformation="false"\r
+                               SubSystem="2"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath=".\BE_NONE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\CMDLINE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\CPROXY.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\dllmain.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\INT64.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\LOGGING.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\MISC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PGSSAPI.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PINGER.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PORTFWD.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PROXY.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\PSFTP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SETTINGS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SFTP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSH.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHAES.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHARCF.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHBLOWF.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHBN.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHCRC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHCRCDA.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHDES.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHDH.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHDSS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHGSSC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHMD5.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHPUBK.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHRAND.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHRSA.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHSH256.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHSH512.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHSHA.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\SSHZLIB.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\TIMING.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\TREE234.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\VERSION.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WILDCARD.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINCONS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINDEFS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINGSS.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINHANDL.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINMISC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINNET.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINNOISE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINNOJMP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINPGNTC.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINPROXY.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINSFTP.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINSTORE.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\WINDOWS\WINTIME.C"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\X11FWD.C"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/putty/RAW.C b/putty/RAW.C
new file mode 100644 (file)
index 0000000..ea51d74
--- /dev/null
@@ -0,0 +1,300 @@
+/*\r
+ * "Raw" backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+#define RAW_MAX_BACKLOG 4096\r
+\r
+typedef struct raw_backend_data {\r
+    const struct plug_function_table *fn;\r
+    /* the above field _must_ be first in the structure */\r
+\r
+    Socket s;\r
+    int bufsize;\r
+    void *frontend;\r
+} *Raw;\r
+\r
+static void raw_size(void *handle, int width, int height);\r
+\r
+static void c_write(Raw raw, char *buf, int len)\r
+{\r
+    int backlog = from_backend(raw->frontend, 0, buf, len);\r
+    sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);\r
+}\r
+\r
+static void raw_log(Plug plug, int type, SockAddr addr, int port,\r
+                   const char *error_msg, int error_code)\r
+{\r
+    Raw raw = (Raw) plug;\r
+    char addrbuf[256], *msg;\r
+\r
+    sk_getaddr(addr, addrbuf, lenof(addrbuf));\r
+\r
+    if (type == 0)\r
+       msg = dupprintf("Connecting to %s port %d", addrbuf, port);\r
+    else\r
+       msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);\r
+\r
+    logevent(raw->frontend, msg);\r
+}\r
+\r
+static int raw_closing(Plug plug, const char *error_msg, int error_code,\r
+                      int calling_back)\r
+{\r
+    Raw raw = (Raw) plug;\r
+\r
+    if (raw->s) {\r
+        sk_close(raw->s);\r
+        raw->s = NULL;\r
+       notify_remote_exit(raw->frontend);\r
+    }\r
+    if (error_msg) {\r
+       /* A socket error has occurred. */\r
+       logevent(raw->frontend, error_msg);\r
+       connection_fatal(raw->frontend, "%s", error_msg);\r
+    }                                 /* Otherwise, the remote side closed the connection normally. */\r
+    return 0;\r
+}\r
+\r
+static int raw_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    Raw raw = (Raw) plug;\r
+    c_write(raw, data, len);\r
+    return 1;\r
+}\r
+\r
+static void raw_sent(Plug plug, int bufsize)\r
+{\r
+    Raw raw = (Raw) plug;\r
+    raw->bufsize = bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set up the raw connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *raw_init(void *frontend_handle, void **backend_handle,\r
+                           Config *cfg,\r
+                           char *host, int port, char **realhost, int nodelay,\r
+                           int keepalive)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       raw_log,\r
+       raw_closing,\r
+       raw_receive,\r
+       raw_sent\r
+    };\r
+    SockAddr addr;\r
+    const char *err;\r
+    Raw raw;\r
+\r
+    raw = snew(struct raw_backend_data);\r
+    raw->fn = &fn_table;\r
+    raw->s = NULL;\r
+    *backend_handle = raw;\r
+\r
+    raw->frontend = frontend_handle;\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    {\r
+       char *buf;\r
+       buf = dupprintf("Looking up host \"%s\"%s", host,\r
+                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
+                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :\r
+                         "")));\r
+       logevent(raw->frontend, buf);\r
+       sfree(buf);\r
+    }\r
+    addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+\r
+    if (port < 0)\r
+       port = 23;                     /* default telnet port */\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive,\r
+                           (Plug) raw, cfg);\r
+    if ((err = sk_socket_error(raw->s)) != NULL)\r
+       return err;\r
+\r
+    if (*cfg->loghost) {\r
+       char *colon;\r
+\r
+       sfree(*realhost);\r
+       *realhost = dupstr(cfg->loghost);\r
+       colon = strrchr(*realhost, ':');\r
+       if (colon) {\r
+           /*\r
+            * FIXME: if we ever update this aspect of ssh.c for\r
+            * IPv6 literal management, this should change in line\r
+            * with it.\r
+            */\r
+           *colon++ = '\0';\r
+       }\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+static void raw_free(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+\r
+    if (raw->s)\r
+       sk_close(raw->s);\r
+    sfree(raw);\r
+}\r
+\r
+/*\r
+ * Stub routine (we don't have any need to reconfigure this backend).\r
+ */\r
+static void raw_reconfig(void *handle, Config *cfg)\r
+{\r
+}\r
+\r
+/*\r
+ * Called to send data down the raw connection.\r
+ */\r
+static int raw_send(void *handle, char *buf, int len)\r
+{\r
+    Raw raw = (Raw) handle;\r
+\r
+    if (raw->s == NULL)\r
+       return 0;\r
+\r
+    raw->bufsize = sk_write(raw->s, buf, len);\r
+\r
+    return raw->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to query the current socket sendability status.\r
+ */\r
+static int raw_sendbuffer(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    return raw->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void raw_size(void *handle, int width, int height)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Send raw special codes.\r
+ */\r
+static void raw_special(void *handle, Telnet_Special code)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *raw_get_specials(void *handle)\r
+{\r
+    return NULL;\r
+}\r
+\r
+static int raw_connected(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    return raw->s != NULL;\r
+}\r
+\r
+static int raw_sendok(void *handle)\r
+{\r
+    return 1;\r
+}\r
+\r
+static void raw_unthrottle(void *handle, int backlog)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);\r
+}\r
+\r
+static int raw_ldisc(void *handle, int option)\r
+{\r
+    if (option == LD_EDIT || option == LD_ECHO)\r
+       return 1;\r
+    return 0;\r
+}\r
+\r
+static void raw_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static void raw_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static int raw_exitcode(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    if (raw->s != NULL)\r
+        return -1;                     /* still connected */\r
+    else\r
+        /* Exit codes are a meaningless concept in the Raw protocol */\r
+        return 0;\r
+}\r
+\r
+/*\r
+ * cfg_info for Raw does nothing at all.\r
+ */\r
+static int raw_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+Backend raw_backend = {\r
+    raw_init,\r
+    raw_free,\r
+    raw_reconfig,\r
+    raw_send,\r
+    raw_sendbuffer,\r
+    raw_size,\r
+    raw_special,\r
+    raw_get_specials,\r
+    raw_connected,\r
+    raw_exitcode,\r
+    raw_sendok,\r
+    raw_ldisc,\r
+    raw_provide_ldisc,\r
+    raw_provide_logctx,\r
+    raw_unthrottle,\r
+    raw_cfg_info,\r
+    "raw",\r
+    PROT_RAW,\r
+    0\r
+};\r
diff --git a/putty/README b/putty/README
new file mode 100644 (file)
index 0000000..9cca5c0
--- /dev/null
@@ -0,0 +1,115 @@
+This is the README for the source archive of PuTTY, a free Win32\r
+and Unix Telnet and SSH client.\r
+\r
+If you want to rebuild PuTTY from source, we provide a variety of\r
+Makefiles and equivalents. (If you have fetched the source from\r
+Subversion, you'll have to generate the Makefiles yourself -- see\r
+below.)\r
+\r
+There are various compile-time directives that you can use to\r
+disable or modify certain features; it may be necessary to do this\r
+in some environments. They are documented in `Recipe', and in\r
+comments in many of the generated Makefiles.\r
+\r
+For building on Windows:\r
+\r
+ - windows/Makefile.vc is for command-line builds on MS Visual C++\r
+   systems. Change into the `windows' subdirectory and type `nmake\r
+   -f Makefile.vc' to build all the PuTTY binaries.\r
+\r
+   Last time we checked, PuTTY built with vanilla VC7, or VC6 with\r
+   an up-to-date Platform SDK. (It might still be possible to build\r
+   with vanilla VC6, but you'll certainly have to remove some\r
+   functionality with directives such as NO_IPV6.)\r
+\r
+   (We've also had reports of success building with the\r
+   OpenWatcom compiler -- www.openwatcom.org -- using Makefile.vc\r
+   with `wmake -ms -f makefile.vc' and NO_MULTIMON, although we\r
+   haven't tried this ourselves. Version 1.3 is reported to work.)\r
+\r
+ - Inside the windows/MSVC subdirectory are MS Visual Studio project\r
+   files for doing GUI-based builds of the various PuTTY utilities.\r
+   These have been tested on Visual Studio 6.\r
+\r
+   You should be able to build each PuTTY utility by loading the\r
+   corresponding .dsp file in Visual Studio. For example,\r
+   MSVC/putty/putty.dsp builds PuTTY itself, MSVC/plink/plink.dsp\r
+   builds Plink, and so on.\r
+\r
+ - windows/Makefile.bor is for the Borland C compiler. Type `make -f\r
+   Makefile.bor' while in the `windows' subdirectory to build all\r
+   the PuTTY binaries.\r
+\r
+ - windows/Makefile.cyg is for Cygwin / mingw32 installations. Type\r
+   `make -f Makefile.cyg' while in the `windows' subdirectory to\r
+   build all the PuTTY binaries.\r
+\r
+   You'll probably need quite a recent version of the w32api package.\r
+   Note that by default the multiple monitor and HTML Help support are\r
+   excluded from the Cygwin build, since at the time of writing Cygwin\r
+   doesn't include the necessary headers.\r
+\r
+ - windows/Makefile.lcc is for lcc-win32. Type `make -f\r
+   Makefile.lcc' while in the `windows' subdirectory. (You will\r
+   probably need to specify COMPAT=-DNO_MULTIMON.)\r
+\r
+ - Inside the windows/DEVCPP subdirectory are Dev-C++ project\r
+   files for doing GUI-based builds of the various PuTTY utilities.\r
+\r
+The PuTTY team actively use Makefile.vc (with VC7) and Makefile.cyg\r
+(with mingw32), so we'll probably notice problems with those\r
+toolchains fairly quickly. Please report any problems with the other\r
+toolchains mentioned above.\r
+\r
+For building on Unix:\r
+\r
+ - unix/configure is for Unix and GTK. If you don't have GTK, you\r
+   should still be able to build the command-line utilities (PSCP,\r
+   PSFTP, Plink, PuTTYgen) using this script. To use it, change\r
+   into the `unix' subdirectory, run `./configure' and then `make'.\r
+\r
+   Note that Unix PuTTY has mostly only been tested on Linux so far;\r
+   portability problems such as BSD-style ptys or different header file\r
+   requirements are expected.\r
+\r
+ - unix/Makefile.gtk and unix/Makefile.ux are for non-autoconfigured\r
+   builds. These makefiles expect you to change into the `unix'\r
+   subdirectory, then run `make -f Makefile.gtk' or `make -f\r
+   Makefile.ux' respectively. Makefile.gtk builds all the programs but\r
+   relies on Gtk, whereas Makefile.ux builds only the command-line\r
+   utilities and has no Gtk dependence.\r
+\r
+ - For the graphical utilities, Gtk+-1.2 and Gtk+-2.0 should both be\r
+   supported.\r
+\r
+ - Both Unix Makefiles have an `install' target. Note that by default\r
+   it tries to install `man' pages, which you may need to have built\r
+   using Halibut first -- see below.\r
+\r
+All of the Makefiles are generated automatically from the file\r
+`Recipe' by the Perl script `mkfiles.pl'. Additions and corrections\r
+to Recipe and the mkfiles.pl are much more useful than additions and\r
+corrections to the alternative Makefiles themselves.\r
+\r
+The Unix `configure' script and its various requirements are generated\r
+by the shell script `mkauto.sh', which requires GNU Autoconf, GNU\r
+Automake, and Gtk; if you've got the source from Subversion rather\r
+than using one of our source snapshots, you'll need to run this\r
+yourself.\r
+\r
+Documentation (in various formats including Windows Help and Unix\r
+`man' pages) is built from the Halibut (`.but') files in the `doc'\r
+subdirectory using `doc/Makefile'. If you aren't using one of our\r
+source snapshots, you'll need to do this yourself. Halibut can be\r
+found at <http://www.chiark.greenend.org.uk/~sgtatham/halibut/>.\r
+\r
+The PuTTY home web site is\r
+\r
+    http://www.chiark.greenend.org.uk/~sgtatham/putty/\r
+\r
+If you want to send bug reports or feature requests, please read the\r
+Feedback section of the web site before doing so. Sending one-line\r
+reports saying `it doesn't work' will waste your time as much as\r
+ours.\r
+\r
+See the file LICENCE for the licence conditions.\r
diff --git a/putty/RECIPE b/putty/RECIPE
new file mode 100644 (file)
index 0000000..ce59a75
--- /dev/null
@@ -0,0 +1,347 @@
+# -*- makefile -*-\r
+# \r
+# This file describes which PuTTY programs are made up from which\r
+# object and resource files. It is processed into the various\r
+# Makefiles by means of a Perl script. Makefile changes should\r
+# really be made by editing this file and/or the Perl script, not\r
+# by editing the actual Makefiles.\r
+\r
+# ------------------------------------------------------------\r
+# Top-level configuration.\r
+\r
+# Overall project name.\r
+!name putty\r
+# Locations and types of output Makefiles.\r
+!makefile vc windows/Makefile.vc\r
+!makefile vcproj windows/MSVC\r
+!makefile cygwin windows/Makefile.cyg\r
+!makefile borland windows/Makefile.bor\r
+!makefile lcc windows/Makefile.lcc\r
+!makefile gtk unix/Makefile.gtk\r
+!makefile unix unix/Makefile.ux\r
+!makefile ac unix/Makefile.in\r
+!makefile osx macosx/Makefile\r
+!makefile devcppproj windows/DEVCPP\r
+# Source directories.\r
+!srcdir charset/\r
+!srcdir windows/\r
+!srcdir unix/\r
+!srcdir macosx/\r
+\r
+# Help text added to the top of each Makefile, with /D converted\r
+# into -D as appropriate for the particular Makefile.\r
+\r
+!begin help\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="/DSNAPSHOT=1999-01-25 /DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=/DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=/DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=/DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=/DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=/DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=/DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=/DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=/DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=/DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=/DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=/DMSVC4 (Windows only)\r
+#  - RCFL=/DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need /DNO_SECURITY and /DNO_MULTIMON.\r
+#\r
+#  - RCFL=/DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=/DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=/DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=/DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=/DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+!end\r
+\r
+# ------------------------------------------------------------\r
+# Additional text added verbatim to each individual Makefile.\r
+\r
+# Hack to force version.o to be rebuilt always.\r
+!begin vc\r
+version.obj: *.c *.h *.rc\r
+       cl $(VER) $(CFLAGS) /c ..\version.c\r
+!end\r
+!specialobj vc version\r
+!begin cygwin\r
+version.o: FORCE\r
+       $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c\r
+!end\r
+!specialobj cygwin version\r
+!begin borland\r
+version.obj: FORCE\r
+       bcc32 $(VER) $(CFLAGS) /c ..\version.c\r
+!end\r
+!specialobj borland version\r
+!begin lcc\r
+version.obj: FORCE\r
+       lcc $(VER) $(CFLAGS) /c ..\version.c\r
+!end\r
+!specialobj lcc version\r
+# For Unix, we also need the gross MD5 hack that causes automatic\r
+# version number selection in release source archives.\r
+!begin gtk\r
+version.o: FORCE\r
+       if test -z "$(VER)" && (cd ..; md5sum -c manifest); then \\r
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat ../version.def` -c ../version.c; \\r
+       else \\r
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c; \\r
+       fi\r
+!end\r
+!specialobj gtk version\r
+\r
+# Add VER to Windows resource targets, and force them to be rebuilt every\r
+# time, on the assumption that they will contain version information.\r
+!begin vc vars\r
+CFLAGS = $(CFLAGS) /DHAS_GSSAPI /DSECURITY_WIN32\r
+RCFLAGS = $(RCFLAGS) $(VER)\r
+!end\r
+!begin cygwin vars\r
+# XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile.\r
+RCFLAGS += $(patsubst -D%,--define %,$(VER))\r
+!end\r
+!begin borland vars\r
+# Borland doesn't support +=. This probably shouldn't work, but seems to.\r
+RCFLAGS = $(RCFLAGS) $(VER)\r
+!end\r
+!begin lcc vars\r
+RCFLAGS += $(VER)\r
+!end\r
+!forceobj putty.res\r
+!forceobj puttytel.res\r
+!forceobj plink.res\r
+!forceobj pscp.res\r
+!forceobj psftp.res\r
+!forceobj pageant.res\r
+!forceobj puttygen.res\r
+\r
+# `make install' target for Unix.\r
+!begin gtk\r
+install:\r
+       mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir)\r
+       $(INSTALL_PROGRAM) -m 755 plink $(DESTDIR)$(bindir)/plink\r
+       $(INSTALL_PROGRAM) -m 755 pscp $(DESTDIR)$(bindir)/pscp\r
+       $(INSTALL_PROGRAM) -m 755 psftp $(DESTDIR)$(bindir)/psftp\r
+       $(INSTALL_PROGRAM) -m 755 pterm $(DESTDIR)$(bindir)/pterm\r
+       if test -n "$(UTMP_GROUP)"; then \\r
+         chgrp $(UTMP_GROUP) $(DESTDIR)$(bindir)/pterm && \\r
+           chmod 2755 $(DESTDIR)$(bindir)/pterm; \\r
+       elif test -n "$(UTMP_USER)"; then \\r
+         chown $(UTMP_USER) $(DESTDIR)$(bindir)/pterm && \\r
+           chmod 4755 $(DESTDIR)$(bindir)/pterm; \\r
+       fi\r
+       $(INSTALL_PROGRAM) -m 755 putty $(DESTDIR)$(bindir)/putty\r
+       $(INSTALL_PROGRAM) -m 755 puttygen $(DESTDIR)$(bindir)/puttygen\r
+       $(INSTALL_PROGRAM) -m 755 puttytel $(DESTDIR)$(bindir)/puttytel\r
+       $(INSTALL_DATA) -m 644 ../doc/plink.1 $(DESTDIR)$(man1dir)/plink.1\r
+       $(INSTALL_DATA) -m 644 ../doc/pscp.1 $(DESTDIR)$(man1dir)/pscp.1\r
+       $(INSTALL_DATA) -m 644 ../doc/psftp.1 $(DESTDIR)$(man1dir)/psftp.1\r
+       $(INSTALL_DATA) -m 644 ../doc/pterm.1 $(DESTDIR)$(man1dir)/pterm.1\r
+       $(INSTALL_DATA) -m 644 ../doc/putty.1 $(DESTDIR)$(man1dir)/putty.1\r
+       $(INSTALL_DATA) -m 644 ../doc/puttygen.1 $(DESTDIR)$(man1dir)/puttygen.1\r
+       $(INSTALL_DATA) -m 644 ../doc/puttytel.1 $(DESTDIR)$(man1dir)/puttytel.1\r
+\r
+install-strip:\r
+       $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s"\r
+!end\r
+!begin osx vars\r
+CFLAGS += -DMACOSX\r
+!end\r
+\r
+# Random symbols.\r
+!begin cygwin vars\r
+# _WIN32_IE is required to expose identifiers that only make sense on\r
+# systems with IE5+ installed, such as some arguments to SHGetFolderPath().\r
+# WINVER etc perform a similar function for FlashWindowEx().\r
+CFLAGS += -D_WIN32_IE=0x0500\r
+CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500\r
+!end\r
+\r
+# ------------------------------------------------------------\r
+# Definitions of object groups. A group name, followed by an =,\r
+# followed by any number of objects or other already-defined group\r
+# names. A line beginning `+' is assumed to continue the previous\r
+# line.\r
+\r
+# Terminal emulator and its (platform-independent) dependencies.\r
+TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi\r
+         + config dialog\r
+\r
+# GUI front end and terminal emulator (putty, puttytel).\r
+GUITERM  = TERMINAL window windlg winctrls sizetip winucs winprint\r
+         + winutils wincfg sercfg winhelp winjump\r
+\r
+# Same thing on Unix.\r
+UXTERM   = TERMINAL uxcfg sercfg uxucs uxprint timing\r
+GTKTERM  = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols xkeysym\r
+OSXTERM  = UXTERM osxwin osxdlg osxctrls\r
+\r
+# Non-SSH back ends (putty, puttytel, plink).\r
+NONSSH   = telnet raw rlogin ldisc pinger\r
+\r
+# SSH back end (putty, plink, pscp, psftp).\r
+SSH      = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf\r
+         + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd\r
+         + sshaes sshsh256 sshsh512 sshbn wildcard pinger ssharcf\r
+         + sshgssc pgssapi\r
+WINSSH   = SSH winnoise winpgntc wingss\r
+UXSSH    = SSH uxnoise uxagentc uxgss\r
+\r
+# SFTP implementation (pscp, psftp).\r
+SFTP     = sftp int64 logging\r
+\r
+# Miscellaneous objects appearing in all the network utilities (not\r
+# Pageant or PuTTYgen).\r
+MISC     = timing misc version settings tree234 proxy\r
+WINMISC  = MISC winstore winnet winhandl cmdline windefs winmisc winproxy\r
+         + wintime\r
+UXMISC   = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time\r
+OSXMISC  = MISC uxstore uxsel osxsel uxnet uxmisc uxproxy time\r
+\r
+# Character set library, for use in pterm.\r
+CHARSET  = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc\r
+\r
+# Standard libraries.\r
+LIBS     = advapi32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib\r
+         + shell32.lib winmm.lib imm32.lib winspool.lib ole32.lib\r
+\r
+# Network backend sets. This also brings in the relevant attachment\r
+# to proxy.c depending on whether we're crypto-avoidant or not.\r
+BE_ALL   = be_all cproxy\r
+BE_NOSSH = be_nossh nocproxy\r
+BE_SSH   = be_none cproxy\r
+BE_NONE  = be_none nocproxy\r
+# More backend sets, with the additional Windows serial-port module.\r
+W_BE_ALL = be_all_s winser cproxy\r
+W_BE_NOSSH = be_nos_s winser nocproxy\r
+# And with the Unix serial-port module.\r
+U_BE_ALL = be_all_s uxser cproxy\r
+U_BE_NOSSH = be_nos_s uxser nocproxy\r
+\r
+# ------------------------------------------------------------\r
+# Definitions of actual programs. The program name, followed by a\r
+# colon, followed by a list of objects. Also in the list may be the\r
+# keywords [G] for Windows GUI app, [C] for Console app, [X] for\r
+# X/GTK Unix app, [U] for command-line Unix app.\r
+\r
+putty    : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS\r
+puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS\r
+plink    : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC\r
+         + winx11 plink.res winnojmp LIBS\r
+pscp     : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC\r
+         + pscp.res winnojmp LIBS\r
+psftp    : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC\r
+         + psftp.res winnojmp LIBS\r
+\r
+pageant  : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234\r
+         + misc sshaes sshsha winpgntc sshdss sshsh256 sshsh512 winutils\r
+         + winmisc winhelp pageant.res LIBS\r
+\r
+puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version\r
+         + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc\r
+         + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res\r
+        + tree234 notiming winhelp winnojmp LIBS wintime\r
+\r
+pterm    : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore\r
+         + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg\r
+        + nogss\r
+putty    : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore\r
+         + uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 xpmputty\r
+         + xpmpucfg\r
+puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH\r
+        + uxstore uxsignal CHARSET uxputty NONSSH UXMISC xpmputty xpmpucfg\r
+        + nogss\r
+\r
+plink    : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal\r
+         + ux_x11\r
+\r
+puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version\r
+         + sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc\r
+         + sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234\r
+        + uxgen notiming\r
+\r
+pscp     : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC\r
+psftp    : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC\r
+\r
+PuTTY    : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH\r
+         + ux_x11 uxpty uxsignal testback putty.icns info.plist\r
diff --git a/putty/RESOURCE.H b/putty/RESOURCE.H
new file mode 100644 (file)
index 0000000..188cc33
--- /dev/null
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}\r
+// Microsoft Developer Studio generated include file.\r
+// Used by win_res.rc\r
+//\r
+\r
+// Next default values for new objects\r
+// \r
+#ifdef APSTUDIO_INVOKED\r
+#ifndef APSTUDIO_READONLY_SYMBOLS\r
+#define _APS_NEXT_RESOURCE_VALUE        101\r
+#define _APS_NEXT_COMMAND_VALUE         40001\r
+#define _APS_NEXT_CONTROL_VALUE         1000\r
+#define _APS_NEXT_SYMED_VALUE           101\r
+#endif\r
+#endif\r
diff --git a/putty/RLOGIN.C b/putty/RLOGIN.C
new file mode 100644 (file)
index 0000000..0abf8cd
--- /dev/null
@@ -0,0 +1,416 @@
+/*\r
+ * Rlogin backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+#define RLOGIN_MAX_BACKLOG 4096\r
+\r
+typedef struct rlogin_tag {\r
+    const struct plug_function_table *fn;\r
+    /* the above field _must_ be first in the structure */\r
+\r
+    Socket s;\r
+    int bufsize;\r
+    int firstbyte;\r
+    int cansize;\r
+    int term_width, term_height;\r
+    void *frontend;\r
+\r
+    Config cfg;\r
+\r
+    /* In case we need to read a username from the terminal before starting */\r
+    prompts_t *prompt;\r
+} *Rlogin;\r
+\r
+static void rlogin_size(void *handle, int width, int height);\r
+\r
+static void c_write(Rlogin rlogin, char *buf, int len)\r
+{\r
+    int backlog = from_backend(rlogin->frontend, 0, buf, len);\r
+    sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);\r
+}\r
+\r
+static void rlogin_log(Plug plug, int type, SockAddr addr, int port,\r
+                      const char *error_msg, int error_code)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    char addrbuf[256], *msg;\r
+\r
+    sk_getaddr(addr, addrbuf, lenof(addrbuf));\r
+\r
+    if (type == 0)\r
+       msg = dupprintf("Connecting to %s port %d", addrbuf, port);\r
+    else\r
+       msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);\r
+\r
+    logevent(rlogin->frontend, msg);\r
+}\r
+\r
+static int rlogin_closing(Plug plug, const char *error_msg, int error_code,\r
+                         int calling_back)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    if (rlogin->s) {\r
+        sk_close(rlogin->s);\r
+        rlogin->s = NULL;\r
+       notify_remote_exit(rlogin->frontend);\r
+    }\r
+    if (error_msg) {\r
+       /* A socket error has occurred. */\r
+       logevent(rlogin->frontend, error_msg);\r
+       connection_fatal(rlogin->frontend, "%s", error_msg);\r
+    }                                 /* Otherwise, the remote side closed the connection normally. */\r
+    return 0;\r
+}\r
+\r
+static int rlogin_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    if (urgent == 2) {\r
+       char c;\r
+\r
+       c = *data++;\r
+       len--;\r
+       if (c == '\x80') {\r
+           rlogin->cansize = 1;\r
+           rlogin_size(rlogin, rlogin->term_width, rlogin->term_height);\r
+        }\r
+       /*\r
+        * We should flush everything (aka Telnet SYNCH) if we see\r
+        * 0x02, and we should turn off and on _local_ flow control\r
+        * on 0x10 and 0x20 respectively. I'm not convinced it's\r
+        * worth it...\r
+        */\r
+    } else {\r
+       /*\r
+        * Main rlogin protocol. This is really simple: the first\r
+        * byte is expected to be NULL and is ignored, and the rest\r
+        * is printed.\r
+        */\r
+       if (rlogin->firstbyte) {\r
+           if (data[0] == '\0') {\r
+               data++;\r
+               len--;\r
+           }\r
+           rlogin->firstbyte = 0;\r
+       }\r
+       if (len > 0)\r
+            c_write(rlogin, data, len);\r
+    }\r
+    return 1;\r
+}\r
+\r
+static void rlogin_sent(Plug plug, int bufsize)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    rlogin->bufsize = bufsize;\r
+}\r
+\r
+static void rlogin_startup(Rlogin rlogin, const char *ruser)\r
+{\r
+    char z = 0;\r
+    char *p;\r
+    sk_write(rlogin->s, &z, 1);\r
+    sk_write(rlogin->s, rlogin->cfg.localusername,\r
+             strlen(rlogin->cfg.localusername));\r
+    sk_write(rlogin->s, &z, 1);\r
+    sk_write(rlogin->s, ruser,\r
+             strlen(ruser));\r
+    sk_write(rlogin->s, &z, 1);\r
+    sk_write(rlogin->s, rlogin->cfg.termtype,\r
+             strlen(rlogin->cfg.termtype));\r
+    sk_write(rlogin->s, "/", 1);\r
+    for (p = rlogin->cfg.termspeed; isdigit((unsigned char)*p); p++) continue;\r
+    sk_write(rlogin->s, rlogin->cfg.termspeed, p - rlogin->cfg.termspeed);\r
+    rlogin->bufsize = sk_write(rlogin->s, &z, 1);\r
+\r
+    rlogin->prompt = NULL;\r
+}\r
+\r
+/*\r
+ * Called to set up the rlogin connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *rlogin_init(void *frontend_handle, void **backend_handle,\r
+                              Config *cfg,\r
+                              char *host, int port, char **realhost,\r
+                              int nodelay, int keepalive)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       rlogin_log,\r
+       rlogin_closing,\r
+       rlogin_receive,\r
+       rlogin_sent\r
+    };\r
+    SockAddr addr;\r
+    const char *err;\r
+    Rlogin rlogin;\r
+    char ruser[sizeof(cfg->username)];\r
+\r
+    rlogin = snew(struct rlogin_tag);\r
+    rlogin->fn = &fn_table;\r
+    rlogin->s = NULL;\r
+    rlogin->frontend = frontend_handle;\r
+    rlogin->term_width = cfg->width;\r
+    rlogin->term_height = cfg->height;\r
+    rlogin->firstbyte = 1;\r
+    rlogin->cansize = 0;\r
+    rlogin->prompt = NULL;\r
+    rlogin->cfg = *cfg;                /* STRUCTURE COPY */\r
+    *backend_handle = rlogin;\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    {\r
+       char *buf;\r
+       buf = dupprintf("Looking up host \"%s\"%s", host,\r
+                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
+                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :\r
+                         "")));\r
+       logevent(rlogin->frontend, buf);\r
+       sfree(buf);\r
+    }\r
+    addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+\r
+    if (port < 0)\r
+       port = 513;                    /* default rlogin port */\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    rlogin->s = new_connection(addr, *realhost, port, 1, 0,\r
+                              nodelay, keepalive, (Plug) rlogin, cfg);\r
+    if ((err = sk_socket_error(rlogin->s)) != NULL)\r
+       return err;\r
+\r
+    if (*cfg->loghost) {\r
+       char *colon;\r
+\r
+       sfree(*realhost);\r
+       *realhost = dupstr(cfg->loghost);\r
+       colon = strrchr(*realhost, ':');\r
+       if (colon) {\r
+           /*\r
+            * FIXME: if we ever update this aspect of ssh.c for\r
+            * IPv6 literal management, this should change in line\r
+            * with it.\r
+            */\r
+           *colon++ = '\0';\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Send local username, remote username, terminal type and\r
+     * terminal speed - unless we don't have the remote username yet,\r
+     * in which case we prompt for it and may end up deferring doing\r
+     * anything else until the local prompt mechanism returns.\r
+     */\r
+    if (get_remote_username(cfg, ruser, sizeof(ruser))) {\r
+        rlogin_startup(rlogin, ruser);\r
+    } else {\r
+        int ret;\r
+\r
+        rlogin->prompt = new_prompts(rlogin->frontend);\r
+        rlogin->prompt->to_server = TRUE;\r
+        rlogin->prompt->name = dupstr("Rlogin login name");\r
+        add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE,\r
+                   sizeof(cfg->username)); \r
+        ret = get_userpass_input(rlogin->prompt, NULL, 0);\r
+        if (ret >= 0) {\r
+            rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);\r
+        }\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+static void rlogin_free(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+\r
+    if (rlogin->prompt)\r
+        free_prompts(rlogin->prompt);\r
+    if (rlogin->s)\r
+       sk_close(rlogin->s);\r
+    sfree(rlogin);\r
+}\r
+\r
+/*\r
+ * Stub routine (we don't have any need to reconfigure this backend).\r
+ */\r
+static void rlogin_reconfig(void *handle, Config *cfg)\r
+{\r
+}\r
+\r
+/*\r
+ * Called to send data down the rlogin connection.\r
+ */\r
+static int rlogin_send(void *handle, char *buf, int len)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+\r
+    if (rlogin->s == NULL)\r
+       return 0;\r
+\r
+    if (rlogin->prompt) {\r
+        /*\r
+         * We're still prompting for a username, and aren't talking\r
+         * directly to the network connection yet.\r
+         */\r
+        int ret = get_userpass_input(rlogin->prompt,\r
+                                     (unsigned char *)buf, len);\r
+        if (ret >= 0) {\r
+            rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);\r
+            /* that nulls out rlogin->prompt, so then we'll start sending\r
+             * data down the wire in the obvious way */\r
+        }\r
+    } else {\r
+        rlogin->bufsize = sk_write(rlogin->s, buf, len);\r
+    }\r
+\r
+    return rlogin->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to query the current socket sendability status.\r
+ */\r
+static int rlogin_sendbuffer(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    return rlogin->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void rlogin_size(void *handle, int width, int height)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };\r
+\r
+    rlogin->term_width = width;\r
+    rlogin->term_height = height;\r
+\r
+    if (rlogin->s == NULL || !rlogin->cansize)\r
+       return;\r
+\r
+    b[6] = rlogin->term_width >> 8;\r
+    b[7] = rlogin->term_width & 0xFF;\r
+    b[4] = rlogin->term_height >> 8;\r
+    b[5] = rlogin->term_height & 0xFF;\r
+    rlogin->bufsize = sk_write(rlogin->s, b, 12);\r
+    return;\r
+}\r
+\r
+/*\r
+ * Send rlogin special codes.\r
+ */\r
+static void rlogin_special(void *handle, Telnet_Special code)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *rlogin_get_specials(void *handle)\r
+{\r
+    return NULL;\r
+}\r
+\r
+static int rlogin_connected(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    return rlogin->s != NULL;\r
+}\r
+\r
+static int rlogin_sendok(void *handle)\r
+{\r
+    /* Rlogin rlogin = (Rlogin) handle; */\r
+    return 1;\r
+}\r
+\r
+static void rlogin_unthrottle(void *handle, int backlog)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);\r
+}\r
+\r
+static int rlogin_ldisc(void *handle, int option)\r
+{\r
+    /* Rlogin rlogin = (Rlogin) handle; */\r
+    return 0;\r
+}\r
+\r
+static void rlogin_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static void rlogin_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static int rlogin_exitcode(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    if (rlogin->s != NULL)\r
+        return -1;                     /* still connected */\r
+    else\r
+        /* If we ever implement RSH, we'll probably need to do this properly */\r
+        return 0;\r
+}\r
+\r
+/*\r
+ * cfg_info for rlogin does nothing at all.\r
+ */\r
+static int rlogin_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+Backend rlogin_backend = {\r
+    rlogin_init,\r
+    rlogin_free,\r
+    rlogin_reconfig,\r
+    rlogin_send,\r
+    rlogin_sendbuffer,\r
+    rlogin_size,\r
+    rlogin_special,\r
+    rlogin_get_specials,\r
+    rlogin_connected,\r
+    rlogin_exitcode,\r
+    rlogin_sendok,\r
+    rlogin_ldisc,\r
+    rlogin_provide_ldisc,\r
+    rlogin_provide_logctx,\r
+    rlogin_unthrottle,\r
+    rlogin_cfg_info,\r
+    "rlogin",\r
+    PROT_RLOGIN,\r
+    513\r
+};\r
diff --git a/putty/Release/PuTTY.dll b/putty/Release/PuTTY.dll
new file mode 100644 (file)
index 0000000..9e8b5bb
Binary files /dev/null and b/putty/Release/PuTTY.dll differ
diff --git a/putty/SERCFG.C b/putty/SERCFG.C
new file mode 100644 (file)
index 0000000..86a8a0b
--- /dev/null
@@ -0,0 +1,199 @@
+/*\r
+ * sercfg.c - the serial-port specific parts of the PuTTY\r
+ * configuration box. Centralised as cross-platform code because\r
+ * more than one platform will want to use it, but not part of the\r
+ * main configuration. The expectation is that each platform's\r
+ * local config function will call out to ser_setup_config_box() if\r
+ * it needs to set up the standard serial stuff. (Of course, it can\r
+ * then apply local tweaks after ser_setup_config_box() returns, if\r
+ * it needs to.)\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "storage.h"\r
+\r
+static void serial_parity_handler(union control *ctrl, void *dlg,\r
+                                 void *data, int event)\r
+{\r
+    static const struct {\r
+       const char *name;\r
+       int val;\r
+    } parities[] = {\r
+       {"None", SER_PAR_NONE},\r
+       {"Odd", SER_PAR_ODD},\r
+       {"Even", SER_PAR_EVEN},\r
+       {"Mark", SER_PAR_MARK},\r
+       {"Space", SER_PAR_SPACE},\r
+    };\r
+    int mask = ctrl->listbox.context.i;\r
+    int i, j;\r
+    Config *cfg = (Config *)data;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       int oldparity = cfg->serparity;/* preserve past reentrant calls */\r
+       dlg_update_start(ctrl, dlg);\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       for (i = 0; i < lenof(parities); i++)  {\r
+           if (mask & (1 << i))\r
+               dlg_listbox_addwithid(ctrl, dlg, parities[i].name,\r
+                                     parities[i].val);\r
+       }\r
+       for (i = j = 0; i < lenof(parities); i++) {\r
+           if (mask & (1 << i)) {\r
+               if (oldparity == parities[i].val) {\r
+                   dlg_listbox_select(ctrl, dlg, j);\r
+                   break;\r
+               }\r
+               j++;\r
+           }\r
+       }\r
+       if (i == lenof(parities)) {    /* an unsupported setting was chosen */\r
+           dlg_listbox_select(ctrl, dlg, 0);\r
+           oldparity = SER_PAR_NONE;\r
+       }\r
+       dlg_update_done(ctrl, dlg);\r
+       cfg->serparity = oldparity;    /* restore */\r
+    } else if (event == EVENT_SELCHANGE) {\r
+       int i = dlg_listbox_index(ctrl, dlg);\r
+       if (i < 0)\r
+           i = SER_PAR_NONE;\r
+       else\r
+           i = dlg_listbox_getid(ctrl, dlg, i);\r
+       cfg->serparity = i;\r
+    }\r
+}\r
+\r
+static void serial_flow_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    static const struct {\r
+       const char *name;\r
+       int val;\r
+    } flows[] = {\r
+       {"None", SER_FLOW_NONE},\r
+       {"XON/XOFF", SER_FLOW_XONXOFF},\r
+       {"RTS/CTS", SER_FLOW_RTSCTS},\r
+       {"DSR/DTR", SER_FLOW_DSRDTR},\r
+    };\r
+    int mask = ctrl->listbox.context.i;\r
+    int i, j;\r
+    Config *cfg = (Config *)data;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       int oldflow = cfg->serflow;    /* preserve past reentrant calls */\r
+       dlg_update_start(ctrl, dlg);\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       for (i = 0; i < lenof(flows); i++)  {\r
+           if (mask & (1 << i))\r
+               dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val);\r
+       }\r
+       for (i = j = 0; i < lenof(flows); i++) {\r
+           if (mask & (1 << i)) {\r
+               if (oldflow == flows[i].val) {\r
+                   dlg_listbox_select(ctrl, dlg, j);\r
+                   break;\r
+               }\r
+               j++;\r
+           }\r
+       }\r
+       if (i == lenof(flows)) {       /* an unsupported setting was chosen */\r
+           dlg_listbox_select(ctrl, dlg, 0);\r
+           oldflow = SER_FLOW_NONE;\r
+       }\r
+       dlg_update_done(ctrl, dlg);\r
+       cfg->serflow = oldflow;        /* restore */\r
+    } else if (event == EVENT_SELCHANGE) {\r
+       int i = dlg_listbox_index(ctrl, dlg);\r
+       if (i < 0)\r
+           i = SER_FLOW_NONE;\r
+       else\r
+           i = dlg_listbox_getid(ctrl, dlg, i);\r
+       cfg->serflow = i;\r
+    }\r
+}\r
+\r
+void ser_setup_config_box(struct controlbox *b, int midsession,\r
+                         int parity_mask, int flow_mask)\r
+{\r
+    struct controlset *s;\r
+    union control *c;\r
+\r
+    if (!midsession) {\r
+       int i;\r
+       extern void config_protocolbuttons_handler(union control *, void *,\r
+                                                  void *, int);\r
+\r
+       /*\r
+        * Add the serial back end to the protocols list at the\r
+        * top of the config box.\r
+        */\r
+       s = ctrl_getset(b, "Session", "hostport",\r
+                       "Specify the destination you want to connect to");\r
+\r
+        for (i = 0; i < s->ncontrols; i++) {\r
+            c = s->ctrls[i];\r
+           if (c->generic.type == CTRL_RADIO &&\r
+               c->generic.handler == config_protocolbuttons_handler) {\r
+               c->radio.nbuttons++;\r
+               c->radio.ncolumns++;\r
+               c->radio.buttons =\r
+                   sresize(c->radio.buttons, c->radio.nbuttons, char *);\r
+               c->radio.buttons[c->radio.nbuttons-1] =\r
+                   dupstr("Serial");\r
+               c->radio.buttondata =\r
+                   sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);\r
+               c->radio.buttondata[c->radio.nbuttons-1] = I(PROT_SERIAL);\r
+               if (c->radio.shortcuts) {\r
+                   c->radio.shortcuts =\r
+                       sresize(c->radio.shortcuts, c->radio.nbuttons, char);\r
+                   c->radio.shortcuts[c->radio.nbuttons-1] = 'r';\r
+               }\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Entirely new Connection/Serial panel for serial port\r
+     * configuration.\r
+     */\r
+    ctrl_settitle(b, "Connection/Serial",\r
+                 "Options controlling local serial lines");\r
+\r
+    if (!midsession) {\r
+       /*\r
+        * We don't permit switching to a different serial port in\r
+        * midflight, although we do allow all other\r
+        * reconfiguration.\r
+        */\r
+       s = ctrl_getset(b, "Connection/Serial", "serline",\r
+                       "Select a serial line");\r
+       ctrl_editbox(s, "Serial line to connect to", 'l', 40,\r
+                    HELPCTX(serial_line),\r
+                    dlg_stdeditbox_handler, I(offsetof(Config,serline)),\r
+                    I(sizeof(((Config *)0)->serline)));\r
+    }\r
+\r
+    s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line");\r
+    ctrl_editbox(s, "Speed (baud)", 's', 40,\r
+                HELPCTX(serial_speed),\r
+                dlg_stdeditbox_handler, I(offsetof(Config,serspeed)), I(-1));\r
+    ctrl_editbox(s, "Data bits", 'b', 40,\r
+                HELPCTX(serial_databits),\r
+                dlg_stdeditbox_handler,I(offsetof(Config,serdatabits)),I(-1));\r
+    /*\r
+     * Stop bits come in units of one half.\r
+     */\r
+    ctrl_editbox(s, "Stop bits", 't', 40,\r
+                HELPCTX(serial_stopbits),\r
+                dlg_stdeditbox_handler,I(offsetof(Config,serstopbits)),I(-2));\r
+    ctrl_droplist(s, "Parity", 'p', 40,\r
+                 HELPCTX(serial_parity),\r
+                 serial_parity_handler, I(parity_mask));\r
+    ctrl_droplist(s, "Flow control", 'f', 40,\r
+                 HELPCTX(serial_flow),\r
+                 serial_flow_handler, I(flow_mask));\r
+}\r
diff --git a/putty/SETTINGS.C b/putty/SETTINGS.C
new file mode 100644 (file)
index 0000000..b2f3ddc
--- /dev/null
@@ -0,0 +1,1005 @@
+/*\r
+ * settings.c: read and write saved sessions. (platform-independent)\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include "putty.h"\r
+#include "storage.h"\r
+\r
+/* The cipher order given here is the default order. */\r
+static const struct keyvalwhere ciphernames[] = {\r
+    { "aes",        CIPHER_AES,             -1, -1 },\r
+    { "blowfish",   CIPHER_BLOWFISH,        -1, -1 },\r
+    { "3des",       CIPHER_3DES,            -1, -1 },\r
+    { "WARN",       CIPHER_WARN,            -1, -1 },\r
+    { "arcfour",    CIPHER_ARCFOUR,         -1, -1 },\r
+    { "des",        CIPHER_DES,             -1, -1 }\r
+};\r
+\r
+static const struct keyvalwhere kexnames[] = {\r
+    { "dh-gex-sha1",        KEX_DHGEX,      -1, -1 },\r
+    { "dh-group14-sha1",    KEX_DHGROUP14,  -1, -1 },\r
+    { "dh-group1-sha1",     KEX_DHGROUP1,   -1, -1 },\r
+    { "rsa",                KEX_RSA,        KEX_WARN, -1 },\r
+    { "WARN",               KEX_WARN,       -1, -1 }\r
+};\r
+\r
+/*\r
+ * All the terminal modes that we know about for the "TerminalModes"\r
+ * setting. (Also used by config.c for the drop-down list.)\r
+ * This is currently precisely the same as the set in ssh.c, but could\r
+ * in principle differ if other backends started to support tty modes\r
+ * (e.g., the pty backend).\r
+ */\r
+const char *const ttymodes[] = {\r
+    "INTR",    "QUIT",     "ERASE",    "KILL",     "EOF",\r
+    "EOL",     "EOL2",     "START",    "STOP",     "SUSP",\r
+    "DSUSP",   "REPRINT",  "WERASE",   "LNEXT",    "FLUSH",\r
+    "SWTCH",   "STATUS",   "DISCARD",  "IGNPAR",   "PARMRK",\r
+    "INPCK",   "ISTRIP",   "INLCR",    "IGNCR",    "ICRNL",\r
+    "IUCLC",   "IXON",     "IXANY",    "IXOFF",    "IMAXBEL",\r
+    "ISIG",    "ICANON",   "XCASE",    "ECHO",     "ECHOE",\r
+    "ECHOK",   "ECHONL",   "NOFLSH",   "TOSTOP",   "IEXTEN",\r
+    "ECHOCTL", "ECHOKE",   "PENDIN",   "OPOST",    "OLCUC",\r
+    "ONLCR",   "OCRNL",    "ONOCR",    "ONLRET",   "CS7",\r
+    "CS8",     "PARENB",   "PARODD",   NULL\r
+};\r
+\r
+/*\r
+ * Convenience functions to access the backends[] array\r
+ * (which is only present in tools that manage settings).\r
+ */\r
+\r
+Backend *backend_from_name(const char *name)\r
+{\r
+    Backend **p;\r
+    for (p = backends; *p != NULL; p++)\r
+       if (!strcmp((*p)->name, name))\r
+           return *p;\r
+    return NULL;\r
+}\r
+\r
+Backend *backend_from_proto(int proto)\r
+{\r
+    Backend **p;\r
+    for (p = backends; *p != NULL; p++)\r
+       if ((*p)->protocol == proto)\r
+           return *p;\r
+    return NULL;\r
+}\r
+\r
+int get_remote_username(Config *cfg, char *user, size_t len)\r
+{\r
+    if (*cfg->username) {\r
+       strncpy(user, cfg->username, len);\r
+       user[len-1] = '\0';\r
+    } else {\r
+       if (cfg->username_from_env) {\r
+           /* Use local username. */\r
+           char *luser = get_username();\r
+           if (luser) {\r
+               strncpy(user, luser, len);\r
+               user[len-1] = '\0';\r
+               sfree(luser);\r
+           } else {\r
+               *user = '\0';\r
+           }\r
+       } else {\r
+           *user = '\0';\r
+       }\r
+    }\r
+    return (*user != '\0');\r
+}\r
+\r
+static void gpps(void *handle, const char *name, const char *def,\r
+                char *val, int len)\r
+{\r
+    if (!read_setting_s(handle, name, val, len)) {\r
+       char *pdef;\r
+\r
+       pdef = platform_default_s(name);\r
+       if (pdef) {\r
+           strncpy(val, pdef, len);\r
+           sfree(pdef);\r
+       } else {\r
+           strncpy(val, def, len);\r
+       }\r
+\r
+       val[len - 1] = '\0';\r
+    }\r
+}\r
+\r
+/*\r
+ * gppfont and gppfile cannot have local defaults, since the very\r
+ * format of a Filename or Font is platform-dependent. So the\r
+ * platform-dependent functions MUST return some sort of value.\r
+ */\r
+static void gppfont(void *handle, const char *name, FontSpec *result)\r
+{\r
+    if (!read_setting_fontspec(handle, name, result))\r
+       *result = platform_default_fontspec(name);\r
+}\r
+static void gppfile(void *handle, const char *name, Filename *result)\r
+{\r
+    if (!read_setting_filename(handle, name, result))\r
+       *result = platform_default_filename(name);\r
+}\r
+\r
+static void gppi(void *handle, char *name, int def, int *i)\r
+{\r
+    def = platform_default_i(name, def);\r
+    *i = read_setting_i(handle, name, def);\r
+}\r
+\r
+/*\r
+ * Read a set of name-value pairs in the format we occasionally use:\r
+ *   NAME\tVALUE\0NAME\tVALUE\0\0 in memory\r
+ *   NAME=VALUE,NAME=VALUE, in storage\r
+ * `def' is in the storage format.\r
+ */\r
+static void gppmap(void *handle, char *name, char *def, char *val, int len)\r
+{\r
+    char *buf = snewn(2*len, char), *p, *q;\r
+    gpps(handle, name, def, buf, 2*len);\r
+    p = buf;\r
+    q = val;\r
+    while (*p) {\r
+       while (*p && *p != ',') {\r
+           int c = *p++;\r
+           if (c == '=')\r
+               c = '\t';\r
+           if (c == '\\')\r
+               c = *p++;\r
+           *q++ = c;\r
+       }\r
+       if (*p == ',')\r
+           p++;\r
+       *q++ = '\0';\r
+    }\r
+    *q = '\0';\r
+    sfree(buf);\r
+}\r
+\r
+/*\r
+ * Write a set of name/value pairs in the above format.\r
+ */\r
+static void wmap(void *handle, char const *key, char const *value, int len)\r
+{\r
+    char *buf = snewn(2*len, char), *p;\r
+    const char *q;\r
+    p = buf;\r
+    q = value;\r
+    while (*q) {\r
+       while (*q) {\r
+           int c = *q++;\r
+           if (c == '=' || c == ',' || c == '\\')\r
+               *p++ = '\\';\r
+           if (c == '\t')\r
+               c = '=';\r
+           *p++ = c;\r
+       }\r
+       *p++ = ',';\r
+       q++;\r
+    }\r
+    *p = '\0';\r
+    write_setting_s(handle, key, buf);\r
+    sfree(buf);\r
+}\r
+\r
+static int key2val(const struct keyvalwhere *mapping,\r
+                   int nmaps, char *key)\r
+{\r
+    int i;\r
+    for (i = 0; i < nmaps; i++)\r
+       if (!strcmp(mapping[i].s, key)) return mapping[i].v;\r
+    return -1;\r
+}\r
+\r
+static const char *val2key(const struct keyvalwhere *mapping,\r
+                           int nmaps, int val)\r
+{\r
+    int i;\r
+    for (i = 0; i < nmaps; i++)\r
+       if (mapping[i].v == val) return mapping[i].s;\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Helper function to parse a comma-separated list of strings into\r
+ * a preference list array of values. Any missing values are added\r
+ * to the end and duplicates are weeded.\r
+ * XXX: assumes vals in 'mapping' are small +ve integers\r
+ */\r
+static void gprefs(void *sesskey, char *name, char *def,\r
+                  const struct keyvalwhere *mapping, int nvals,\r
+                  int *array)\r
+{\r
+    char commalist[256];\r
+    char *p, *q;\r
+    int i, j, n, v, pos;\r
+    unsigned long seen = 0;           /* bitmap for weeding dups etc */\r
+\r
+    /*\r
+     * Fetch the string which we'll parse as a comma-separated list.\r
+     */\r
+    gpps(sesskey, name, def, commalist, sizeof(commalist));\r
+\r
+    /*\r
+     * Go through that list and convert it into values.\r
+     */\r
+    n = 0;\r
+    p = commalist;\r
+    while (1) {\r
+        while (*p && *p == ',') p++;\r
+        if (!*p)\r
+            break;                     /* no more words */\r
+\r
+        q = p;\r
+        while (*p && *p != ',') p++;\r
+        if (*p) *p++ = '\0';\r
+\r
+        v = key2val(mapping, nvals, q);\r
+        if (v != -1 && !(seen & (1 << v))) {\r
+           seen |= (1 << v);\r
+           array[n++] = v;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Now go through 'mapping' and add values that weren't mentioned\r
+     * in the list we fetched. We may have to loop over it multiple\r
+     * times so that we add values before other values whose default\r
+     * positions depend on them.\r
+     */\r
+    while (n < nvals) {\r
+        for (i = 0; i < nvals; i++) {\r
+           assert(mapping[i].v < 32);\r
+\r
+           if (!(seen & (1 << mapping[i].v))) {\r
+                /*\r
+                 * This element needs adding. But can we add it yet?\r
+                 */\r
+                if (mapping[i].vrel != -1 && !(seen & (1 << mapping[i].vrel)))\r
+                    continue;          /* nope */\r
+\r
+                /*\r
+                 * OK, we can work out where to add this element, so\r
+                 * do so.\r
+                 */\r
+                if (mapping[i].vrel == -1) {\r
+                    pos = (mapping[i].where < 0 ? n : 0);\r
+                } else {\r
+                    for (j = 0; j < n; j++)\r
+                        if (array[j] == mapping[i].vrel)\r
+                            break;\r
+                    assert(j < n);     /* implied by (seen & (1<<vrel)) */\r
+                    pos = (mapping[i].where < 0 ? j : j+1);\r
+                }\r
+\r
+                /*\r
+                 * And add it.\r
+                 */\r
+                for (j = n-1; j >= pos; j--)\r
+                    array[j+1] = array[j];\r
+                array[pos] = mapping[i].v;\r
+                n++;\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+/* \r
+ * Write out a preference list.\r
+ */\r
+static void wprefs(void *sesskey, char *name,\r
+                  const struct keyvalwhere *mapping, int nvals,\r
+                  int *array)\r
+{\r
+    char *buf, *p;\r
+    int i, maxlen;\r
+\r
+    for (maxlen = i = 0; i < nvals; i++) {\r
+       const char *s = val2key(mapping, nvals, array[i]);\r
+       if (s) {\r
+            maxlen += 1 + strlen(s);\r
+        }\r
+    }\r
+\r
+    buf = snewn(maxlen, char);\r
+    p = buf;\r
+\r
+    for (i = 0; i < nvals; i++) {\r
+       const char *s = val2key(mapping, nvals, array[i]);\r
+       if (s) {\r
+            p += sprintf(p, "%s%s", (p > buf ? "," : ""), s);\r
+       }\r
+    }\r
+\r
+    assert(p - buf == maxlen - 1);     /* maxlen counted the NUL */\r
+\r
+    write_setting_s(sesskey, name, buf);\r
+\r
+    sfree(buf);\r
+}\r
+\r
+char *save_settings(char *section, Config * cfg)\r
+{\r
+    void *sesskey;\r
+    char *errmsg;\r
+\r
+    sesskey = open_settings_w(section, &errmsg);\r
+    if (!sesskey)\r
+       return errmsg;\r
+    save_open_settings(sesskey, cfg);\r
+    close_settings_w(sesskey);\r
+    return NULL;\r
+}\r
+\r
+void save_open_settings(void *sesskey, Config *cfg)\r
+{\r
+    int i;\r
+    char *p;\r
+\r
+    write_setting_i(sesskey, "Present", 1);\r
+    write_setting_s(sesskey, "HostName", cfg->host);\r
+    write_setting_filename(sesskey, "LogFileName", cfg->logfilename);\r
+    write_setting_i(sesskey, "LogType", cfg->logtype);\r
+    write_setting_i(sesskey, "LogFileClash", cfg->logxfovr);\r
+    write_setting_i(sesskey, "LogFlush", cfg->logflush);\r
+    write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass);\r
+    write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata);\r
+    p = "raw";\r
+    {\r
+       const Backend *b = backend_from_proto(cfg->protocol);\r
+       if (b)\r
+           p = b->name;\r
+    }\r
+    write_setting_s(sesskey, "Protocol", p);\r
+    write_setting_i(sesskey, "PortNumber", cfg->port);\r
+    /* The CloseOnExit numbers are arranged in a different order from\r
+     * the standard FORCE_ON / FORCE_OFF / AUTO. */\r
+    write_setting_i(sesskey, "CloseOnExit", (cfg->close_on_exit+2)%3);\r
+    write_setting_i(sesskey, "WarnOnClose", !!cfg->warn_on_close);\r
+    write_setting_i(sesskey, "PingInterval", cfg->ping_interval / 60); /* minutes */\r
+    write_setting_i(sesskey, "PingIntervalSecs", cfg->ping_interval % 60);     /* seconds */\r
+    write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay);\r
+    write_setting_i(sesskey, "TCPKeepalives", cfg->tcp_keepalives);\r
+    write_setting_s(sesskey, "TerminalType", cfg->termtype);\r
+    write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed);\r
+    wmap(sesskey, "TerminalModes", cfg->ttymodes, lenof(cfg->ttymodes));\r
+\r
+    /* Address family selection */\r
+    write_setting_i(sesskey, "AddressFamily", cfg->addressfamily);\r
+\r
+    /* proxy settings */\r
+    write_setting_s(sesskey, "ProxyExcludeList", cfg->proxy_exclude_list);\r
+    write_setting_i(sesskey, "ProxyDNS", (cfg->proxy_dns+2)%3);\r
+    write_setting_i(sesskey, "ProxyLocalhost", cfg->even_proxy_localhost);\r
+    write_setting_i(sesskey, "ProxyMethod", cfg->proxy_type);\r
+    write_setting_s(sesskey, "ProxyHost", cfg->proxy_host);\r
+    write_setting_i(sesskey, "ProxyPort", cfg->proxy_port);\r
+    write_setting_s(sesskey, "ProxyUsername", cfg->proxy_username);\r
+    write_setting_s(sesskey, "ProxyPassword", cfg->proxy_password);\r
+    write_setting_s(sesskey, "ProxyTelnetCommand", cfg->proxy_telnet_command);\r
+    wmap(sesskey, "Environment", cfg->environmt, lenof(cfg->environmt));\r
+    write_setting_s(sesskey, "UserName", cfg->username);\r
+    write_setting_i(sesskey, "UserNameFromEnvironment", cfg->username_from_env);\r
+    write_setting_s(sesskey, "LocalUserName", cfg->localusername);\r
+    write_setting_i(sesskey, "NoPTY", cfg->nopty);\r
+    write_setting_i(sesskey, "Compression", cfg->compression);\r
+    write_setting_i(sesskey, "TryAgent", cfg->tryagent);\r
+    write_setting_i(sesskey, "AgentFwd", cfg->agentfwd);\r
+    write_setting_i(sesskey, "GssapiFwd", cfg->gssapifwd);\r
+    write_setting_i(sesskey, "ChangeUsername", cfg->change_username);\r
+    wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX,\r
+          cfg->ssh_cipherlist);\r
+    wprefs(sesskey, "KEX", kexnames, KEX_MAX, cfg->ssh_kexlist);\r
+    write_setting_i(sesskey, "RekeyTime", cfg->ssh_rekey_time);\r
+    write_setting_s(sesskey, "RekeyBytes", cfg->ssh_rekey_data);\r
+    write_setting_i(sesskey, "SshNoAuth", cfg->ssh_no_userauth);\r
+    write_setting_i(sesskey, "SshBanner", cfg->ssh_show_banner);\r
+    write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth);\r
+    write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth);\r
+    write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth);\r
+#ifndef NO_GSSAPI\r
+    wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs,\r
+          cfg->ssh_gsslist);\r
+    write_setting_filename(sesskey, "GSSCustom", cfg->ssh_gss_custom);\r
+#endif\r
+    write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell);\r
+    write_setting_i(sesskey, "SshProt", cfg->sshprot);\r
+    write_setting_s(sesskey, "LogHost", cfg->loghost);\r
+    write_setting_i(sesskey, "SSH2DES", cfg->ssh2_des_cbc);\r
+    write_setting_filename(sesskey, "PublicKeyFile", cfg->keyfile);\r
+    write_setting_s(sesskey, "RemoteCommand", cfg->remote_cmd);\r
+    write_setting_i(sesskey, "RFCEnviron", cfg->rfc_environ);\r
+    write_setting_i(sesskey, "PassiveTelnet", cfg->passive_telnet);\r
+    write_setting_i(sesskey, "BackspaceIsDelete", cfg->bksp_is_delete);\r
+    write_setting_i(sesskey, "RXVTHomeEnd", cfg->rxvt_homeend);\r
+    write_setting_i(sesskey, "LinuxFunctionKeys", cfg->funky_type);\r
+    write_setting_i(sesskey, "NoApplicationKeys", cfg->no_applic_k);\r
+    write_setting_i(sesskey, "NoApplicationCursors", cfg->no_applic_c);\r
+    write_setting_i(sesskey, "NoMouseReporting", cfg->no_mouse_rep);\r
+    write_setting_i(sesskey, "NoRemoteResize", cfg->no_remote_resize);\r
+    write_setting_i(sesskey, "NoAltScreen", cfg->no_alt_screen);\r
+    write_setting_i(sesskey, "NoRemoteWinTitle", cfg->no_remote_wintitle);\r
+    write_setting_i(sesskey, "RemoteQTitleAction", cfg->remote_qtitle_action);\r
+    write_setting_i(sesskey, "NoDBackspace", cfg->no_dbackspace);\r
+    write_setting_i(sesskey, "NoRemoteCharset", cfg->no_remote_charset);\r
+    write_setting_i(sesskey, "ApplicationCursorKeys", cfg->app_cursor);\r
+    write_setting_i(sesskey, "ApplicationKeypad", cfg->app_keypad);\r
+    write_setting_i(sesskey, "NetHackKeypad", cfg->nethack_keypad);\r
+    write_setting_i(sesskey, "AltF4", cfg->alt_f4);\r
+    write_setting_i(sesskey, "AltSpace", cfg->alt_space);\r
+    write_setting_i(sesskey, "AltOnly", cfg->alt_only);\r
+    write_setting_i(sesskey, "ComposeKey", cfg->compose_key);\r
+    write_setting_i(sesskey, "CtrlAltKeys", cfg->ctrlaltkeys);\r
+    write_setting_i(sesskey, "TelnetKey", cfg->telnet_keyboard);\r
+    write_setting_i(sesskey, "TelnetRet", cfg->telnet_newline);\r
+    write_setting_i(sesskey, "LocalEcho", cfg->localecho);\r
+    write_setting_i(sesskey, "LocalEdit", cfg->localedit);\r
+    write_setting_s(sesskey, "Answerback", cfg->answerback);\r
+    write_setting_i(sesskey, "AlwaysOnTop", cfg->alwaysontop);\r
+    write_setting_i(sesskey, "FullScreenOnAltEnter", cfg->fullscreenonaltenter);\r
+    write_setting_i(sesskey, "HideMousePtr", cfg->hide_mouseptr);\r
+    write_setting_i(sesskey, "SunkenEdge", cfg->sunken_edge);\r
+    write_setting_i(sesskey, "WindowBorder", cfg->window_border);\r
+    write_setting_i(sesskey, "CurType", cfg->cursor_type);\r
+    write_setting_i(sesskey, "BlinkCur", cfg->blink_cur);\r
+    write_setting_i(sesskey, "Beep", cfg->beep);\r
+    write_setting_i(sesskey, "BeepInd", cfg->beep_ind);\r
+    write_setting_filename(sesskey, "BellWaveFile", cfg->bell_wavefile);\r
+    write_setting_i(sesskey, "BellOverload", cfg->bellovl);\r
+    write_setting_i(sesskey, "BellOverloadN", cfg->bellovl_n);\r
+    write_setting_i(sesskey, "BellOverloadT", cfg->bellovl_t\r
+#ifdef PUTTY_UNIX_H\r
+                   * 1000\r
+#endif\r
+                   );\r
+    write_setting_i(sesskey, "BellOverloadS", cfg->bellovl_s\r
+#ifdef PUTTY_UNIX_H\r
+                   * 1000\r
+#endif\r
+                   );\r
+    write_setting_i(sesskey, "ScrollbackLines", cfg->savelines);\r
+    write_setting_i(sesskey, "DECOriginMode", cfg->dec_om);\r
+    write_setting_i(sesskey, "AutoWrapMode", cfg->wrap_mode);\r
+    write_setting_i(sesskey, "LFImpliesCR", cfg->lfhascr);\r
+    write_setting_i(sesskey, "CRImpliesLF", cfg->crhaslf);\r
+    write_setting_i(sesskey, "DisableArabicShaping", cfg->arabicshaping);\r
+    write_setting_i(sesskey, "DisableBidi", cfg->bidi);\r
+    write_setting_i(sesskey, "WinNameAlways", cfg->win_name_always);\r
+    write_setting_s(sesskey, "WinTitle", cfg->wintitle);\r
+    write_setting_i(sesskey, "TermWidth", cfg->width);\r
+    write_setting_i(sesskey, "TermHeight", cfg->height);\r
+    write_setting_fontspec(sesskey, "Font", cfg->font);\r
+    write_setting_i(sesskey, "FontQuality", cfg->font_quality);\r
+    write_setting_i(sesskey, "FontVTMode", cfg->vtmode);\r
+    write_setting_i(sesskey, "UseSystemColours", cfg->system_colour);\r
+    write_setting_i(sesskey, "TryPalette", cfg->try_palette);\r
+    write_setting_i(sesskey, "ANSIColour", cfg->ansi_colour);\r
+    write_setting_i(sesskey, "Xterm256Colour", cfg->xterm_256_colour);\r
+    write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour);\r
+\r
+    for (i = 0; i < 22; i++) {\r
+       char buf[20], buf2[30];\r
+       sprintf(buf, "Colour%d", i);\r
+       sprintf(buf2, "%d,%d,%d", cfg->colours[i][0],\r
+               cfg->colours[i][1], cfg->colours[i][2]);\r
+       write_setting_s(sesskey, buf, buf2);\r
+    }\r
+    write_setting_i(sesskey, "RawCNP", cfg->rawcnp);\r
+    write_setting_i(sesskey, "PasteRTF", cfg->rtf_paste);\r
+    write_setting_i(sesskey, "MouseIsXterm", cfg->mouse_is_xterm);\r
+    write_setting_i(sesskey, "RectSelect", cfg->rect_select);\r
+    write_setting_i(sesskey, "MouseOverride", cfg->mouse_override);\r
+    for (i = 0; i < 256; i += 32) {\r
+       char buf[20], buf2[256];\r
+       int j;\r
+       sprintf(buf, "Wordness%d", i);\r
+       *buf2 = '\0';\r
+       for (j = i; j < i + 32; j++) {\r
+           sprintf(buf2 + strlen(buf2), "%s%d",\r
+                   (*buf2 ? "," : ""), cfg->wordness[j]);\r
+       }\r
+       write_setting_s(sesskey, buf, buf2);\r
+    }\r
+    write_setting_s(sesskey, "LineCodePage", cfg->line_codepage);\r
+    write_setting_i(sesskey, "CJKAmbigWide", cfg->cjk_ambig_wide);\r
+    write_setting_i(sesskey, "UTF8Override", cfg->utf8_override);\r
+    write_setting_s(sesskey, "Printer", cfg->printer);\r
+    write_setting_i(sesskey, "CapsLockCyr", cfg->xlat_capslockcyr);\r
+    write_setting_i(sesskey, "ScrollBar", cfg->scrollbar);\r
+    write_setting_i(sesskey, "ScrollBarFullScreen", cfg->scrollbar_in_fullscreen);\r
+    write_setting_i(sesskey, "ScrollOnKey", cfg->scroll_on_key);\r
+    write_setting_i(sesskey, "ScrollOnDisp", cfg->scroll_on_disp);\r
+    write_setting_i(sesskey, "EraseToScrollback", cfg->erase_to_scrollback);\r
+    write_setting_i(sesskey, "LockSize", cfg->resize_action);\r
+    write_setting_i(sesskey, "BCE", cfg->bce);\r
+    write_setting_i(sesskey, "BlinkText", cfg->blinktext);\r
+    write_setting_i(sesskey, "X11Forward", cfg->x11_forward);\r
+    write_setting_s(sesskey, "X11Display", cfg->x11_display);\r
+    write_setting_i(sesskey, "X11AuthType", cfg->x11_auth);\r
+    write_setting_filename(sesskey, "X11AuthFile", cfg->xauthfile);\r
+    write_setting_i(sesskey, "LocalPortAcceptAll", cfg->lport_acceptall);\r
+    write_setting_i(sesskey, "RemotePortAcceptAll", cfg->rport_acceptall);\r
+    wmap(sesskey, "PortForwardings", cfg->portfwd, lenof(cfg->portfwd));\r
+    write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1);\r
+    write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1);\r
+    write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1);\r
+    write_setting_i(sesskey, "BugIgnore2", 2-cfg->sshbug_ignore2);\r
+    write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2);\r
+    write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2);\r
+    write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2);\r
+    write_setting_i(sesskey, "BugPKSessID2", 2-cfg->sshbug_pksessid2);\r
+    write_setting_i(sesskey, "BugRekey2", 2-cfg->sshbug_rekey2);\r
+    write_setting_i(sesskey, "BugMaxPkt2", 2-cfg->sshbug_maxpkt2);\r
+    write_setting_i(sesskey, "StampUtmp", cfg->stamp_utmp);\r
+    write_setting_i(sesskey, "LoginShell", cfg->login_shell);\r
+    write_setting_i(sesskey, "ScrollbarOnLeft", cfg->scrollbar_on_left);\r
+    write_setting_fontspec(sesskey, "BoldFont", cfg->boldfont);\r
+    write_setting_fontspec(sesskey, "WideFont", cfg->widefont);\r
+    write_setting_fontspec(sesskey, "WideBoldFont", cfg->wideboldfont);\r
+    write_setting_i(sesskey, "ShadowBold", cfg->shadowbold);\r
+    write_setting_i(sesskey, "ShadowBoldOffset", cfg->shadowboldoffset);\r
+    write_setting_s(sesskey, "SerialLine", cfg->serline);\r
+    write_setting_i(sesskey, "SerialSpeed", cfg->serspeed);\r
+    write_setting_i(sesskey, "SerialDataBits", cfg->serdatabits);\r
+    write_setting_i(sesskey, "SerialStopHalfbits", cfg->serstopbits);\r
+    write_setting_i(sesskey, "SerialParity", cfg->serparity);\r
+    write_setting_i(sesskey, "SerialFlowControl", cfg->serflow);\r
+    write_setting_s(sesskey, "WindowClass", cfg->winclass);\r
+}\r
+\r
+void load_settings(char *section, Config * cfg)\r
+{\r
+    void *sesskey;\r
+\r
+    sesskey = open_settings_r(section);\r
+    load_open_settings(sesskey, cfg);\r
+    close_settings_r(sesskey);\r
+\r
+    if (cfg_launchable(cfg))\r
+        add_session_to_jumplist(section);\r
+}\r
+\r
+void load_open_settings(void *sesskey, Config *cfg)\r
+{\r
+    int i;\r
+    char prot[10];\r
+\r
+    cfg->ssh_subsys = 0;              /* FIXME: load this properly */\r
+    cfg->remote_cmd_ptr = NULL;\r
+    cfg->remote_cmd_ptr2 = NULL;\r
+    cfg->ssh_nc_host[0] = '\0';\r
+\r
+    gpps(sesskey, "HostName", "", cfg->host, sizeof(cfg->host));\r
+    gppfile(sesskey, "LogFileName", &cfg->logfilename);\r
+    gppi(sesskey, "LogType", 0, &cfg->logtype);\r
+    gppi(sesskey, "LogFileClash", LGXF_ASK, &cfg->logxfovr);\r
+    gppi(sesskey, "LogFlush", 1, &cfg->logflush);\r
+    gppi(sesskey, "SSHLogOmitPasswords", 1, &cfg->logomitpass);\r
+    gppi(sesskey, "SSHLogOmitData", 0, &cfg->logomitdata);\r
+\r
+    gpps(sesskey, "Protocol", "default", prot, 10);\r
+    cfg->protocol = default_protocol;\r
+    cfg->port = default_port;\r
+    {\r
+       const Backend *b = backend_from_name(prot);\r
+       if (b) {\r
+           cfg->protocol = b->protocol;\r
+           gppi(sesskey, "PortNumber", default_port, &cfg->port);\r
+       }\r
+    }\r
+\r
+    /* Address family selection */\r
+    gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily);\r
+\r
+    /* The CloseOnExit numbers are arranged in a different order from\r
+     * the standard FORCE_ON / FORCE_OFF / AUTO. */\r
+    gppi(sesskey, "CloseOnExit", 1, &i); cfg->close_on_exit = (i+1)%3;\r
+    gppi(sesskey, "WarnOnClose", 1, &cfg->warn_on_close);\r
+    {\r
+       /* This is two values for backward compatibility with 0.50/0.51 */\r
+       int pingmin, pingsec;\r
+       gppi(sesskey, "PingInterval", 0, &pingmin);\r
+       gppi(sesskey, "PingIntervalSecs", 0, &pingsec);\r
+       cfg->ping_interval = pingmin * 60 + pingsec;\r
+    }\r
+    gppi(sesskey, "TCPNoDelay", 1, &cfg->tcp_nodelay);\r
+    gppi(sesskey, "TCPKeepalives", 0, &cfg->tcp_keepalives);\r
+    gpps(sesskey, "TerminalType", "xterm", cfg->termtype,\r
+        sizeof(cfg->termtype));\r
+    gpps(sesskey, "TerminalSpeed", "38400,38400", cfg->termspeed,\r
+        sizeof(cfg->termspeed));\r
+    {\r
+       /* This hardcodes a big set of defaults in any new saved\r
+        * sessions. Let's hope we don't change our mind. */\r
+       int i;\r
+       char *def = dupstr("");\r
+       /* Default: all set to "auto" */\r
+       for (i = 0; ttymodes[i]; i++) {\r
+           char *def2 = dupprintf("%s%s=A,", def, ttymodes[i]);\r
+           sfree(def);\r
+           def = def2;\r
+       }\r
+       gppmap(sesskey, "TerminalModes", def,\r
+              cfg->ttymodes, lenof(cfg->ttymodes));\r
+       sfree(def);\r
+    }\r
+\r
+    /* proxy settings */\r
+    gpps(sesskey, "ProxyExcludeList", "", cfg->proxy_exclude_list,\r
+        sizeof(cfg->proxy_exclude_list));\r
+    gppi(sesskey, "ProxyDNS", 1, &i); cfg->proxy_dns = (i+1)%3;\r
+    gppi(sesskey, "ProxyLocalhost", 0, &cfg->even_proxy_localhost);\r
+    gppi(sesskey, "ProxyMethod", -1, &cfg->proxy_type);\r
+    if (cfg->proxy_type == -1) {\r
+        int i;\r
+        gppi(sesskey, "ProxyType", 0, &i);\r
+        if (i == 0)\r
+            cfg->proxy_type = PROXY_NONE;\r
+        else if (i == 1)\r
+            cfg->proxy_type = PROXY_HTTP;\r
+        else if (i == 3)\r
+            cfg->proxy_type = PROXY_TELNET;\r
+        else if (i == 4)\r
+            cfg->proxy_type = PROXY_CMD;\r
+        else {\r
+            gppi(sesskey, "ProxySOCKSVersion", 5, &i);\r
+            if (i == 5)\r
+                cfg->proxy_type = PROXY_SOCKS5;\r
+            else\r
+                cfg->proxy_type = PROXY_SOCKS4;\r
+        }\r
+    }\r
+    gpps(sesskey, "ProxyHost", "proxy", cfg->proxy_host,\r
+        sizeof(cfg->proxy_host));\r
+    gppi(sesskey, "ProxyPort", 80, &cfg->proxy_port);\r
+    gpps(sesskey, "ProxyUsername", "", cfg->proxy_username,\r
+        sizeof(cfg->proxy_username));\r
+    gpps(sesskey, "ProxyPassword", "", cfg->proxy_password,\r
+        sizeof(cfg->proxy_password));\r
+    gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n",\r
+        cfg->proxy_telnet_command, sizeof(cfg->proxy_telnet_command));\r
+    gppmap(sesskey, "Environment", "", cfg->environmt, lenof(cfg->environmt));\r
+    gpps(sesskey, "UserName", "", cfg->username, sizeof(cfg->username));\r
+    gppi(sesskey, "UserNameFromEnvironment", 0, &cfg->username_from_env);\r
+    gpps(sesskey, "LocalUserName", "", cfg->localusername,\r
+        sizeof(cfg->localusername));\r
+    gppi(sesskey, "NoPTY", 0, &cfg->nopty);\r
+    gppi(sesskey, "Compression", 0, &cfg->compression);\r
+    gppi(sesskey, "TryAgent", 1, &cfg->tryagent);\r
+    gppi(sesskey, "AgentFwd", 0, &cfg->agentfwd);\r
+    gppi(sesskey, "ChangeUsername", 0, &cfg->change_username);\r
+    gppi(sesskey, "GssapiFwd", 0, &cfg->gssapifwd);\r
+    gprefs(sesskey, "Cipher", "\0",\r
+          ciphernames, CIPHER_MAX, cfg->ssh_cipherlist);\r
+    {\r
+       /* Backward-compatibility: we used to have an option to\r
+        * disable gex under the "bugs" panel after one report of\r
+        * a server which offered it then choked, but we never got\r
+        * a server version string or any other reports. */\r
+       char *default_kexes;\r
+       gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i;\r
+       if (i == FORCE_ON)\r
+           default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1";\r
+       else\r
+           default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN";\r
+       gprefs(sesskey, "KEX", default_kexes,\r
+              kexnames, KEX_MAX, cfg->ssh_kexlist);\r
+    }\r
+    gppi(sesskey, "RekeyTime", 60, &cfg->ssh_rekey_time);\r
+    gpps(sesskey, "RekeyBytes", "1G", cfg->ssh_rekey_data,\r
+        sizeof(cfg->ssh_rekey_data));\r
+    gppi(sesskey, "SshProt", 2, &cfg->sshprot);\r
+    gpps(sesskey, "LogHost", "", cfg->loghost, sizeof(cfg->loghost));\r
+    gppi(sesskey, "SSH2DES", 0, &cfg->ssh2_des_cbc);\r
+    gppi(sesskey, "SshNoAuth", 0, &cfg->ssh_no_userauth);\r
+    gppi(sesskey, "SshBanner", 1, &cfg->ssh_show_banner);\r
+    gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth);\r
+    gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth);\r
+    gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth);\r
+#ifndef NO_GSSAPI\r
+    gprefs(sesskey, "GSSLibs", "\0",\r
+          gsslibkeywords, ngsslibs, cfg->ssh_gsslist);\r
+    gppfile(sesskey, "GSSCustom", &cfg->ssh_gss_custom);\r
+#endif\r
+    gppi(sesskey, "SshNoShell", 0, &cfg->ssh_no_shell);\r
+    gppfile(sesskey, "PublicKeyFile", &cfg->keyfile);\r
+    gpps(sesskey, "RemoteCommand", "", cfg->remote_cmd,\r
+        sizeof(cfg->remote_cmd));\r
+    gppi(sesskey, "RFCEnviron", 0, &cfg->rfc_environ);\r
+    gppi(sesskey, "PassiveTelnet", 0, &cfg->passive_telnet);\r
+    gppi(sesskey, "BackspaceIsDelete", 1, &cfg->bksp_is_delete);\r
+    gppi(sesskey, "RXVTHomeEnd", 0, &cfg->rxvt_homeend);\r
+    gppi(sesskey, "LinuxFunctionKeys", 0, &cfg->funky_type);\r
+    gppi(sesskey, "NoApplicationKeys", 0, &cfg->no_applic_k);\r
+    gppi(sesskey, "NoApplicationCursors", 0, &cfg->no_applic_c);\r
+    gppi(sesskey, "NoMouseReporting", 0, &cfg->no_mouse_rep);\r
+    gppi(sesskey, "NoRemoteResize", 0, &cfg->no_remote_resize);\r
+    gppi(sesskey, "NoAltScreen", 0, &cfg->no_alt_screen);\r
+    gppi(sesskey, "NoRemoteWinTitle", 0, &cfg->no_remote_wintitle);\r
+    {\r
+       /* Backward compatibility */\r
+       int no_remote_qtitle;\r
+       gppi(sesskey, "NoRemoteQTitle", 1, &no_remote_qtitle);\r
+       /* We deliberately interpret the old setting of "no response" as\r
+        * "empty string". This changes the behaviour, but hopefully for\r
+        * the better; the user can always recover the old behaviour. */\r
+       gppi(sesskey, "RemoteQTitleAction",\r
+            no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL,\r
+            &cfg->remote_qtitle_action);\r
+    }\r
+    gppi(sesskey, "NoDBackspace", 0, &cfg->no_dbackspace);\r
+    gppi(sesskey, "NoRemoteCharset", 0, &cfg->no_remote_charset);\r
+    gppi(sesskey, "ApplicationCursorKeys", 0, &cfg->app_cursor);\r
+    gppi(sesskey, "ApplicationKeypad", 0, &cfg->app_keypad);\r
+    gppi(sesskey, "NetHackKeypad", 0, &cfg->nethack_keypad);\r
+    gppi(sesskey, "AltF4", 1, &cfg->alt_f4);\r
+    gppi(sesskey, "AltSpace", 0, &cfg->alt_space);\r
+    gppi(sesskey, "AltOnly", 0, &cfg->alt_only);\r
+    gppi(sesskey, "ComposeKey", 0, &cfg->compose_key);\r
+    gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys);\r
+    gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard);\r
+    gppi(sesskey, "TelnetRet", 1, &cfg->telnet_newline);\r
+    gppi(sesskey, "LocalEcho", AUTO, &cfg->localecho);\r
+    gppi(sesskey, "LocalEdit", AUTO, &cfg->localedit);\r
+    gpps(sesskey, "Answerback", "PuTTY", cfg->answerback,\r
+        sizeof(cfg->answerback));\r
+    gppi(sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop);\r
+    gppi(sesskey, "FullScreenOnAltEnter", 0, &cfg->fullscreenonaltenter);\r
+    gppi(sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr);\r
+    gppi(sesskey, "SunkenEdge", 0, &cfg->sunken_edge);\r
+    gppi(sesskey, "WindowBorder", 1, &cfg->window_border);\r
+    gppi(sesskey, "CurType", 0, &cfg->cursor_type);\r
+    gppi(sesskey, "BlinkCur", 0, &cfg->blink_cur);\r
+    /* pedantic compiler tells me I can't use &cfg->beep as an int * :-) */\r
+    gppi(sesskey, "Beep", 1, &cfg->beep);\r
+    gppi(sesskey, "BeepInd", 0, &cfg->beep_ind);\r
+    gppfile(sesskey, "BellWaveFile", &cfg->bell_wavefile);\r
+    gppi(sesskey, "BellOverload", 1, &cfg->bellovl);\r
+    gppi(sesskey, "BellOverloadN", 5, &cfg->bellovl_n);\r
+    gppi(sesskey, "BellOverloadT", 2*TICKSPERSEC\r
+#ifdef PUTTY_UNIX_H\r
+                                  *1000\r
+#endif\r
+                                  , &i);\r
+    cfg->bellovl_t = i\r
+#ifdef PUTTY_UNIX_H\r
+                   / 1000\r
+#endif\r
+       ;\r
+    gppi(sesskey, "BellOverloadS", 5*TICKSPERSEC\r
+#ifdef PUTTY_UNIX_H\r
+                                  *1000\r
+#endif\r
+                                  , &i);\r
+    cfg->bellovl_s = i\r
+#ifdef PUTTY_UNIX_H\r
+                   / 1000\r
+#endif\r
+       ;\r
+    gppi(sesskey, "ScrollbackLines", 200, &cfg->savelines);\r
+    gppi(sesskey, "DECOriginMode", 0, &cfg->dec_om);\r
+    gppi(sesskey, "AutoWrapMode", 1, &cfg->wrap_mode);\r
+    gppi(sesskey, "LFImpliesCR", 0, &cfg->lfhascr);\r
+    gppi(sesskey, "CRImpliesLF", 0, &cfg->crhaslf);\r
+    gppi(sesskey, "DisableArabicShaping", 0, &cfg->arabicshaping);\r
+    gppi(sesskey, "DisableBidi", 0, &cfg->bidi);\r
+    gppi(sesskey, "WinNameAlways", 1, &cfg->win_name_always);\r
+    gpps(sesskey, "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle));\r
+    gppi(sesskey, "TermWidth", 80, &cfg->width);\r
+    gppi(sesskey, "TermHeight", 24, &cfg->height);\r
+    gppfont(sesskey, "Font", &cfg->font);\r
+    gppi(sesskey, "FontQuality", FQ_DEFAULT, &cfg->font_quality);\r
+    gppi(sesskey, "FontVTMode", VT_UNICODE, (int *) &cfg->vtmode);\r
+    gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour);\r
+    gppi(sesskey, "TryPalette", 0, &cfg->try_palette);\r
+    gppi(sesskey, "ANSIColour", 1, &cfg->ansi_colour);\r
+    gppi(sesskey, "Xterm256Colour", 1, &cfg->xterm_256_colour);\r
+    gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour);\r
+\r
+    for (i = 0; i < 22; i++) {\r
+       static const char *const defaults[] = {\r
+           "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0",\r
+           "0,255,0", "0,0,0", "85,85,85", "187,0,0", "255,85,85",\r
+           "0,187,0", "85,255,85", "187,187,0", "255,255,85", "0,0,187",\r
+           "85,85,255", "187,0,187", "255,85,255", "0,187,187",\r
+           "85,255,255", "187,187,187", "255,255,255"\r
+       };\r
+       char buf[20], buf2[30];\r
+       int c0, c1, c2;\r
+       sprintf(buf, "Colour%d", i);\r
+       gpps(sesskey, buf, defaults[i], buf2, sizeof(buf2));\r
+       if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) {\r
+           cfg->colours[i][0] = c0;\r
+           cfg->colours[i][1] = c1;\r
+           cfg->colours[i][2] = c2;\r
+       }\r
+    }\r
+    gppi(sesskey, "RawCNP", 0, &cfg->rawcnp);\r
+    gppi(sesskey, "PasteRTF", 0, &cfg->rtf_paste);\r
+    gppi(sesskey, "MouseIsXterm", 0, &cfg->mouse_is_xterm);\r
+    gppi(sesskey, "RectSelect", 0, &cfg->rect_select);\r
+    gppi(sesskey, "MouseOverride", 1, &cfg->mouse_override);\r
+    for (i = 0; i < 256; i += 32) {\r
+       static const char *const defaults[] = {\r
+           "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",\r
+           "0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1",\r
+           "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2",\r
+           "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1",\r
+           "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",\r
+           "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",\r
+           "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2",\r
+           "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2"\r
+       };\r
+       char buf[20], buf2[256], *p;\r
+       int j;\r
+       sprintf(buf, "Wordness%d", i);\r
+       gpps(sesskey, buf, defaults[i / 32], buf2, sizeof(buf2));\r
+       p = buf2;\r
+       for (j = i; j < i + 32; j++) {\r
+           char *q = p;\r
+           while (*p && *p != ',')\r
+               p++;\r
+           if (*p == ',')\r
+               *p++ = '\0';\r
+           cfg->wordness[j] = atoi(q);\r
+       }\r
+    }\r
+    /*\r
+     * The empty default for LineCodePage will be converted later\r
+     * into a plausible default for the locale.\r
+     */\r
+    gpps(sesskey, "LineCodePage", "", cfg->line_codepage,\r
+        sizeof(cfg->line_codepage));\r
+    gppi(sesskey, "CJKAmbigWide", 0, &cfg->cjk_ambig_wide);\r
+    gppi(sesskey, "UTF8Override", 1, &cfg->utf8_override);\r
+    gpps(sesskey, "Printer", "", cfg->printer, sizeof(cfg->printer));\r
+    gppi (sesskey, "CapsLockCyr", 0, &cfg->xlat_capslockcyr);\r
+    gppi(sesskey, "ScrollBar", 1, &cfg->scrollbar);\r
+    gppi(sesskey, "ScrollBarFullScreen", 0, &cfg->scrollbar_in_fullscreen);\r
+    gppi(sesskey, "ScrollOnKey", 0, &cfg->scroll_on_key);\r
+    gppi(sesskey, "ScrollOnDisp", 1, &cfg->scroll_on_disp);\r
+    gppi(sesskey, "EraseToScrollback", 1, &cfg->erase_to_scrollback);\r
+    gppi(sesskey, "LockSize", 0, &cfg->resize_action);\r
+    gppi(sesskey, "BCE", 1, &cfg->bce);\r
+    gppi(sesskey, "BlinkText", 0, &cfg->blinktext);\r
+    gppi(sesskey, "X11Forward", 0, &cfg->x11_forward);\r
+    gpps(sesskey, "X11Display", "", cfg->x11_display,\r
+        sizeof(cfg->x11_display));\r
+    gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth);\r
+    gppfile(sesskey, "X11AuthFile", &cfg->xauthfile);\r
+\r
+    gppi(sesskey, "LocalPortAcceptAll", 0, &cfg->lport_acceptall);\r
+    gppi(sesskey, "RemotePortAcceptAll", 0, &cfg->rport_acceptall);\r
+    gppmap(sesskey, "PortForwardings", "", cfg->portfwd, lenof(cfg->portfwd));\r
+    gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i;\r
+    gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i;\r
+    gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i;\r
+    gppi(sesskey, "BugIgnore2", 0, &i); cfg->sshbug_ignore2 = 2-i;\r
+    {\r
+       int i;\r
+       gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;\r
+       if (cfg->sshbug_hmac2 == AUTO) {\r
+           gppi(sesskey, "BuggyMAC", 0, &i);\r
+           if (i == 1)\r
+               cfg->sshbug_hmac2 = FORCE_ON;\r
+       }\r
+    }\r
+    gppi(sesskey, "BugDeriveKey2", 0, &i); cfg->sshbug_derivekey2 = 2-i;\r
+    gppi(sesskey, "BugRSAPad2", 0, &i); cfg->sshbug_rsapad2 = 2-i;\r
+    gppi(sesskey, "BugPKSessID2", 0, &i); cfg->sshbug_pksessid2 = 2-i;\r
+    gppi(sesskey, "BugRekey2", 0, &i); cfg->sshbug_rekey2 = 2-i;\r
+    gppi(sesskey, "BugMaxPkt2", 0, &i); cfg->sshbug_maxpkt2 = 2-i;\r
+    cfg->ssh_simple = FALSE;\r
+    gppi(sesskey, "StampUtmp", 1, &cfg->stamp_utmp);\r
+    gppi(sesskey, "LoginShell", 1, &cfg->login_shell);\r
+    gppi(sesskey, "ScrollbarOnLeft", 0, &cfg->scrollbar_on_left);\r
+    gppi(sesskey, "ShadowBold", 0, &cfg->shadowbold);\r
+    gppfont(sesskey, "BoldFont", &cfg->boldfont);\r
+    gppfont(sesskey, "WideFont", &cfg->widefont);\r
+    gppfont(sesskey, "WideBoldFont", &cfg->wideboldfont);\r
+    gppi(sesskey, "ShadowBoldOffset", 1, &cfg->shadowboldoffset);\r
+    gpps(sesskey, "SerialLine", "", cfg->serline, sizeof(cfg->serline));\r
+    gppi(sesskey, "SerialSpeed", 9600, &cfg->serspeed);\r
+    gppi(sesskey, "SerialDataBits", 8, &cfg->serdatabits);\r
+    gppi(sesskey, "SerialStopHalfbits", 2, &cfg->serstopbits);\r
+    gppi(sesskey, "SerialParity", SER_PAR_NONE, &cfg->serparity);\r
+    gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, &cfg->serflow);\r
+    gpps(sesskey, "WindowClass", "", cfg->winclass, sizeof(cfg->winclass));\r
+}\r
+\r
+void do_defaults(char *session, Config * cfg)\r
+{\r
+    load_settings(session, cfg);\r
+}\r
+\r
+static int sessioncmp(const void *av, const void *bv)\r
+{\r
+    const char *a = *(const char *const *) av;\r
+    const char *b = *(const char *const *) bv;\r
+\r
+    /*\r
+     * Alphabetical order, except that "Default Settings" is a\r
+     * special case and comes first.\r
+     */\r
+    if (!strcmp(a, "Default Settings"))\r
+       return -1;                     /* a comes first */\r
+    if (!strcmp(b, "Default Settings"))\r
+       return +1;                     /* b comes first */\r
+    /*\r
+     * FIXME: perhaps we should ignore the first & in determining\r
+     * sort order.\r
+     */\r
+    return strcmp(a, b);              /* otherwise, compare normally */\r
+}\r
+\r
+void get_sesslist(struct sesslist *list, int allocate)\r
+{\r
+    char otherbuf[2048];\r
+    int buflen, bufsize, i;\r
+    char *p, *ret;\r
+    void *handle;\r
+\r
+    if (allocate) {\r
+\r
+       buflen = bufsize = 0;\r
+       list->buffer = NULL;\r
+       if ((handle = enum_settings_start()) != NULL) {\r
+           do {\r
+               ret = enum_settings_next(handle, otherbuf, sizeof(otherbuf));\r
+               if (ret) {\r
+                   int len = strlen(otherbuf) + 1;\r
+                   if (bufsize < buflen + len) {\r
+                       bufsize = buflen + len + 2048;\r
+                       list->buffer = sresize(list->buffer, bufsize, char);\r
+                   }\r
+                   strcpy(list->buffer + buflen, otherbuf);\r
+                   buflen += strlen(list->buffer + buflen) + 1;\r
+               }\r
+           } while (ret);\r
+           enum_settings_finish(handle);\r
+       }\r
+       list->buffer = sresize(list->buffer, buflen + 1, char);\r
+       list->buffer[buflen] = '\0';\r
+\r
+       /*\r
+        * Now set up the list of sessions. Note that "Default\r
+        * Settings" must always be claimed to exist, even if it\r
+        * doesn't really.\r
+        */\r
+\r
+       p = list->buffer;\r
+       list->nsessions = 1;           /* "Default Settings" counts as one */\r
+       while (*p) {\r
+           if (strcmp(p, "Default Settings"))\r
+               list->nsessions++;\r
+           while (*p)\r
+               p++;\r
+           p++;\r
+       }\r
+\r
+       list->sessions = snewn(list->nsessions + 1, char *);\r
+       list->sessions[0] = "Default Settings";\r
+       p = list->buffer;\r
+       i = 1;\r
+       while (*p) {\r
+           if (strcmp(p, "Default Settings"))\r
+               list->sessions[i++] = p;\r
+           while (*p)\r
+               p++;\r
+           p++;\r
+       }\r
+\r
+       qsort(list->sessions, i, sizeof(char *), sessioncmp);\r
+    } else {\r
+       sfree(list->buffer);\r
+       sfree(list->sessions);\r
+       list->buffer = NULL;\r
+       list->sessions = NULL;\r
+    }\r
+}\r
diff --git a/putty/SFTP.C b/putty/SFTP.C
new file mode 100644 (file)
index 0000000..e665dfb
--- /dev/null
@@ -0,0 +1,1422 @@
+/*\r
+ * sftp.c: SFTP generic client code.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <assert.h>\r
+#include <limits.h>\r
+\r
+#include "misc.h"\r
+#include "int64.h"\r
+#include "tree234.h"\r
+#include "sftp.h"\r
+\r
+struct sftp_packet {\r
+    char *data;\r
+    unsigned length, maxlen;\r
+    unsigned savedpos;\r
+    int type;\r
+};\r
+\r
+static const char *fxp_error_message;\r
+static int fxp_errtype;\r
+\r
+static void fxp_internal_error(char *msg);\r
+\r
+/* ----------------------------------------------------------------------\r
+ * SFTP packet construction functions.\r
+ */\r
+static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)\r
+{\r
+    if ((int)pkt->maxlen < length) {\r
+       pkt->maxlen = length + 256;\r
+       pkt->data = sresize(pkt->data, pkt->maxlen, char);\r
+    }\r
+}\r
+static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)\r
+{\r
+    pkt->length += len;\r
+    sftp_pkt_ensure(pkt, pkt->length);\r
+    memcpy(pkt->data + pkt->length - len, data, len);\r
+}\r
+static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)\r
+{\r
+    sftp_pkt_adddata(pkt, &byte, 1);\r
+}\r
+static struct sftp_packet *sftp_pkt_init(int pkt_type)\r
+{\r
+    struct sftp_packet *pkt;\r
+    pkt = snew(struct sftp_packet);\r
+    pkt->data = NULL;\r
+    pkt->savedpos = -1;\r
+    pkt->length = 0;\r
+    pkt->maxlen = 0;\r
+    sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);\r
+    return pkt;\r
+}\r
+/*\r
+static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)\r
+{\r
+    sftp_pkt_adddata(pkt, &value, 1);\r
+}\r
+*/\r
+static void sftp_pkt_adduint32(struct sftp_packet *pkt,\r
+                              unsigned long value)\r
+{\r
+    unsigned char x[4];\r
+    PUT_32BIT(x, value);\r
+    sftp_pkt_adddata(pkt, x, 4);\r
+}\r
+static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)\r
+{\r
+    unsigned char x[8];\r
+    PUT_32BIT(x, value.hi);\r
+    PUT_32BIT(x + 4, value.lo);\r
+    sftp_pkt_adddata(pkt, x, 8);\r
+}\r
+static void sftp_pkt_addstring_start(struct sftp_packet *pkt)\r
+{\r
+    sftp_pkt_adduint32(pkt, 0);\r
+    pkt->savedpos = pkt->length;\r
+}\r
+static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)\r
+{\r
+    sftp_pkt_adddata(pkt, data, strlen(data));\r
+    PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);\r
+}\r
+static void sftp_pkt_addstring_data(struct sftp_packet *pkt,\r
+                                   char *data, int len)\r
+{\r
+    sftp_pkt_adddata(pkt, data, len);\r
+    PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);\r
+}\r
+static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)\r
+{\r
+    sftp_pkt_addstring_start(pkt);\r
+    sftp_pkt_addstring_str(pkt, data);\r
+}\r
+static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)\r
+{\r
+    sftp_pkt_adduint32(pkt, attrs.flags);\r
+    if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {\r
+       sftp_pkt_adduint32(pkt, attrs.size.hi);\r
+       sftp_pkt_adduint32(pkt, attrs.size.lo);\r
+    }\r
+    if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {\r
+       sftp_pkt_adduint32(pkt, attrs.uid);\r
+       sftp_pkt_adduint32(pkt, attrs.gid);\r
+    }\r
+    if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {\r
+       sftp_pkt_adduint32(pkt, attrs.permissions);\r
+    }\r
+    if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {\r
+       sftp_pkt_adduint32(pkt, attrs.atime);\r
+       sftp_pkt_adduint32(pkt, attrs.mtime);\r
+    }\r
+    if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {\r
+       /*\r
+        * We currently don't support sending any extended\r
+        * attributes.\r
+        */\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * SFTP packet decode functions.\r
+ */\r
+\r
+static int sftp_pkt_getbyte(struct sftp_packet *pkt, unsigned char *ret)\r
+{\r
+    if (pkt->length - pkt->savedpos < 1)\r
+       return 0;\r
+    *ret = (unsigned char) pkt->data[pkt->savedpos];\r
+    pkt->savedpos++;\r
+    return 1;\r
+}\r
+static int sftp_pkt_getuint32(struct sftp_packet *pkt, unsigned long *ret)\r
+{\r
+    if (pkt->length - pkt->savedpos < 4)\r
+       return 0;\r
+    *ret = GET_32BIT(pkt->data + pkt->savedpos);\r
+    pkt->savedpos += 4;\r
+    return 1;\r
+}\r
+static int sftp_pkt_getstring(struct sftp_packet *pkt,\r
+                             char **p, int *length)\r
+{\r
+    *p = NULL;\r
+    if (pkt->length - pkt->savedpos < 4)\r
+       return 0;\r
+    *length = GET_32BIT(pkt->data + pkt->savedpos);\r
+    pkt->savedpos += 4;\r
+    if ((int)(pkt->length - pkt->savedpos) < *length || *length < 0) {\r
+       *length = 0;\r
+       return 0;\r
+    }\r
+    *p = pkt->data + pkt->savedpos;\r
+    pkt->savedpos += *length;\r
+    return 1;\r
+}\r
+static int sftp_pkt_getattrs(struct sftp_packet *pkt, struct fxp_attrs *ret)\r
+{\r
+    if (!sftp_pkt_getuint32(pkt, &ret->flags))\r
+       return 0;\r
+    if (ret->flags & SSH_FILEXFER_ATTR_SIZE) {\r
+       unsigned long hi, lo;\r
+       if (!sftp_pkt_getuint32(pkt, &hi) ||\r
+           !sftp_pkt_getuint32(pkt, &lo))\r
+           return 0;\r
+       ret->size = uint64_make(hi, lo);\r
+    }\r
+    if (ret->flags & SSH_FILEXFER_ATTR_UIDGID) {\r
+       if (!sftp_pkt_getuint32(pkt, &ret->uid) ||\r
+           !sftp_pkt_getuint32(pkt, &ret->gid))\r
+           return 0;\r
+    }\r
+    if (ret->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {\r
+       if (!sftp_pkt_getuint32(pkt, &ret->permissions))\r
+           return 0;\r
+    }\r
+    if (ret->flags & SSH_FILEXFER_ATTR_ACMODTIME) {\r
+       if (!sftp_pkt_getuint32(pkt, &ret->atime) ||\r
+           !sftp_pkt_getuint32(pkt, &ret->mtime))\r
+           return 0;\r
+    }\r
+    if (ret->flags & SSH_FILEXFER_ATTR_EXTENDED) {\r
+       unsigned long count;\r
+       if (!sftp_pkt_getuint32(pkt, &count))\r
+           return 0;\r
+       while (count--) {\r
+           char *str;\r
+           int len;\r
+           /*\r
+            * We should try to analyse these, if we ever find one\r
+            * we recognise.\r
+            */\r
+           if (!sftp_pkt_getstring(pkt, &str, &len) ||\r
+               !sftp_pkt_getstring(pkt, &str, &len))\r
+               return 0;\r
+       }\r
+    }\r
+    return 1;\r
+}\r
+static void sftp_pkt_free(struct sftp_packet *pkt)\r
+{\r
+    if (pkt->data)\r
+       sfree(pkt->data);\r
+    sfree(pkt);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Send and receive packet functions.\r
+ */\r
+int sftp_send(struct sftp_packet *pkt)\r
+{\r
+    int ret;\r
+    char x[4];\r
+    PUT_32BIT(x, pkt->length);\r
+    ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));\r
+    sftp_pkt_free(pkt);\r
+    return ret;\r
+}\r
+struct sftp_packet *sftp_recv(void)\r
+{\r
+    struct sftp_packet *pkt;\r
+    char x[4];\r
+    unsigned char uc;\r
+\r
+    if (!sftp_recvdata(x, 4))\r
+       return NULL;\r
+\r
+    pkt = snew(struct sftp_packet);\r
+    pkt->savedpos = 0;\r
+    pkt->length = pkt->maxlen = GET_32BIT(x);\r
+    pkt->data = snewn(pkt->length, char);\r
+\r
+    if (!sftp_recvdata(pkt->data, pkt->length)) {\r
+       sftp_pkt_free(pkt);\r
+       return NULL;\r
+    }\r
+\r
+    if (!sftp_pkt_getbyte(pkt, &uc)) {\r
+       sftp_pkt_free(pkt);\r
+       return NULL;\r
+    } else {\r
+       pkt->type = uc;\r
+    }\r
+\r
+    return pkt;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Request ID allocation and temporary dispatch routines.\r
+ */\r
+\r
+#define REQUEST_ID_OFFSET 256\r
+\r
+struct sftp_request {\r
+    unsigned id;\r
+    int registered;\r
+    void *userdata;\r
+};\r
+\r
+static int sftp_reqcmp(void *av, void *bv)\r
+{\r
+    struct sftp_request *a = (struct sftp_request *)av;\r
+    struct sftp_request *b = (struct sftp_request *)bv;\r
+    if (a->id < b->id)\r
+       return -1;\r
+    if (a->id > b->id)\r
+       return +1;\r
+    return 0;\r
+}\r
+static int sftp_reqfind(void *av, void *bv)\r
+{\r
+    unsigned *a = (unsigned *) av;\r
+    struct sftp_request *b = (struct sftp_request *)bv;\r
+    if (*a < b->id)\r
+       return -1;\r
+    if (*a > b->id)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static tree234 *sftp_requests;\r
+\r
+static struct sftp_request *sftp_alloc_request(void)\r
+{\r
+    unsigned low, high, mid;\r
+    int tsize;\r
+    struct sftp_request *r;\r
+\r
+    if (sftp_requests == NULL)\r
+       sftp_requests = newtree234(sftp_reqcmp);\r
+\r
+    /*\r
+     * First-fit allocation of request IDs: always pick the lowest\r
+     * unused one. To do this, binary-search using the counted\r
+     * B-tree to find the largest ID which is in a contiguous\r
+     * sequence from the beginning. (Precisely everything in that\r
+     * sequence must have ID equal to its tree index plus\r
+     * REQUEST_ID_OFFSET.)\r
+     */\r
+    tsize = count234(sftp_requests);\r
+\r
+    low = -1;\r
+    high = tsize;\r
+    while (high - low > 1) {\r
+       mid = (high + low) / 2;\r
+       r = index234(sftp_requests, mid);\r
+       if (r->id == mid + REQUEST_ID_OFFSET)\r
+           low = mid;                 /* this one is fine */\r
+       else\r
+           high = mid;                /* this one is past it */\r
+    }\r
+    /*\r
+     * Now low points to either -1, or the tree index of the\r
+     * largest ID in the initial sequence.\r
+     */\r
+    {\r
+       unsigned i = low + 1 + REQUEST_ID_OFFSET;\r
+       assert(NULL == find234(sftp_requests, &i, sftp_reqfind));\r
+    }\r
+\r
+    /*\r
+     * So the request ID we need to create is\r
+     * low + 1 + REQUEST_ID_OFFSET.\r
+     */\r
+    r = snew(struct sftp_request);\r
+    r->id = low + 1 + REQUEST_ID_OFFSET;\r
+    r->registered = 0;\r
+    r->userdata = NULL;\r
+    add234(sftp_requests, r);\r
+    return r;\r
+}\r
+\r
+void sftp_cleanup_request(void)\r
+{\r
+    if (sftp_requests != NULL) {\r
+       freetree234(sftp_requests);\r
+       sftp_requests = NULL;\r
+    }\r
+}\r
+\r
+void sftp_register(struct sftp_request *req)\r
+{\r
+    req->registered = 1;\r
+}\r
+\r
+struct sftp_request *sftp_find_request(struct sftp_packet *pktin)\r
+{\r
+    unsigned long id;\r
+    struct sftp_request *req;\r
+\r
+    if (!pktin) {\r
+       fxp_internal_error("did not receive a valid SFTP packet\n");\r
+       return NULL;\r
+    }\r
+\r
+    if (!sftp_pkt_getuint32(pktin, &id)) {\r
+       fxp_internal_error("did not receive a valid SFTP packet\n");\r
+       return NULL;\r
+    }\r
+    req = find234(sftp_requests, &id, sftp_reqfind);\r
+\r
+    if (!req || !req->registered) {\r
+       fxp_internal_error("request ID mismatch\n");\r
+        sftp_pkt_free(pktin);\r
+       return NULL;\r
+    }\r
+\r
+    del234(sftp_requests, req);\r
+\r
+    return req;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * String handling routines.\r
+ */\r
+\r
+static char *mkstr(char *s, int len)\r
+{\r
+    char *p = snewn(len + 1, char);\r
+    memcpy(p, s, len);\r
+    p[len] = '\0';\r
+    return p;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * SFTP primitives.\r
+ */\r
+\r
+/*\r
+ * Deal with (and free) an FXP_STATUS packet. Return 1 if\r
+ * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).\r
+ * Also place the status into fxp_errtype.\r
+ */\r
+static int fxp_got_status(struct sftp_packet *pktin)\r
+{\r
+    static const char *const messages[] = {\r
+       /* SSH_FX_OK. The only time we will display a _message_ for this\r
+        * is if we were expecting something other than FXP_STATUS on\r
+        * success, so this is actually an error message! */\r
+       "unexpected OK response",\r
+       "end of file",\r
+       "no such file or directory",\r
+       "permission denied",\r
+       "failure",\r
+       "bad message",\r
+       "no connection",\r
+       "connection lost",\r
+       "operation unsupported",\r
+    };\r
+\r
+    if (pktin->type != SSH_FXP_STATUS) {\r
+       fxp_error_message = "expected FXP_STATUS packet";\r
+       fxp_errtype = -1;\r
+    } else {\r
+       unsigned long ul;\r
+       if (!sftp_pkt_getuint32(pktin, &ul)) {\r
+           fxp_error_message = "malformed FXP_STATUS packet";\r
+           fxp_errtype = -1;\r
+       } else {\r
+           fxp_errtype = ul;\r
+           if (fxp_errtype < 0 ||\r
+               fxp_errtype >= sizeof(messages) / sizeof(*messages))\r
+               fxp_error_message = "unknown error code";\r
+           else\r
+               fxp_error_message = messages[fxp_errtype];\r
+       }\r
+    }\r
+\r
+    if (fxp_errtype == SSH_FX_OK)\r
+       return 1;\r
+    else if (fxp_errtype == SSH_FX_EOF)\r
+       return 0;\r
+    else\r
+       return -1;\r
+}\r
+\r
+static void fxp_internal_error(char *msg)\r
+{\r
+    fxp_error_message = msg;\r
+    fxp_errtype = -1;\r
+}\r
+\r
+const char *fxp_error(void)\r
+{\r
+    return fxp_error_message;\r
+}\r
+\r
+int fxp_error_type(void)\r
+{\r
+    return fxp_errtype;\r
+}\r
+\r
+/*\r
+ * Perform exchange of init/version packets. Return 0 on failure.\r
+ */\r
+int fxp_init(void)\r
+{\r
+    struct sftp_packet *pktout, *pktin;\r
+    unsigned long remotever;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_INIT);\r
+    sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);\r
+    sftp_send(pktout);\r
+\r
+    pktin = sftp_recv();\r
+    if (!pktin) {\r
+       fxp_internal_error("could not connect");\r
+       return 0;\r
+    }\r
+    if (pktin->type != SSH_FXP_VERSION) {\r
+       fxp_internal_error("did not receive FXP_VERSION");\r
+        sftp_pkt_free(pktin);\r
+       return 0;\r
+    }\r
+    if (!sftp_pkt_getuint32(pktin, &remotever)) {\r
+       fxp_internal_error("malformed FXP_VERSION packet");\r
+        sftp_pkt_free(pktin);\r
+       return 0;\r
+    }\r
+    if (remotever > SFTP_PROTO_VERSION) {\r
+       fxp_internal_error\r
+           ("remote protocol is more advanced than we support");\r
+        sftp_pkt_free(pktin);\r
+       return 0;\r
+    }\r
+    /*\r
+     * In principle, this packet might also contain extension-\r
+     * string pairs. We should work through them and look for any\r
+     * we recognise. In practice we don't currently do so because\r
+     * we know we don't recognise _any_.\r
+     */\r
+    sftp_pkt_free(pktin);\r
+\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Canonify a pathname.\r
+ */\r
+struct sftp_request *fxp_realpath_send(char *path)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_REALPATH);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_str(pktout, path);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    sfree(req);\r
+\r
+    if (pktin->type == SSH_FXP_NAME) {\r
+       unsigned long count;\r
+       char *path;\r
+       int len;\r
+\r
+       if (!sftp_pkt_getuint32(pktin, &count) || count != 1) {\r
+           fxp_internal_error("REALPATH did not return name count of 1\n");\r
+            sftp_pkt_free(pktin);\r
+           return NULL;\r
+       }\r
+       if (!sftp_pkt_getstring(pktin, &path, &len)) {\r
+           fxp_internal_error("REALPATH returned malformed FXP_NAME\n");\r
+            sftp_pkt_free(pktin);\r
+           return NULL;\r
+       }\r
+       path = mkstr(path, len);\r
+       sftp_pkt_free(pktin);\r
+       return path;\r
+    } else {\r
+       fxp_got_status(pktin);\r
+        sftp_pkt_free(pktin);\r
+       return NULL;\r
+    }\r
+}\r
+\r
+/*\r
+ * Open a file.\r
+ */\r
+struct sftp_request *fxp_open_send(char *path, int type)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_OPEN);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, path);\r
+    sftp_pkt_adduint32(pktout, type);\r
+    sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,\r
+                                struct sftp_request *req)\r
+{\r
+    sfree(req);\r
+\r
+    if (pktin->type == SSH_FXP_HANDLE) {\r
+       char *hstring;\r
+       struct fxp_handle *handle;\r
+       int len;\r
+\r
+       if (!sftp_pkt_getstring(pktin, &hstring, &len)) {\r
+           fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");\r
+            sftp_pkt_free(pktin);\r
+           return NULL;\r
+       }\r
+       handle = snew(struct fxp_handle);\r
+       handle->hstring = mkstr(hstring, len);\r
+       handle->hlen = len;\r
+       sftp_pkt_free(pktin);\r
+       return handle;\r
+    } else {\r
+       fxp_got_status(pktin);\r
+        sftp_pkt_free(pktin);\r
+       return NULL;\r
+    }\r
+}\r
+\r
+/*\r
+ * Open a directory.\r
+ */\r
+struct sftp_request *fxp_opendir_send(char *path)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_OPENDIR);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, path);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,\r
+                                   struct sftp_request *req)\r
+{\r
+    sfree(req);\r
+    if (pktin->type == SSH_FXP_HANDLE) {\r
+       char *hstring;\r
+       struct fxp_handle *handle;\r
+       int len;\r
+\r
+       if (!sftp_pkt_getstring(pktin, &hstring, &len)) {\r
+           fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");\r
+            sftp_pkt_free(pktin);\r
+           return NULL;\r
+       }\r
+       handle = snew(struct fxp_handle);\r
+       handle->hstring = mkstr(hstring, len);\r
+       handle->hlen = len;\r
+       sftp_pkt_free(pktin);\r
+       return handle;\r
+    } else {\r
+       fxp_got_status(pktin);\r
+        sftp_pkt_free(pktin);\r
+       return NULL;\r
+    }\r
+}\r
+\r
+/*\r
+ * Close a file/dir.\r
+ */\r
+struct sftp_request *fxp_close_send(struct fxp_handle *handle)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_CLOSE);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);\r
+    sftp_send(pktout);\r
+\r
+    sfree(handle->hstring);\r
+    sfree(handle);\r
+\r
+    return req;\r
+}\r
+\r
+void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    sfree(req);\r
+    fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+}\r
+\r
+struct sftp_request *fxp_mkdir_send(char *path)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_MKDIR);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, path);\r
+    sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    int id;\r
+    sfree(req);\r
+    id = fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+    if (id != 1) {\r
+       return 0;\r
+    }\r
+    return 1;\r
+}\r
+\r
+struct sftp_request *fxp_rmdir_send(char *path)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_RMDIR);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, path);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    int id;\r
+    sfree(req);\r
+    id = fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+    if (id != 1) {\r
+       return 0;\r
+    }\r
+    return 1;\r
+}\r
+\r
+struct sftp_request *fxp_remove_send(char *fname)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_REMOVE);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, fname);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    int id;\r
+    sfree(req);\r
+    id = fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+    if (id != 1) {\r
+       return 0;\r
+    }\r
+    return 1;\r
+}\r
+\r
+struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_RENAME);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, srcfname);\r
+    sftp_pkt_addstring(pktout, dstfname);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    int id;\r
+    sfree(req);\r
+    id = fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+    if (id != 1) {\r
+       return 0;\r
+    }\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Retrieve the attributes of a file. We have fxp_stat which works\r
+ * on filenames, and fxp_fstat which works on open file handles.\r
+ */\r
+struct sftp_request *fxp_stat_send(char *fname)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_STAT);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, fname);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,\r
+                 struct fxp_attrs *attrs)\r
+{\r
+    sfree(req);\r
+    if (pktin->type == SSH_FXP_ATTRS) {\r
+       if (!sftp_pkt_getattrs(pktin, attrs)) {\r
+           fxp_internal_error("malformed SSH_FXP_ATTRS packet");\r
+           sftp_pkt_free(pktin);\r
+           return 0;\r
+       }\r
+       sftp_pkt_free(pktin);\r
+       return 1;\r
+    } else {\r
+       fxp_got_status(pktin);\r
+        sftp_pkt_free(pktin);\r
+       return 0;\r
+    }\r
+}\r
+\r
+struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_FSTAT);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,\r
+                  struct fxp_attrs *attrs)\r
+{\r
+    sfree(req);\r
+    if (pktin->type == SSH_FXP_ATTRS) {\r
+       if (!sftp_pkt_getattrs(pktin, attrs)) {\r
+           fxp_internal_error("malformed SSH_FXP_ATTRS packet");\r
+           sftp_pkt_free(pktin);\r
+           return 0;\r
+       }\r
+       sftp_pkt_free(pktin);\r
+       return 1;\r
+    } else {\r
+       fxp_got_status(pktin);\r
+        sftp_pkt_free(pktin);\r
+       return 0;\r
+    }\r
+}\r
+\r
+/*\r
+ * Set the attributes of a file.\r
+ */\r
+struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_SETSTAT);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring(pktout, fname);\r
+    sftp_pkt_addattrs(pktout, attrs);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    int id;\r
+    sfree(req);\r
+    id = fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+    if (id != 1) {\r
+       return 0;\r
+    }\r
+    return 1;\r
+}\r
+\r
+struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,\r
+                                      struct fxp_attrs attrs)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);\r
+    sftp_pkt_addattrs(pktout, attrs);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    int id;\r
+    sfree(req);\r
+    id = fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+    if (id != 1) {\r
+       return 0;\r
+    }\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Read from a file. Returns the number of bytes read, or -1 on an\r
+ * error, or possibly 0 if EOF. (I'm not entirely sure whether it\r
+ * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the\r
+ * error indicator. It might even depend on the SFTP server.)\r
+ */\r
+struct sftp_request *fxp_read_send(struct fxp_handle *handle,\r
+                                  uint64 offset, int len)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_READ);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);\r
+    sftp_pkt_adduint64(pktout, offset);\r
+    sftp_pkt_adduint32(pktout, len);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,\r
+                 char *buffer, int len)\r
+{\r
+    sfree(req);\r
+    if (pktin->type == SSH_FXP_DATA) {\r
+       char *str;\r
+       int rlen;\r
+\r
+       if (!sftp_pkt_getstring(pktin, &str, &rlen)) {\r
+           fxp_internal_error("READ returned malformed SSH_FXP_DATA packet");\r
+            sftp_pkt_free(pktin);\r
+           return -1;\r
+       }\r
+\r
+       if (rlen > len || rlen < 0) {\r
+           fxp_internal_error("READ returned more bytes than requested");\r
+            sftp_pkt_free(pktin);\r
+           return -1;\r
+       }\r
+\r
+       memcpy(buffer, str, rlen);\r
+        sftp_pkt_free(pktin);\r
+       return rlen;\r
+    } else {\r
+       fxp_got_status(pktin);\r
+        sftp_pkt_free(pktin);\r
+       return -1;\r
+    }\r
+}\r
+\r
+/*\r
+ * Read from a directory.\r
+ */\r
+struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_READDIR);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,\r
+                                  struct sftp_request *req)\r
+{\r
+    sfree(req);\r
+    if (pktin->type == SSH_FXP_NAME) {\r
+       struct fxp_names *ret;\r
+       unsigned long i;\r
+\r
+       /*\r
+        * Sanity-check the number of names. Minimum is obviously\r
+        * zero. Maximum is the remaining space in the packet\r
+        * divided by the very minimum length of a name, which is\r
+        * 12 bytes (4 for an empty filename, 4 for an empty\r
+        * longname, 4 for a set of attribute flags indicating that\r
+        * no other attributes are supplied).\r
+        */\r
+       if (!sftp_pkt_getuint32(pktin, &i) ||\r
+           i > (pktin->length-pktin->savedpos)/12) {\r
+           fxp_internal_error("malformed FXP_NAME packet");\r
+           sftp_pkt_free(pktin);\r
+           return NULL;\r
+       }\r
+\r
+       /*\r
+        * Ensure the implicit multiplication in the snewn() call\r
+        * doesn't suffer integer overflow and cause us to malloc\r
+        * too little space.\r
+        */\r
+       if (i > INT_MAX / sizeof(struct fxp_name)) {\r
+           fxp_internal_error("unreasonably large FXP_NAME packet");\r
+           sftp_pkt_free(pktin);\r
+           return NULL;\r
+       }\r
+\r
+       ret = snew(struct fxp_names);\r
+       ret->nnames = i;\r
+       ret->names = snewn(ret->nnames, struct fxp_name);\r
+       for (i = 0; i < (unsigned long)ret->nnames; i++) {\r
+           char *str1, *str2;\r
+           int len1, len2;\r
+           if (!sftp_pkt_getstring(pktin, &str1, &len1) ||\r
+               !sftp_pkt_getstring(pktin, &str2, &len2) ||\r
+               !sftp_pkt_getattrs(pktin, &ret->names[i].attrs)) {\r
+               fxp_internal_error("malformed FXP_NAME packet");\r
+               while (i--) {\r
+                   sfree(ret->names[i].filename);\r
+                   sfree(ret->names[i].longname);\r
+               }\r
+               sfree(ret->names);\r
+               sfree(ret);\r
+               sfree(pktin);\r
+               return NULL;\r
+           }\r
+           ret->names[i].filename = mkstr(str1, len1);\r
+           ret->names[i].longname = mkstr(str2, len2);\r
+       }\r
+        sftp_pkt_free(pktin);\r
+       return ret;\r
+    } else {\r
+       fxp_got_status(pktin);\r
+        sftp_pkt_free(pktin);\r
+       return NULL;\r
+    }\r
+}\r
+\r
+/*\r
+ * Write to a file. Returns 0 on error, 1 on OK.\r
+ */\r
+struct sftp_request *fxp_write_send(struct fxp_handle *handle,\r
+                                   char *buffer, uint64 offset, int len)\r
+{\r
+    struct sftp_request *req = sftp_alloc_request();\r
+    struct sftp_packet *pktout;\r
+\r
+    pktout = sftp_pkt_init(SSH_FXP_WRITE);\r
+    sftp_pkt_adduint32(pktout, req->id);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);\r
+    sftp_pkt_adduint64(pktout, offset);\r
+    sftp_pkt_addstring_start(pktout);\r
+    sftp_pkt_addstring_data(pktout, buffer, len);\r
+    sftp_send(pktout);\r
+\r
+    return req;\r
+}\r
+\r
+int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)\r
+{\r
+    sfree(req);\r
+    fxp_got_status(pktin);\r
+    sftp_pkt_free(pktin);\r
+    return fxp_errtype == SSH_FX_OK;\r
+}\r
+\r
+/*\r
+ * Free up an fxp_names structure.\r
+ */\r
+void fxp_free_names(struct fxp_names *names)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < names->nnames; i++) {\r
+       sfree(names->names[i].filename);\r
+       sfree(names->names[i].longname);\r
+    }\r
+    sfree(names->names);\r
+    sfree(names);\r
+}\r
+\r
+/*\r
+ * Duplicate an fxp_name structure.\r
+ */\r
+struct fxp_name *fxp_dup_name(struct fxp_name *name)\r
+{\r
+    struct fxp_name *ret;\r
+    ret = snew(struct fxp_name);\r
+    ret->filename = dupstr(name->filename);\r
+    ret->longname = dupstr(name->longname);\r
+    ret->attrs = name->attrs;         /* structure copy */\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Free up an fxp_name structure.\r
+ */\r
+void fxp_free_name(struct fxp_name *name)\r
+{\r
+    sfree(name->filename);\r
+    sfree(name->longname);\r
+    sfree(name);\r
+}\r
+\r
+/*\r
+ * Store user data in an sftp_request structure.\r
+ */\r
+void *fxp_get_userdata(struct sftp_request *req)\r
+{\r
+    return req->userdata;\r
+}\r
+\r
+void fxp_set_userdata(struct sftp_request *req, void *data)\r
+{\r
+    req->userdata = data;\r
+}\r
+\r
+/*\r
+ * A wrapper to go round fxp_read_* and fxp_write_*, which manages\r
+ * the queueing of multiple read/write requests.\r
+ */\r
+\r
+struct req {\r
+    char *buffer;\r
+    int len, retlen, complete;\r
+    uint64 offset;\r
+    struct req *next, *prev;\r
+};\r
+\r
+struct fxp_xfer {\r
+    uint64 offset, furthestdata, filesize;\r
+    int req_totalsize, req_maxsize, eof, err;\r
+    struct fxp_handle *fh;\r
+    struct req *head, *tail;\r
+};\r
+\r
+static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)\r
+{\r
+    struct fxp_xfer *xfer = snew(struct fxp_xfer);\r
+\r
+    xfer->fh = fh;\r
+    xfer->offset = offset;\r
+    xfer->head = xfer->tail = NULL;\r
+    xfer->req_totalsize = 0;\r
+    xfer->req_maxsize = 1048576;\r
+    xfer->err = 0;\r
+    xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);\r
+    xfer->furthestdata = uint64_make(0, 0);\r
+\r
+    return xfer;\r
+}\r
+\r
+int xfer_done(struct fxp_xfer *xfer)\r
+{\r
+    /*\r
+     * We're finished if we've seen EOF _and_ there are no\r
+     * outstanding requests.\r
+     */\r
+    return (xfer->eof || xfer->err) && !xfer->head;\r
+}\r
+\r
+void xfer_download_queue(struct fxp_xfer *xfer)\r
+{\r
+    while (xfer->req_totalsize < xfer->req_maxsize &&\r
+          !xfer->eof && !xfer->err) {\r
+       /*\r
+        * Queue a new read request.\r
+        */\r
+       struct req *rr;\r
+       struct sftp_request *req;\r
+\r
+       rr = snew(struct req);\r
+       rr->offset = xfer->offset;\r
+       rr->complete = 0;\r
+       if (xfer->tail) {\r
+           xfer->tail->next = rr;\r
+           rr->prev = xfer->tail;\r
+       } else {\r
+           xfer->head = rr;\r
+           rr->prev = NULL;\r
+       }\r
+       xfer->tail = rr;\r
+       rr->next = NULL;\r
+\r
+       rr->len = 32768;\r
+       rr->buffer = snewn(rr->len, char);\r
+       sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));\r
+       fxp_set_userdata(req, rr);\r
+\r
+       xfer->offset = uint64_add32(xfer->offset, rr->len);\r
+       xfer->req_totalsize += rr->len;\r
+\r
+#ifdef DEBUG_DOWNLOAD\r
+       { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }\r
+#endif\r
+    }\r
+}\r
+\r
+struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)\r
+{\r
+    struct fxp_xfer *xfer = xfer_init(fh, offset);\r
+\r
+    xfer->eof = FALSE;\r
+    xfer_download_queue(xfer);\r
+\r
+    return xfer;\r
+}\r
+\r
+int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)\r
+{\r
+    struct sftp_request *rreq;\r
+    struct req *rr;\r
+\r
+    rreq = sftp_find_request(pktin);\r
+    rr = (struct req *)fxp_get_userdata(rreq);\r
+    if (!rr)\r
+       return 0;                      /* this packet isn't ours */\r
+    rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);\r
+#ifdef DEBUG_DOWNLOAD\r
+    printf("read request %p has returned [%d]\n", rr, rr->retlen);\r
+#endif\r
+\r
+    if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {\r
+       xfer->eof = TRUE;\r
+       rr->complete = -1;\r
+#ifdef DEBUG_DOWNLOAD\r
+       printf("setting eof\n");\r
+#endif\r
+    } else if (rr->retlen < 0) {\r
+       /* some error other than EOF; signal it back to caller */\r
+       xfer_set_error(xfer);\r
+       rr->complete = -1;\r
+       return -1;\r
+    }\r
+\r
+    rr->complete = 1;\r
+\r
+    /*\r
+     * Special case: if we have received fewer bytes than we\r
+     * actually read, we should do something. For the moment I'll\r
+     * just throw an ersatz FXP error to signal this; the SFTP\r
+     * draft I've got says that it can't happen except on special\r
+     * files, in which case seeking probably has very little\r
+     * meaning and so queueing an additional read request to fill\r
+     * up the gap sounds like the wrong answer. I'm not sure what I\r
+     * should be doing here - if it _was_ a special file, I suspect\r
+     * I simply shouldn't have been queueing multiple requests in\r
+     * the first place...\r
+     */\r
+    if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {\r
+       xfer->furthestdata = rr->offset;\r
+#ifdef DEBUG_DOWNLOAD\r
+       { char buf[40];\r
+       uint64_decimal(xfer->furthestdata, buf);\r
+       printf("setting furthestdata = %s\n", buf); }\r
+#endif\r
+    }\r
+\r
+    if (rr->retlen < rr->len) {\r
+       uint64 filesize = uint64_add32(rr->offset,\r
+                                      (rr->retlen < 0 ? 0 : rr->retlen));\r
+#ifdef DEBUG_DOWNLOAD\r
+       { char buf[40];\r
+       uint64_decimal(filesize, buf);\r
+       printf("short block! trying filesize = %s\n", buf); }\r
+#endif\r
+       if (uint64_compare(xfer->filesize, filesize) > 0) {\r
+           xfer->filesize = filesize;\r
+#ifdef DEBUG_DOWNLOAD\r
+           printf("actually changing filesize\n");\r
+#endif     \r
+       }\r
+    }\r
+\r
+    if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {\r
+       fxp_error_message = "received a short buffer from FXP_READ, but not"\r
+           " at EOF";\r
+       fxp_errtype = -1;\r
+       xfer_set_error(xfer);\r
+       return -1;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+void xfer_set_error(struct fxp_xfer *xfer)\r
+{\r
+    xfer->err = 1;\r
+}\r
+\r
+int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)\r
+{\r
+    void *retbuf = NULL;\r
+    int retlen = 0;\r
+\r
+    /*\r
+     * Discard anything at the head of the rr queue with complete <\r
+     * 0; return the first thing with complete > 0.\r
+     */\r
+    while (xfer->head && xfer->head->complete && !retbuf) {\r
+       struct req *rr = xfer->head;\r
+\r
+       if (rr->complete > 0) {\r
+           retbuf = rr->buffer;\r
+           retlen = rr->retlen;\r
+#ifdef DEBUG_DOWNLOAD\r
+           printf("handing back data from read request %p\n", rr);\r
+#endif\r
+       }\r
+#ifdef DEBUG_DOWNLOAD\r
+       else\r
+           printf("skipping failed read request %p\n", rr);\r
+#endif\r
+\r
+       xfer->head = xfer->head->next;\r
+       if (xfer->head)\r
+           xfer->head->prev = NULL;\r
+       else\r
+           xfer->tail = NULL;\r
+       xfer->req_totalsize -= rr->len;\r
+       sfree(rr);\r
+    }\r
+\r
+    if (retbuf) {\r
+       *buf = retbuf;\r
+       *len = retlen;\r
+       return 1;\r
+    } else\r
+       return 0;\r
+}\r
+\r
+struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)\r
+{\r
+    struct fxp_xfer *xfer = xfer_init(fh, offset);\r
+\r
+    /*\r
+     * We set `eof' to 1 because this will cause xfer_done() to\r
+     * return true iff there are no outstanding requests. During an\r
+     * upload, our caller will be responsible for working out\r
+     * whether all the data has been sent, so all it needs to know\r
+     * from us is whether the outstanding requests have been\r
+     * handled once that's done.\r
+     */\r
+    xfer->eof = 1;\r
+\r
+    return xfer;\r
+}\r
+\r
+int xfer_upload_ready(struct fxp_xfer *xfer)\r
+{\r
+    if (xfer->req_totalsize < xfer->req_maxsize)\r
+       return 1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)\r
+{\r
+    struct req *rr;\r
+    struct sftp_request *req;\r
+\r
+    rr = snew(struct req);\r
+    rr->offset = xfer->offset;\r
+    rr->complete = 0;\r
+    if (xfer->tail) {\r
+       xfer->tail->next = rr;\r
+       rr->prev = xfer->tail;\r
+    } else {\r
+       xfer->head = rr;\r
+       rr->prev = NULL;\r
+    }\r
+    xfer->tail = rr;\r
+    rr->next = NULL;\r
+\r
+    rr->len = len;\r
+    rr->buffer = NULL;\r
+    sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));\r
+    fxp_set_userdata(req, rr);\r
+\r
+    xfer->offset = uint64_add32(xfer->offset, rr->len);\r
+    xfer->req_totalsize += rr->len;\r
+\r
+#ifdef DEBUG_UPLOAD\r
+    { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }\r
+#endif\r
+}\r
+\r
+int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)\r
+{\r
+    struct sftp_request *rreq;\r
+    struct req *rr, *prev, *next;\r
+    int ret;\r
+\r
+    rreq = sftp_find_request(pktin);\r
+    rr = (struct req *)fxp_get_userdata(rreq);\r
+    if (!rr)\r
+       return 0;                      /* this packet isn't ours */\r
+    ret = fxp_write_recv(pktin, rreq);\r
+#ifdef DEBUG_UPLOAD\r
+    printf("write request %p has returned [%d]\n", rr, ret);\r
+#endif\r
+\r
+    /*\r
+     * Remove this one from the queue.\r
+     */\r
+    prev = rr->prev;\r
+    next = rr->next;\r
+    if (prev)\r
+       prev->next = next;\r
+    else\r
+       xfer->head = next;\r
+    if (next)\r
+       next->prev = prev;\r
+    else\r
+       xfer->tail = prev;\r
+    xfer->req_totalsize -= rr->len;\r
+    sfree(rr);\r
+\r
+    if (!ret)\r
+       return -1;\r
+\r
+    return 1;\r
+}\r
+\r
+void xfer_cleanup(struct fxp_xfer *xfer)\r
+{\r
+    struct req *rr;\r
+    while (xfer->head) {\r
+       rr = xfer->head;\r
+       xfer->head = xfer->head->next;\r
+       sfree(rr->buffer);\r
+       sfree(rr);\r
+    }\r
+    sfree(xfer);\r
+}\r
diff --git a/putty/SFTP.H b/putty/SFTP.H
new file mode 100644 (file)
index 0000000..98368b3
--- /dev/null
@@ -0,0 +1,248 @@
+/*\r
+ * sftp.h: definitions for SFTP and the sftp.c routines.\r
+ */\r
+\r
+#include "int64.h"\r
+\r
+#define SSH_FXP_INIT                              1    /* 0x1 */\r
+#define SSH_FXP_VERSION                           2    /* 0x2 */\r
+#define SSH_FXP_OPEN                              3    /* 0x3 */\r
+#define SSH_FXP_CLOSE                             4    /* 0x4 */\r
+#define SSH_FXP_READ                              5    /* 0x5 */\r
+#define SSH_FXP_WRITE                             6    /* 0x6 */\r
+#define SSH_FXP_LSTAT                             7    /* 0x7 */\r
+#define SSH_FXP_FSTAT                             8    /* 0x8 */\r
+#define SSH_FXP_SETSTAT                           9    /* 0x9 */\r
+#define SSH_FXP_FSETSTAT                          10   /* 0xa */\r
+#define SSH_FXP_OPENDIR                           11   /* 0xb */\r
+#define SSH_FXP_READDIR                           12   /* 0xc */\r
+#define SSH_FXP_REMOVE                            13   /* 0xd */\r
+#define SSH_FXP_MKDIR                             14   /* 0xe */\r
+#define SSH_FXP_RMDIR                             15   /* 0xf */\r
+#define SSH_FXP_REALPATH                          16   /* 0x10 */\r
+#define SSH_FXP_STAT                              17   /* 0x11 */\r
+#define SSH_FXP_RENAME                            18   /* 0x12 */\r
+#define SSH_FXP_STATUS                            101  /* 0x65 */\r
+#define SSH_FXP_HANDLE                            102  /* 0x66 */\r
+#define SSH_FXP_DATA                              103  /* 0x67 */\r
+#define SSH_FXP_NAME                              104  /* 0x68 */\r
+#define SSH_FXP_ATTRS                             105  /* 0x69 */\r
+#define SSH_FXP_EXTENDED                          200  /* 0xc8 */\r
+#define SSH_FXP_EXTENDED_REPLY                    201  /* 0xc9 */\r
+\r
+#define SSH_FX_OK                                 0\r
+#define SSH_FX_EOF                                1\r
+#define SSH_FX_NO_SUCH_FILE                       2\r
+#define SSH_FX_PERMISSION_DENIED                  3\r
+#define SSH_FX_FAILURE                            4\r
+#define SSH_FX_BAD_MESSAGE                        5\r
+#define SSH_FX_NO_CONNECTION                      6\r
+#define SSH_FX_CONNECTION_LOST                    7\r
+#define SSH_FX_OP_UNSUPPORTED                     8\r
+\r
+#define SSH_FILEXFER_ATTR_SIZE                    0x00000001\r
+#define SSH_FILEXFER_ATTR_UIDGID                  0x00000002\r
+#define SSH_FILEXFER_ATTR_PERMISSIONS             0x00000004\r
+#define SSH_FILEXFER_ATTR_ACMODTIME               0x00000008\r
+#define SSH_FILEXFER_ATTR_EXTENDED                0x80000000\r
+\r
+#define SSH_FXF_READ                              0x00000001\r
+#define SSH_FXF_WRITE                             0x00000002\r
+#define SSH_FXF_APPEND                            0x00000004\r
+#define SSH_FXF_CREAT                             0x00000008\r
+#define SSH_FXF_TRUNC                             0x00000010\r
+#define SSH_FXF_EXCL                              0x00000020\r
+\r
+#define SFTP_PROTO_VERSION 3\r
+\r
+/*\r
+ * External references. The sftp client module sftp.c expects to be\r
+ * able to get at these functions.\r
+ * \r
+ * sftp_recvdata must never return less than len. It either blocks\r
+ * until len is available, or it returns failure.\r
+ * \r
+ * Both functions return 1 on success, 0 on failure.\r
+ */\r
+int sftp_senddata(char *data, int len);\r
+int sftp_recvdata(char *data, int len);\r
+\r
+/*\r
+ * Free sftp_requests\r
+ */\r
+void sftp_cleanup_request(void);\r
+\r
+struct fxp_attrs {\r
+    unsigned long flags;\r
+    uint64 size;\r
+    unsigned long uid;\r
+    unsigned long gid;\r
+    unsigned long permissions;\r
+    unsigned long atime;\r
+    unsigned long mtime;\r
+};\r
+\r
+struct fxp_handle {\r
+    char *hstring;\r
+    int hlen;\r
+};\r
+\r
+struct fxp_name {\r
+    char *filename, *longname;\r
+    struct fxp_attrs attrs;\r
+};\r
+\r
+struct fxp_names {\r
+    int nnames;\r
+    struct fxp_name *names;\r
+};\r
+\r
+struct sftp_request;\r
+struct sftp_packet;\r
+\r
+const char *fxp_error(void);\r
+int fxp_error_type(void);\r
+\r
+/*\r
+ * Perform exchange of init/version packets. Return 0 on failure.\r
+ */\r
+int fxp_init(void);\r
+\r
+/*\r
+ * Canonify a pathname. Concatenate the two given path elements\r
+ * with a separating slash, unless the second is NULL.\r
+ */\r
+struct sftp_request *fxp_realpath_send(char *path);\r
+char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Open a file.\r
+ */\r
+struct sftp_request *fxp_open_send(char *path, int type);\r
+struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,\r
+                                struct sftp_request *req);\r
+\r
+/*\r
+ * Open a directory.\r
+ */\r
+struct sftp_request *fxp_opendir_send(char *path);\r
+struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,\r
+                                   struct sftp_request *req);\r
+\r
+/*\r
+ * Close a file/dir.\r
+ */\r
+struct sftp_request *fxp_close_send(struct fxp_handle *handle);\r
+void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Make a directory.\r
+ */\r
+struct sftp_request *fxp_mkdir_send(char *path);\r
+int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Remove a directory.\r
+ */\r
+struct sftp_request *fxp_rmdir_send(char *path);\r
+int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Remove a file.\r
+ */\r
+struct sftp_request *fxp_remove_send(char *fname);\r
+int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Rename a file.\r
+ */\r
+struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname);\r
+int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Return file attributes.\r
+ */\r
+struct sftp_request *fxp_stat_send(char *fname);\r
+int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,\r
+                 struct fxp_attrs *attrs);\r
+struct sftp_request *fxp_fstat_send(struct fxp_handle *handle);\r
+int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,\r
+                  struct fxp_attrs *attrs);\r
+\r
+/*\r
+ * Set file attributes.\r
+ */\r
+struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs);\r
+int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,\r
+                                      struct fxp_attrs attrs);\r
+int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Read from a file.\r
+ */\r
+struct sftp_request *fxp_read_send(struct fxp_handle *handle,\r
+                                  uint64 offset, int len);\r
+int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,\r
+                 char *buffer, int len);\r
+\r
+/*\r
+ * Write to a file. Returns 0 on error, 1 on OK.\r
+ */\r
+struct sftp_request *fxp_write_send(struct fxp_handle *handle,\r
+                                   char *buffer, uint64 offset, int len);\r
+int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req);\r
+\r
+/*\r
+ * Read from a directory.\r
+ */\r
+struct sftp_request *fxp_readdir_send(struct fxp_handle *handle);\r
+struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,\r
+                                  struct sftp_request *req);\r
+\r
+/*\r
+ * Free up an fxp_names structure.\r
+ */\r
+void fxp_free_names(struct fxp_names *names);\r
+\r
+/*\r
+ * Duplicate and free fxp_name structures.\r
+ */\r
+struct fxp_name *fxp_dup_name(struct fxp_name *name);\r
+void fxp_free_name(struct fxp_name *name);\r
+\r
+/*\r
+ * Store user data in an sftp_request structure.\r
+ */\r
+void *fxp_get_userdata(struct sftp_request *req);\r
+void fxp_set_userdata(struct sftp_request *req, void *data);\r
+\r
+/*\r
+ * These functions might well be temporary placeholders to be\r
+ * replaced with more useful similar functions later. They form the\r
+ * main dispatch loop for processing incoming SFTP responses.\r
+ */\r
+void sftp_register(struct sftp_request *req);\r
+struct sftp_request *sftp_find_request(struct sftp_packet *pktin);\r
+struct sftp_packet *sftp_recv(void);\r
+\r
+/*\r
+ * A wrapper to go round fxp_read_* and fxp_write_*, which manages\r
+ * the queueing of multiple read/write requests.\r
+ */\r
+\r
+struct fxp_xfer;\r
+\r
+struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset);\r
+void xfer_download_queue(struct fxp_xfer *xfer);\r
+int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin);\r
+int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len);\r
+\r
+struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset);\r
+int xfer_upload_ready(struct fxp_xfer *xfer);\r
+void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len);\r
+int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin);\r
+\r
+int xfer_done(struct fxp_xfer *xfer);\r
+void xfer_set_error(struct fxp_xfer *xfer);\r
+void xfer_cleanup(struct fxp_xfer *xfer);\r
diff --git a/putty/SIGN.SH b/putty/SIGN.SH
new file mode 100644 (file)
index 0000000..dfa9fbc
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh \r
+\r
+# Generate GPG signatures on a PuTTY release/snapshot directory as\r
+# delivered by Buildscr.\r
+\r
+# Usage: sh sign.sh <builddir> <keytype>\r
+# e.g.   sh sign.sh putty Snapshots  (probably in the build.out directory)\r
+#   or   sh sign.sh 0.60 Releases\r
+\r
+set -e\r
+\r
+sign() {\r
+  # Check for the prior existence of the signature, so we can\r
+  # re-run this script if it encounters an error part way\r
+  # through.\r
+  echo "----- Signing $2 with '$keyname'"\r
+  test -f "$3" || \\r
+    gpg --load-extension=idea "$1" -u "$keyname" -o "$3" "$2"\r
+}\r
+\r
+cd "$1"\r
+for t in DSA RSA; do\r
+  keyname="$2 ($t)"\r
+  echo "===== Signing with '$keyname'"\r
+  for i in putty*src.zip putty*.tar.gz x86/*.exe x86/*.zip; do\r
+    sign --detach-sign "$i" "$i.$t"\r
+  done\r
+  for i in md5sums sha1sums sha256sums sha512sums; do\r
+    sign --clearsign $i ${i}.$t\r
+  done\r
+done\r
diff --git a/putty/SSH.C b/putty/SSH.C
new file mode 100644 (file)
index 0000000..950af14
--- /dev/null
@@ -0,0 +1,9972 @@
+/*\r
+ * SSH backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <assert.h>\r
+#include <limits.h>\r
+#include <signal.h>\r
+\r
+#include "putty.h"\r
+#include "tree234.h"\r
+#include "ssh.h"\r
+#ifndef NO_GSSAPI\r
+#include "sshgssc.h"\r
+#include "sshgss.h"\r
+#endif\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+#define SSH1_MSG_DISCONNECT                       1    /* 0x1 */\r
+#define SSH1_SMSG_PUBLIC_KEY                      2    /* 0x2 */\r
+#define SSH1_CMSG_SESSION_KEY                     3    /* 0x3 */\r
+#define SSH1_CMSG_USER                            4    /* 0x4 */\r
+#define SSH1_CMSG_AUTH_RSA                        6    /* 0x6 */\r
+#define SSH1_SMSG_AUTH_RSA_CHALLENGE              7    /* 0x7 */\r
+#define SSH1_CMSG_AUTH_RSA_RESPONSE               8    /* 0x8 */\r
+#define SSH1_CMSG_AUTH_PASSWORD                   9    /* 0x9 */\r
+#define SSH1_CMSG_REQUEST_PTY                     10   /* 0xa */\r
+#define SSH1_CMSG_WINDOW_SIZE                     11   /* 0xb */\r
+#define SSH1_CMSG_EXEC_SHELL                      12   /* 0xc */\r
+#define SSH1_CMSG_EXEC_CMD                        13   /* 0xd */\r
+#define SSH1_SMSG_SUCCESS                         14   /* 0xe */\r
+#define SSH1_SMSG_FAILURE                         15   /* 0xf */\r
+#define SSH1_CMSG_STDIN_DATA                      16   /* 0x10 */\r
+#define SSH1_SMSG_STDOUT_DATA                     17   /* 0x11 */\r
+#define SSH1_SMSG_STDERR_DATA                     18   /* 0x12 */\r
+#define SSH1_CMSG_EOF                             19   /* 0x13 */\r
+#define SSH1_SMSG_EXIT_STATUS                     20   /* 0x14 */\r
+#define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION        21   /* 0x15 */\r
+#define SSH1_MSG_CHANNEL_OPEN_FAILURE             22   /* 0x16 */\r
+#define SSH1_MSG_CHANNEL_DATA                     23   /* 0x17 */\r
+#define SSH1_MSG_CHANNEL_CLOSE                    24   /* 0x18 */\r
+#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION       25   /* 0x19 */\r
+#define SSH1_SMSG_X11_OPEN                        27   /* 0x1b */\r
+#define SSH1_CMSG_PORT_FORWARD_REQUEST            28   /* 0x1c */\r
+#define SSH1_MSG_PORT_OPEN                        29   /* 0x1d */\r
+#define SSH1_CMSG_AGENT_REQUEST_FORWARDING        30   /* 0x1e */\r
+#define SSH1_SMSG_AGENT_OPEN                      31   /* 0x1f */\r
+#define SSH1_MSG_IGNORE                           32   /* 0x20 */\r
+#define SSH1_CMSG_EXIT_CONFIRMATION               33   /* 0x21 */\r
+#define SSH1_CMSG_X11_REQUEST_FORWARDING          34   /* 0x22 */\r
+#define SSH1_CMSG_AUTH_RHOSTS_RSA                 35   /* 0x23 */\r
+#define SSH1_MSG_DEBUG                            36   /* 0x24 */\r
+#define SSH1_CMSG_REQUEST_COMPRESSION             37   /* 0x25 */\r
+#define SSH1_CMSG_AUTH_TIS                        39   /* 0x27 */\r
+#define SSH1_SMSG_AUTH_TIS_CHALLENGE              40   /* 0x28 */\r
+#define SSH1_CMSG_AUTH_TIS_RESPONSE               41   /* 0x29 */\r
+#define SSH1_CMSG_AUTH_CCARD                      70   /* 0x46 */\r
+#define SSH1_SMSG_AUTH_CCARD_CHALLENGE            71   /* 0x47 */\r
+#define SSH1_CMSG_AUTH_CCARD_RESPONSE             72   /* 0x48 */\r
+\r
+#define SSH1_AUTH_RHOSTS                          1    /* 0x1 */\r
+#define SSH1_AUTH_RSA                             2    /* 0x2 */\r
+#define SSH1_AUTH_PASSWORD                        3    /* 0x3 */\r
+#define SSH1_AUTH_RHOSTS_RSA                      4    /* 0x4 */\r
+#define SSH1_AUTH_TIS                             5    /* 0x5 */\r
+#define SSH1_AUTH_CCARD                           16   /* 0x10 */\r
+\r
+#define SSH1_PROTOFLAG_SCREEN_NUMBER              1    /* 0x1 */\r
+/* Mask for protoflags we will echo back to server if seen */\r
+#define SSH1_PROTOFLAGS_SUPPORTED                 0    /* 0x1 */\r
+\r
+#define SSH2_MSG_DISCONNECT                       1    /* 0x1 */\r
+#define SSH2_MSG_IGNORE                           2    /* 0x2 */\r
+#define SSH2_MSG_UNIMPLEMENTED                    3    /* 0x3 */\r
+#define SSH2_MSG_DEBUG                            4    /* 0x4 */\r
+#define SSH2_MSG_SERVICE_REQUEST                  5    /* 0x5 */\r
+#define SSH2_MSG_SERVICE_ACCEPT                   6    /* 0x6 */\r
+#define SSH2_MSG_KEXINIT                          20   /* 0x14 */\r
+#define SSH2_MSG_NEWKEYS                          21   /* 0x15 */\r
+#define SSH2_MSG_KEXDH_INIT                       30   /* 0x1e */\r
+#define SSH2_MSG_KEXDH_REPLY                      31   /* 0x1f */\r
+#define SSH2_MSG_KEX_DH_GEX_REQUEST               30   /* 0x1e */\r
+#define SSH2_MSG_KEX_DH_GEX_GROUP                 31   /* 0x1f */\r
+#define SSH2_MSG_KEX_DH_GEX_INIT                  32   /* 0x20 */\r
+#define SSH2_MSG_KEX_DH_GEX_REPLY                 33   /* 0x21 */\r
+#define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */\r
+#define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */\r
+#define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */\r
+#define SSH2_MSG_USERAUTH_REQUEST                 50   /* 0x32 */\r
+#define SSH2_MSG_USERAUTH_FAILURE                 51   /* 0x33 */\r
+#define SSH2_MSG_USERAUTH_SUCCESS                 52   /* 0x34 */\r
+#define SSH2_MSG_USERAUTH_BANNER                  53   /* 0x35 */\r
+#define SSH2_MSG_USERAUTH_PK_OK                   60   /* 0x3c */\r
+#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ        60   /* 0x3c */\r
+#define SSH2_MSG_USERAUTH_INFO_REQUEST            60   /* 0x3c */\r
+#define SSH2_MSG_USERAUTH_INFO_RESPONSE           61   /* 0x3d */\r
+#define SSH2_MSG_GLOBAL_REQUEST                   80   /* 0x50 */\r
+#define SSH2_MSG_REQUEST_SUCCESS                  81   /* 0x51 */\r
+#define SSH2_MSG_REQUEST_FAILURE                  82   /* 0x52 */\r
+#define SSH2_MSG_CHANNEL_OPEN                     90   /* 0x5a */\r
+#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION        91   /* 0x5b */\r
+#define SSH2_MSG_CHANNEL_OPEN_FAILURE             92   /* 0x5c */\r
+#define SSH2_MSG_CHANNEL_WINDOW_ADJUST            93   /* 0x5d */\r
+#define SSH2_MSG_CHANNEL_DATA                     94   /* 0x5e */\r
+#define SSH2_MSG_CHANNEL_EXTENDED_DATA            95   /* 0x5f */\r
+#define SSH2_MSG_CHANNEL_EOF                      96   /* 0x60 */\r
+#define SSH2_MSG_CHANNEL_CLOSE                    97   /* 0x61 */\r
+#define SSH2_MSG_CHANNEL_REQUEST                  98   /* 0x62 */\r
+#define SSH2_MSG_CHANNEL_SUCCESS                  99   /* 0x63 */\r
+#define SSH2_MSG_CHANNEL_FAILURE                  100  /* 0x64 */\r
+#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE               60\r
+#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN                  61\r
+#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE      63\r
+#define SSH2_MSG_USERAUTH_GSSAPI_ERROR                  64\r
+#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK                 65\r
+#define SSH2_MSG_USERAUTH_GSSAPI_MIC                    66\r
+\r
+/*\r
+ * Packet type contexts, so that ssh2_pkt_type can correctly decode\r
+ * the ambiguous type numbers back into the correct type strings.\r
+ */\r
+typedef enum {\r
+    SSH2_PKTCTX_NOKEX,\r
+    SSH2_PKTCTX_DHGROUP,\r
+    SSH2_PKTCTX_DHGEX,\r
+    SSH2_PKTCTX_RSAKEX\r
+} Pkt_KCtx;\r
+typedef enum {\r
+    SSH2_PKTCTX_NOAUTH,\r
+    SSH2_PKTCTX_PUBLICKEY,\r
+    SSH2_PKTCTX_PASSWORD,\r
+    SSH2_PKTCTX_GSSAPI,\r
+    SSH2_PKTCTX_KBDINTER\r
+} Pkt_ACtx;\r
+\r
+#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1  /* 0x1 */\r
+#define SSH2_DISCONNECT_PROTOCOL_ERROR            2    /* 0x2 */\r
+#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED       3    /* 0x3 */\r
+#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4   /* 0x4 */\r
+#define SSH2_DISCONNECT_MAC_ERROR                 5    /* 0x5 */\r
+#define SSH2_DISCONNECT_COMPRESSION_ERROR         6    /* 0x6 */\r
+#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE     7    /* 0x7 */\r
+#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8       /* 0x8 */\r
+#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE   9    /* 0x9 */\r
+#define SSH2_DISCONNECT_CONNECTION_LOST           10   /* 0xa */\r
+#define SSH2_DISCONNECT_BY_APPLICATION            11   /* 0xb */\r
+#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS      12   /* 0xc */\r
+#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER    13   /* 0xd */\r
+#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14      /* 0xe */\r
+#define SSH2_DISCONNECT_ILLEGAL_USER_NAME         15   /* 0xf */\r
+\r
+static const char *const ssh2_disconnect_reasons[] = {\r
+    NULL,\r
+    "host not allowed to connect",\r
+    "protocol error",\r
+    "key exchange failed",\r
+    "host authentication failed",\r
+    "MAC error",\r
+    "compression error",\r
+    "service not available",\r
+    "protocol version not supported",\r
+    "host key not verifiable",\r
+    "connection lost",\r
+    "by application",\r
+    "too many connections",\r
+    "auth cancelled by user",\r
+    "no more auth methods available",\r
+    "illegal user name",\r
+};\r
+\r
+#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED     1    /* 0x1 */\r
+#define SSH2_OPEN_CONNECT_FAILED                  2    /* 0x2 */\r
+#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE            3    /* 0x3 */\r
+#define SSH2_OPEN_RESOURCE_SHORTAGE               4    /* 0x4 */\r
+\r
+#define SSH2_EXTENDED_DATA_STDERR                 1    /* 0x1 */\r
+\r
+/*\r
+ * Various remote-bug flags.\r
+ */\r
+#define BUG_CHOKES_ON_SSH1_IGNORE                 1\r
+#define BUG_SSH2_HMAC                             2\r
+#define BUG_NEEDS_SSH1_PLAIN_PASSWORD            4\r
+#define BUG_CHOKES_ON_RSA                        8\r
+#define BUG_SSH2_RSA_PADDING                    16\r
+#define BUG_SSH2_DERIVEKEY                       32\r
+#define BUG_SSH2_REKEY                           64\r
+#define BUG_SSH2_PK_SESSIONID                   128\r
+#define BUG_SSH2_MAXPKT                                256\r
+#define BUG_CHOKES_ON_SSH2_IGNORE               512\r
+\r
+/*\r
+ * Codes for terminal modes.\r
+ * Most of these are the same in SSH-1 and SSH-2.\r
+ * This list is derived from RFC 4254 and\r
+ * SSH-1 RFC-1.2.31.\r
+ */\r
+static const struct {\r
+    const char* const mode;\r
+    int opcode;\r
+    enum { TTY_OP_CHAR, TTY_OP_BOOL } type;\r
+} ssh_ttymodes[] = {\r
+    /* "V" prefix discarded for special characters relative to SSH specs */\r
+    { "INTR",        1, TTY_OP_CHAR },\r
+    { "QUIT",        2, TTY_OP_CHAR },\r
+    { "ERASE",       3, TTY_OP_CHAR },\r
+    { "KILL",        4, TTY_OP_CHAR },\r
+    { "EOF",         5, TTY_OP_CHAR },\r
+    { "EOL",         6, TTY_OP_CHAR },\r
+    { "EOL2",        7, TTY_OP_CHAR },\r
+    { "START",       8, TTY_OP_CHAR },\r
+    { "STOP",        9, TTY_OP_CHAR },\r
+    { "SUSP",       10, TTY_OP_CHAR },\r
+    { "DSUSP",      11, TTY_OP_CHAR },\r
+    { "REPRINT",     12, TTY_OP_CHAR },\r
+    { "WERASE",             13, TTY_OP_CHAR },\r
+    { "LNEXT",      14, TTY_OP_CHAR },\r
+    { "FLUSH",      15, TTY_OP_CHAR },\r
+    { "SWTCH",      16, TTY_OP_CHAR },\r
+    { "STATUS",             17, TTY_OP_CHAR },\r
+    { "DISCARD",     18, TTY_OP_CHAR },\r
+    { "IGNPAR",             30, TTY_OP_BOOL },\r
+    { "PARMRK",             31, TTY_OP_BOOL },\r
+    { "INPCK",      32, TTY_OP_BOOL },\r
+    { "ISTRIP",             33, TTY_OP_BOOL },\r
+    { "INLCR",      34, TTY_OP_BOOL },\r
+    { "IGNCR",      35, TTY_OP_BOOL },\r
+    { "ICRNL",      36, TTY_OP_BOOL },\r
+    { "IUCLC",      37, TTY_OP_BOOL },\r
+    { "IXON",       38, TTY_OP_BOOL },\r
+    { "IXANY",      39, TTY_OP_BOOL },\r
+    { "IXOFF",      40, TTY_OP_BOOL },\r
+    { "IMAXBEL",     41, TTY_OP_BOOL },\r
+    { "ISIG",       50, TTY_OP_BOOL },\r
+    { "ICANON",             51, TTY_OP_BOOL },\r
+    { "XCASE",      52, TTY_OP_BOOL },\r
+    { "ECHO",       53, TTY_OP_BOOL },\r
+    { "ECHOE",      54, TTY_OP_BOOL },\r
+    { "ECHOK",      55, TTY_OP_BOOL },\r
+    { "ECHONL",             56, TTY_OP_BOOL },\r
+    { "NOFLSH",             57, TTY_OP_BOOL },\r
+    { "TOSTOP",             58, TTY_OP_BOOL },\r
+    { "IEXTEN",             59, TTY_OP_BOOL },\r
+    { "ECHOCTL",     60, TTY_OP_BOOL },\r
+    { "ECHOKE",             61, TTY_OP_BOOL },\r
+    { "PENDIN",             62, TTY_OP_BOOL }, /* XXX is this a real mode? */\r
+    { "OPOST",      70, TTY_OP_BOOL },\r
+    { "OLCUC",      71, TTY_OP_BOOL },\r
+    { "ONLCR",      72, TTY_OP_BOOL },\r
+    { "OCRNL",      73, TTY_OP_BOOL },\r
+    { "ONOCR",      74, TTY_OP_BOOL },\r
+    { "ONLRET",             75, TTY_OP_BOOL },\r
+    { "CS7",        90, TTY_OP_BOOL },\r
+    { "CS8",        91, TTY_OP_BOOL },\r
+    { "PARENB",             92, TTY_OP_BOOL },\r
+    { "PARODD",             93, TTY_OP_BOOL }\r
+};\r
+\r
+/* Miscellaneous other tty-related constants. */\r
+#define SSH_TTY_OP_END           0\r
+/* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */\r
+#define SSH1_TTY_OP_ISPEED     192\r
+#define SSH1_TTY_OP_OSPEED     193\r
+#define SSH2_TTY_OP_ISPEED     128\r
+#define SSH2_TTY_OP_OSPEED     129\r
+\r
+/* Helper functions for parsing tty-related config. */\r
+static unsigned int ssh_tty_parse_specchar(char *s)\r
+{\r
+    unsigned int ret;\r
+    if (*s) {\r
+       char *next = NULL;\r
+       ret = ctrlparse(s, &next);\r
+       if (!next) ret = s[0];\r
+    } else {\r
+       ret = 255; /* special value meaning "don't set" */\r
+    }\r
+    return ret;\r
+}\r
+static unsigned int ssh_tty_parse_boolean(char *s)\r
+{\r
+    if (stricmp(s, "yes") == 0 ||\r
+       stricmp(s, "on") == 0 ||\r
+       stricmp(s, "true") == 0 ||\r
+       stricmp(s, "+") == 0)\r
+       return 1; /* true */\r
+    else if (stricmp(s, "no") == 0 ||\r
+            stricmp(s, "off") == 0 ||\r
+            stricmp(s, "false") == 0 ||\r
+            stricmp(s, "-") == 0)\r
+       return 0; /* false */\r
+    else\r
+       return (atoi(s) != 0);\r
+}\r
+\r
+#define translate(x) if (type == x) return #x\r
+#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x\r
+#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x\r
+static char *ssh1_pkt_type(int type)\r
+{\r
+    translate(SSH1_MSG_DISCONNECT);\r
+    translate(SSH1_SMSG_PUBLIC_KEY);\r
+    translate(SSH1_CMSG_SESSION_KEY);\r
+    translate(SSH1_CMSG_USER);\r
+    translate(SSH1_CMSG_AUTH_RSA);\r
+    translate(SSH1_SMSG_AUTH_RSA_CHALLENGE);\r
+    translate(SSH1_CMSG_AUTH_RSA_RESPONSE);\r
+    translate(SSH1_CMSG_AUTH_PASSWORD);\r
+    translate(SSH1_CMSG_REQUEST_PTY);\r
+    translate(SSH1_CMSG_WINDOW_SIZE);\r
+    translate(SSH1_CMSG_EXEC_SHELL);\r
+    translate(SSH1_CMSG_EXEC_CMD);\r
+    translate(SSH1_SMSG_SUCCESS);\r
+    translate(SSH1_SMSG_FAILURE);\r
+    translate(SSH1_CMSG_STDIN_DATA);\r
+    translate(SSH1_SMSG_STDOUT_DATA);\r
+    translate(SSH1_SMSG_STDERR_DATA);\r
+    translate(SSH1_CMSG_EOF);\r
+    translate(SSH1_SMSG_EXIT_STATUS);\r
+    translate(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);\r
+    translate(SSH1_MSG_CHANNEL_OPEN_FAILURE);\r
+    translate(SSH1_MSG_CHANNEL_DATA);\r
+    translate(SSH1_MSG_CHANNEL_CLOSE);\r
+    translate(SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);\r
+    translate(SSH1_SMSG_X11_OPEN);\r
+    translate(SSH1_CMSG_PORT_FORWARD_REQUEST);\r
+    translate(SSH1_MSG_PORT_OPEN);\r
+    translate(SSH1_CMSG_AGENT_REQUEST_FORWARDING);\r
+    translate(SSH1_SMSG_AGENT_OPEN);\r
+    translate(SSH1_MSG_IGNORE);\r
+    translate(SSH1_CMSG_EXIT_CONFIRMATION);\r
+    translate(SSH1_CMSG_X11_REQUEST_FORWARDING);\r
+    translate(SSH1_CMSG_AUTH_RHOSTS_RSA);\r
+    translate(SSH1_MSG_DEBUG);\r
+    translate(SSH1_CMSG_REQUEST_COMPRESSION);\r
+    translate(SSH1_CMSG_AUTH_TIS);\r
+    translate(SSH1_SMSG_AUTH_TIS_CHALLENGE);\r
+    translate(SSH1_CMSG_AUTH_TIS_RESPONSE);\r
+    translate(SSH1_CMSG_AUTH_CCARD);\r
+    translate(SSH1_SMSG_AUTH_CCARD_CHALLENGE);\r
+    translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);\r
+    return "unknown";\r
+}\r
+static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)\r
+{\r
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);\r
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);\r
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI);\r
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI);\r
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI);\r
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI);\r
+    translate(SSH2_MSG_DISCONNECT);\r
+    translate(SSH2_MSG_IGNORE);\r
+    translate(SSH2_MSG_UNIMPLEMENTED);\r
+    translate(SSH2_MSG_DEBUG);\r
+    translate(SSH2_MSG_SERVICE_REQUEST);\r
+    translate(SSH2_MSG_SERVICE_ACCEPT);\r
+    translate(SSH2_MSG_KEXINIT);\r
+    translate(SSH2_MSG_NEWKEYS);\r
+    translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);\r
+    translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);\r
+    translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);\r
+    translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);\r
+    translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);\r
+    translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);\r
+    translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);\r
+    translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);\r
+    translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);\r
+    translate(SSH2_MSG_USERAUTH_REQUEST);\r
+    translate(SSH2_MSG_USERAUTH_FAILURE);\r
+    translate(SSH2_MSG_USERAUTH_SUCCESS);\r
+    translate(SSH2_MSG_USERAUTH_BANNER);\r
+    translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);\r
+    translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);\r
+    translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);\r
+    translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);\r
+    translate(SSH2_MSG_GLOBAL_REQUEST);\r
+    translate(SSH2_MSG_REQUEST_SUCCESS);\r
+    translate(SSH2_MSG_REQUEST_FAILURE);\r
+    translate(SSH2_MSG_CHANNEL_OPEN);\r
+    translate(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);\r
+    translate(SSH2_MSG_CHANNEL_OPEN_FAILURE);\r
+    translate(SSH2_MSG_CHANNEL_WINDOW_ADJUST);\r
+    translate(SSH2_MSG_CHANNEL_DATA);\r
+    translate(SSH2_MSG_CHANNEL_EXTENDED_DATA);\r
+    translate(SSH2_MSG_CHANNEL_EOF);\r
+    translate(SSH2_MSG_CHANNEL_CLOSE);\r
+    translate(SSH2_MSG_CHANNEL_REQUEST);\r
+    translate(SSH2_MSG_CHANNEL_SUCCESS);\r
+    translate(SSH2_MSG_CHANNEL_FAILURE);\r
+    return "unknown";\r
+}\r
+#undef translate\r
+#undef translatec\r
+\r
+/* Enumeration values for fields in SSH-1 packets */\r
+enum {\r
+    PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,\r
+    /* These values are for communicating relevant semantics of\r
+     * fields to the packet logging code. */\r
+    PKTT_OTHER, PKTT_PASSWORD, PKTT_DATA\r
+};\r
+\r
+/*\r
+ * Coroutine mechanics for the sillier bits of the code. If these\r
+ * macros look impenetrable to you, you might find it helpful to\r
+ * read\r
+ * \r
+ *   http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html\r
+ * \r
+ * which explains the theory behind these macros.\r
+ * \r
+ * In particular, if you are getting `case expression not constant'\r
+ * errors when building with MS Visual Studio, this is because MS's\r
+ * Edit and Continue debugging feature causes their compiler to\r
+ * violate ANSI C. To disable Edit and Continue debugging:\r
+ * \r
+ *  - right-click ssh.c in the FileView\r
+ *  - click Settings\r
+ *  - select the C/C++ tab and the General category\r
+ *  - under `Debug info:', select anything _other_ than `Program\r
+ *    Database for Edit and Continue'.\r
+ */\r
+#define crBegin(v)     { int *crLine = &v; switch(v) { case 0:;\r
+#define crState(t) \\r
+    struct t *s; \\r
+    if (!ssh->t) ssh->t = snew(struct t); \\r
+    s = ssh->t;\r
+#define crFinish(z)    } *crLine = 0; return (z); }\r
+#define crFinishV      } *crLine = 0; return; }\r
+#define crReturn(z)    \\r
+       do {\\r
+           *crLine =__LINE__; return (z); case __LINE__:;\\r
+       } while (0)\r
+#define crReturnV      \\r
+       do {\\r
+           *crLine=__LINE__; return; case __LINE__:;\\r
+       } while (0)\r
+#define crStop(z)      do{ *crLine = 0; return (z); }while(0)\r
+#define crStopV                do{ *crLine = 0; return; }while(0)\r
+#define crWaitUntil(c) do { crReturn(0); } while (!(c))\r
+#define crWaitUntilV(c)        do { crReturnV; } while (!(c))\r
+\r
+typedef struct ssh_tag *Ssh;\r
+struct Packet;\r
+\r
+static struct Packet *ssh1_pkt_init(int pkt_type);\r
+static struct Packet *ssh2_pkt_init(int pkt_type);\r
+static void ssh_pkt_ensure(struct Packet *, int length);\r
+static void ssh_pkt_adddata(struct Packet *, void *data, int len);\r
+static void ssh_pkt_addbyte(struct Packet *, unsigned char value);\r
+static void ssh2_pkt_addbool(struct Packet *, unsigned char value);\r
+static void ssh_pkt_adduint32(struct Packet *, unsigned long value);\r
+static void ssh_pkt_addstring_start(struct Packet *);\r
+static void ssh_pkt_addstring_str(struct Packet *, char *data);\r
+static void ssh_pkt_addstring_data(struct Packet *, char *data, int len);\r
+static void ssh_pkt_addstring(struct Packet *, char *data);\r
+static unsigned char *ssh2_mpint_fmt(Bignum b, int *len);\r
+static void ssh1_pkt_addmp(struct Packet *, Bignum b);\r
+static void ssh2_pkt_addmp(struct Packet *, Bignum b);\r
+static int ssh2_pkt_construct(Ssh, struct Packet *);\r
+static void ssh2_pkt_send(Ssh, struct Packet *);\r
+static void ssh2_pkt_send_noqueue(Ssh, struct Packet *);\r
+static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,\r
+                        struct Packet *pktin);\r
+static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,\r
+                            struct Packet *pktin);\r
+\r
+/*\r
+ * Buffer management constants. There are several of these for\r
+ * various different purposes:\r
+ * \r
+ *  - SSH1_BUFFER_LIMIT is the amount of backlog that must build up\r
+ *    on a local data stream before we throttle the whole SSH\r
+ *    connection (in SSH-1 only). Throttling the whole connection is\r
+ *    pretty drastic so we set this high in the hope it won't\r
+ *    happen very often.\r
+ * \r
+ *  - SSH_MAX_BACKLOG is the amount of backlog that must build up\r
+ *    on the SSH connection itself before we defensively throttle\r
+ *    _all_ local data streams. This is pretty drastic too (though\r
+ *    thankfully unlikely in SSH-2 since the window mechanism should\r
+ *    ensure that the server never has any need to throttle its end\r
+ *    of the connection), so we set this high as well.\r
+ * \r
+ *  - OUR_V2_WINSIZE is the maximum window size we present on SSH-2\r
+ *    channels.\r
+ *\r
+ *  - OUR_V2_BIGWIN is the window size we advertise for the only\r
+ *    channel in a simple connection.  It must be <= INT_MAX.\r
+ *\r
+ *  - OUR_V2_MAXPKT is the official "maximum packet size" we send\r
+ *    to the remote side. This actually has nothing to do with the\r
+ *    size of the _packet_, but is instead a limit on the amount\r
+ *    of data we're willing to receive in a single SSH2 channel\r
+ *    data message.\r
+ *\r
+ *  - OUR_V2_PACKETLIMIT is actually the maximum size of SSH\r
+ *    _packet_ we're prepared to cope with.  It must be a multiple\r
+ *    of the cipher block size, and must be at least 35000.\r
+ */\r
+\r
+#define SSH1_BUFFER_LIMIT 32768\r
+#define SSH_MAX_BACKLOG 32768\r
+#define OUR_V2_WINSIZE 16384\r
+#define OUR_V2_BIGWIN 0x7fffffff\r
+#define OUR_V2_MAXPKT 0x4000UL\r
+#define OUR_V2_PACKETLIMIT 0x9000UL\r
+\r
+/* Maximum length of passwords/passphrases (arbitrary) */\r
+#define SSH_MAX_PASSWORD_LEN 100\r
+\r
+const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };\r
+\r
+const static struct ssh_mac *macs[] = {\r
+    &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5\r
+};\r
+const static struct ssh_mac *buggymacs[] = {\r
+    &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5\r
+};\r
+\r
+static void *ssh_comp_none_init(void)\r
+{\r
+    return NULL;\r
+}\r
+static void ssh_comp_none_cleanup(void *handle)\r
+{\r
+}\r
+static int ssh_comp_none_block(void *handle, unsigned char *block, int len,\r
+                              unsigned char **outblock, int *outlen)\r
+{\r
+    return 0;\r
+}\r
+static int ssh_comp_none_disable(void *handle)\r
+{\r
+    return 0;\r
+}\r
+const static struct ssh_compress ssh_comp_none = {\r
+    "none", NULL,\r
+    ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,\r
+    ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,\r
+    ssh_comp_none_disable, NULL\r
+};\r
+extern const struct ssh_compress ssh_zlib;\r
+const static struct ssh_compress *compressions[] = {\r
+    &ssh_zlib, &ssh_comp_none\r
+};\r
+\r
+enum {                                /* channel types */\r
+    CHAN_MAINSESSION,\r
+    CHAN_X11,\r
+    CHAN_AGENT,\r
+    CHAN_SOCKDATA,\r
+    CHAN_SOCKDATA_DORMANT             /* one the remote hasn't confirmed */\r
+};\r
+\r
+/*\r
+ * little structure to keep track of outstanding WINDOW_ADJUSTs\r
+ */\r
+struct winadj {\r
+    struct winadj *next;\r
+    unsigned size;\r
+};\r
+\r
+/*\r
+ * 2-3-4 tree storing channels.\r
+ */\r
+struct ssh_channel {\r
+    Ssh ssh;                          /* pointer back to main context */\r
+    unsigned remoteid, localid;\r
+    int type;\r
+    /* True if we opened this channel but server hasn't confirmed. */\r
+    int halfopen;\r
+    /*\r
+     * In SSH-1, this value contains four bits:\r
+     * \r
+     *   1   We have sent SSH1_MSG_CHANNEL_CLOSE.\r
+     *   2   We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.\r
+     *   4   We have received SSH1_MSG_CHANNEL_CLOSE.\r
+     *   8   We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.\r
+     * \r
+     * A channel is completely finished with when all four bits are set.\r
+     */\r
+    int closes;\r
+\r
+    /*\r
+     * This flag indicates that a close is pending on the outgoing\r
+     * side of the channel: that is, wherever we're getting the data\r
+     * for this channel has sent us some data followed by EOF. We\r
+     * can't actually close the channel until we've finished sending\r
+     * the data, so we set this flag instead to remind us to\r
+     * initiate the closing process once our buffer is clear.\r
+     */\r
+    int pending_close;\r
+\r
+    /*\r
+     * True if this channel is causing the underlying connection to be\r
+     * throttled.\r
+     */\r
+    int throttling_conn;\r
+    union {\r
+       struct ssh2_data_channel {\r
+           bufchain outbuffer;\r
+           unsigned remwindow, remmaxpkt;\r
+           /* locwindow is signed so we can cope with excess data. */\r
+           int locwindow, locmaxwin;\r
+           /*\r
+            * remlocwin is the amount of local window that we think\r
+            * the remote end had available to it after it sent the\r
+            * last data packet or window adjust ack.\r
+            */\r
+           int remlocwin;\r
+           /*\r
+            * These store the list of window adjusts that haven't\r
+            * been acked.\r
+            */\r
+           struct winadj *winadj_head, *winadj_tail;\r
+           enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state;\r
+       } v2;\r
+    } v;\r
+    union {\r
+       struct ssh_agent_channel {\r
+           unsigned char *message;\r
+           unsigned char msglen[4];\r
+           unsigned lensofar, totallen;\r
+       } a;\r
+       struct ssh_x11_channel {\r
+           Socket s;\r
+       } x11;\r
+       struct ssh_pfd_channel {\r
+           Socket s;\r
+       } pfd;\r
+    } u;\r
+};\r
+\r
+/*\r
+ * 2-3-4 tree storing remote->local port forwardings. SSH-1 and SSH-2\r
+ * use this structure in different ways, reflecting SSH-2's\r
+ * altogether saner approach to port forwarding.\r
+ * \r
+ * In SSH-1, you arrange a remote forwarding by sending the server\r
+ * the remote port number, and the local destination host:port.\r
+ * When a connection comes in, the server sends you back that\r
+ * host:port pair, and you connect to it. This is a ready-made\r
+ * security hole if you're not on the ball: a malicious server\r
+ * could send you back _any_ host:port pair, so if you trustingly\r
+ * connect to the address it gives you then you've just opened the\r
+ * entire inside of your corporate network just by connecting\r
+ * through it to a dodgy SSH server. Hence, we must store a list of\r
+ * host:port pairs we _are_ trying to forward to, and reject a\r
+ * connection request from the server if it's not in the list.\r
+ * \r
+ * In SSH-2, each side of the connection minds its own business and\r
+ * doesn't send unnecessary information to the other. You arrange a\r
+ * remote forwarding by sending the server just the remote port\r
+ * number. When a connection comes in, the server tells you which\r
+ * of its ports was connected to; and _you_ have to remember what\r
+ * local host:port pair went with that port number.\r
+ * \r
+ * Hence, in SSH-1 this structure is indexed by destination\r
+ * host:port pair, whereas in SSH-2 it is indexed by source port.\r
+ */\r
+struct ssh_portfwd; /* forward declaration */\r
+\r
+struct ssh_rportfwd {\r
+    unsigned sport, dport;\r
+    char dhost[256];\r
+    char *sportdesc;\r
+    struct ssh_portfwd *pfrec;\r
+};\r
+#define free_rportfwd(pf) ( \\r
+    ((pf) ? (sfree((pf)->sportdesc)) : (void)0 ), sfree(pf) )\r
+\r
+/*\r
+ * Separately to the rportfwd tree (which is for looking up port\r
+ * open requests from the server), a tree of _these_ structures is\r
+ * used to keep track of all the currently open port forwardings,\r
+ * so that we can reconfigure in mid-session if the user requests\r
+ * it.\r
+ */\r
+struct ssh_portfwd {\r
+    enum { DESTROY, KEEP, CREATE } status;\r
+    int type;\r
+    unsigned sport, dport;\r
+    char *saddr, *daddr;\r
+    char *sserv, *dserv;\r
+    struct ssh_rportfwd *remote;\r
+    int addressfamily;\r
+    void *local;\r
+};\r
+#define free_portfwd(pf) ( \\r
+    ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \\r
+            sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )\r
+\r
+struct Packet {\r
+    long length;           /* length of `data' actually used */\r
+    long forcepad;         /* SSH-2: force padding to at least this length */\r
+    int type;              /* only used for incoming packets */\r
+    unsigned long sequence; /* SSH-2 incoming sequence number */\r
+    unsigned char *data;    /* allocated storage */\r
+    unsigned char *body;    /* offset of payload within `data' */\r
+    long savedpos;         /* temporary index into `data' (for strings) */\r
+    long maxlen;           /* amount of storage allocated for `data' */\r
+    long encrypted_len;            /* for SSH-2 total-size counting */\r
+\r
+    /*\r
+     * State associated with packet logging\r
+     */\r
+    int logmode;\r
+    int nblanks;\r
+    struct logblank_t *blanks;\r
+};\r
+\r
+static void ssh1_protocol(Ssh ssh, void *vin, int inlen,\r
+                         struct Packet *pktin);\r
+static void ssh2_protocol(Ssh ssh, void *vin, int inlen,\r
+                         struct Packet *pktin);\r
+static void ssh1_protocol_setup(Ssh ssh);\r
+static void ssh2_protocol_setup(Ssh ssh);\r
+static void ssh_size(void *handle, int width, int height);\r
+static void ssh_special(void *handle, Telnet_Special);\r
+static int ssh2_try_send(struct ssh_channel *c);\r
+static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);\r
+static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);\r
+static void ssh2_set_window(struct ssh_channel *c, int newwin);\r
+static int ssh_sendbuffer(void *handle);\r
+static int ssh_do_close(Ssh ssh, int notify_exit);\r
+static unsigned long ssh_pkt_getuint32(struct Packet *pkt);\r
+static int ssh2_pkt_getbool(struct Packet *pkt);\r
+static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);\r
+static void ssh2_timer(void *ctx, long now);\r
+static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,\r
+                            struct Packet *pktin);\r
+\r
+struct rdpkt1_state_tag {\r
+    long len, pad, biglen, to_read;\r
+    unsigned long realcrc, gotcrc;\r
+    unsigned char *p;\r
+    int i;\r
+    int chunk;\r
+    struct Packet *pktin;\r
+};\r
+\r
+struct rdpkt2_state_tag {\r
+    long len, pad, payload, packetlen, maclen;\r
+    int i;\r
+    int cipherblk;\r
+    unsigned long incoming_sequence;\r
+    struct Packet *pktin;\r
+};\r
+\r
+typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin);\r
+typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx);\r
+\r
+struct queued_handler;\r
+struct queued_handler {\r
+    int msg1, msg2;\r
+    chandler_fn_t handler;\r
+    void *ctx;\r
+    struct queued_handler *next;\r
+};\r
+\r
+struct ssh_tag {\r
+    const struct plug_function_table *fn;\r
+    /* the above field _must_ be first in the structure */\r
+\r
+    char *v_c, *v_s;\r
+    void *exhash;\r
+\r
+    Socket s;\r
+\r
+    void *ldisc;\r
+    void *logctx;\r
+\r
+    unsigned char session_key[32];\r
+    int v1_compressing;\r
+    int v1_remote_protoflags;\r
+    int v1_local_protoflags;\r
+    int agentfwd_enabled;\r
+    int X11_fwd_enabled;\r
+    int remote_bugs;\r
+    const struct ssh_cipher *cipher;\r
+    void *v1_cipher_ctx;\r
+    void *crcda_ctx;\r
+    const struct ssh2_cipher *cscipher, *sccipher;\r
+    void *cs_cipher_ctx, *sc_cipher_ctx;\r
+    const struct ssh_mac *csmac, *scmac;\r
+    void *cs_mac_ctx, *sc_mac_ctx;\r
+    const struct ssh_compress *cscomp, *sccomp;\r
+    void *cs_comp_ctx, *sc_comp_ctx;\r
+    const struct ssh_kex *kex;\r
+    const struct ssh_signkey *hostkey;\r
+    unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN];\r
+    int v2_session_id_len;\r
+    void *kex_ctx;\r
+\r
+    char *savedhost;\r
+    int savedport;\r
+    int send_ok;\r
+    int echoing, editing;\r
+\r
+    void *frontend;\r
+\r
+    int ospeed, ispeed;                       /* temporaries */\r
+    int term_width, term_height;\r
+\r
+    tree234 *channels;                /* indexed by local id */\r
+    struct ssh_channel *mainchan;      /* primary session channel */\r
+    int ncmode;                               /* is primary channel direct-tcpip? */\r
+    int exitcode;\r
+    int close_expected;\r
+    int clean_exit;\r
+\r
+    tree234 *rportfwds, *portfwds;\r
+\r
+    enum {\r
+       SSH_STATE_PREPACKET,\r
+       SSH_STATE_BEFORE_SIZE,\r
+       SSH_STATE_INTERMED,\r
+       SSH_STATE_SESSION,\r
+       SSH_STATE_CLOSED\r
+    } state;\r
+\r
+    int size_needed, eof_needed;\r
+\r
+    struct Packet **queue;\r
+    int queuelen, queuesize;\r
+    int queueing;\r
+    unsigned char *deferred_send_data;\r
+    int deferred_len, deferred_size;\r
+\r
+    /*\r
+     * Gross hack: pscp will try to start SFTP but fall back to\r
+     * scp1 if that fails. This variable is the means by which\r
+     * scp.c can reach into the SSH code and find out which one it\r
+     * got.\r
+     */\r
+    int fallback_cmd;\r
+\r
+    bufchain banner;   /* accumulates banners during do_ssh2_authconn */\r
+\r
+    Pkt_KCtx pkt_kctx;\r
+    Pkt_ACtx pkt_actx;\r
+\r
+    struct X11Display *x11disp;\r
+\r
+    int version;\r
+    int conn_throttle_count;\r
+    int overall_bufsize;\r
+    int throttled_all;\r
+    int v1_stdout_throttling;\r
+    unsigned long v2_outgoing_sequence;\r
+\r
+    int ssh1_rdpkt_crstate;\r
+    int ssh2_rdpkt_crstate;\r
+    int do_ssh_init_crstate;\r
+    int ssh_gotdata_crstate;\r
+    int do_ssh1_login_crstate;\r
+    int do_ssh1_connection_crstate;\r
+    int do_ssh2_transport_crstate;\r
+    int do_ssh2_authconn_crstate;\r
+\r
+    void *do_ssh_init_state;\r
+    void *do_ssh1_login_state;\r
+    void *do_ssh2_transport_state;\r
+    void *do_ssh2_authconn_state;\r
+\r
+    struct rdpkt1_state_tag rdpkt1_state;\r
+    struct rdpkt2_state_tag rdpkt2_state;\r
+\r
+    /* SSH-1 and SSH-2 use this for different things, but both use it */\r
+    int protocol_initial_phase_done;\r
+\r
+    void (*protocol) (Ssh ssh, void *vin, int inlen,\r
+                     struct Packet *pkt);\r
+    struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);\r
+\r
+    /*\r
+     * We maintain a full _copy_ of a Config structure here, not\r
+     * merely a pointer to it. That way, when we're passed a new\r
+     * one for reconfiguration, we can check the differences and\r
+     * potentially reconfigure port forwardings etc in mid-session.\r
+     */\r
+    Config cfg;\r
+\r
+    /*\r
+     * Used to transfer data back from async callbacks.\r
+     */\r
+    void *agent_response;\r
+    int agent_response_len;\r
+    int user_response;\r
+\r
+    /*\r
+     * The SSH connection can be set as `frozen', meaning we are\r
+     * not currently accepting incoming data from the network. This\r
+     * is slightly more serious than setting the _socket_ as\r
+     * frozen, because we may already have had data passed to us\r
+     * from the network which we need to delay processing until\r
+     * after the freeze is lifted, so we also need a bufchain to\r
+     * store that data.\r
+     */\r
+    int frozen;\r
+    bufchain queued_incoming_data;\r
+\r
+    /*\r
+     * Dispatch table for packet types that we may have to deal\r
+     * with at any time.\r
+     */\r
+    handler_fn_t packet_dispatch[256];\r
+\r
+    /*\r
+     * Queues of one-off handler functions for success/failure\r
+     * indications from a request.\r
+     */\r
+    struct queued_handler *qhead, *qtail;\r
+\r
+    /*\r
+     * This module deals with sending keepalives.\r
+     */\r
+    Pinger pinger;\r
+\r
+    /*\r
+     * Track incoming and outgoing data sizes and time, for\r
+     * size-based rekeys.\r
+     */\r
+    unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;\r
+    unsigned long max_data_size;\r
+    int kex_in_progress;\r
+    long next_rekey, last_rekey;\r
+    char *deferred_rekey_reason;    /* points to STATIC string; don't free */\r
+\r
+    /*\r
+     * Fully qualified host name, which we need if doing GSSAPI.\r
+     */\r
+    char *fullhostname;\r
+\r
+#ifndef NO_GSSAPI\r
+    /*\r
+     * GSSAPI libraries for this session.\r
+     */\r
+    struct ssh_gss_liblist *gsslibs;\r
+#endif\r
+};\r
+\r
+#define logevent(s) logevent(ssh->frontend, s)\r
+\r
+/* logevent, only printf-formatted. */\r
+static void logeventf(Ssh ssh, const char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *buf;\r
+\r
+    va_start(ap, fmt);\r
+    buf = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    logevent(buf);\r
+    sfree(buf);\r
+}\r
+\r
+#define bombout(msg) \\r
+    do { \\r
+        char *text = dupprintf msg; \\r
+       ssh_do_close(ssh, FALSE); \\r
+        logevent(text); \\r
+        connection_fatal(ssh->frontend, "%s", text); \\r
+        sfree(text); \\r
+    } while (0)\r
+\r
+/* Functions to leave bits out of the SSH packet log file. */\r
+\r
+static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype)\r
+{\r
+    if (ssh->cfg.logomitpass)\r
+       pkt->logmode = blanktype;\r
+}\r
+\r
+static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype)\r
+{\r
+    if (ssh->cfg.logomitdata)\r
+       pkt->logmode = blanktype;\r
+}\r
+\r
+static void end_log_omission(Ssh ssh, struct Packet *pkt)\r
+{\r
+    pkt->logmode = PKTLOG_EMIT;\r
+}\r
+\r
+/* Helper function for common bits of parsing cfg.ttymodes. */\r
+static void parse_ttymodes(Ssh ssh, char *modes,\r
+                          void (*do_mode)(void *data, char *mode, char *val),\r
+                          void *data)\r
+{\r
+    while (*modes) {\r
+       char *t = strchr(modes, '\t');\r
+       char *m = snewn(t-modes+1, char);\r
+       char *val;\r
+       strncpy(m, modes, t-modes);\r
+       m[t-modes] = '\0';\r
+       if (*(t+1) == 'A')\r
+           val = get_ttymode(ssh->frontend, m);\r
+       else\r
+           val = dupstr(t+2);\r
+       if (val)\r
+           do_mode(data, m, val);\r
+       sfree(m);\r
+       sfree(val);\r
+       modes += strlen(modes) + 1;\r
+    }\r
+}\r
+\r
+static int ssh_channelcmp(void *av, void *bv)\r
+{\r
+    struct ssh_channel *a = (struct ssh_channel *) av;\r
+    struct ssh_channel *b = (struct ssh_channel *) bv;\r
+    if (a->localid < b->localid)\r
+       return -1;\r
+    if (a->localid > b->localid)\r
+       return +1;\r
+    return 0;\r
+}\r
+static int ssh_channelfind(void *av, void *bv)\r
+{\r
+    unsigned *a = (unsigned *) av;\r
+    struct ssh_channel *b = (struct ssh_channel *) bv;\r
+    if (*a < b->localid)\r
+       return -1;\r
+    if (*a > b->localid)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int ssh_rportcmp_ssh1(void *av, void *bv)\r
+{\r
+    struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;\r
+    struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;\r
+    int i;\r
+    if ( (i = strcmp(a->dhost, b->dhost)) != 0)\r
+       return i < 0 ? -1 : +1;\r
+    if (a->dport > b->dport)\r
+       return +1;\r
+    if (a->dport < b->dport)\r
+       return -1;\r
+    return 0;\r
+}\r
+\r
+static int ssh_rportcmp_ssh2(void *av, void *bv)\r
+{\r
+    struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;\r
+    struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;\r
+\r
+    if (a->sport > b->sport)\r
+       return +1;\r
+    if (a->sport < b->sport)\r
+       return -1;\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Special form of strcmp which can cope with NULL inputs. NULL is\r
+ * defined to sort before even the empty string.\r
+ */\r
+static int nullstrcmp(const char *a, const char *b)\r
+{\r
+    if (a == NULL && b == NULL)\r
+       return 0;\r
+    if (a == NULL)\r
+       return -1;\r
+    if (b == NULL)\r
+       return +1;\r
+    return strcmp(a, b);\r
+}\r
+\r
+static int ssh_portcmp(void *av, void *bv)\r
+{\r
+    struct ssh_portfwd *a = (struct ssh_portfwd *) av;\r
+    struct ssh_portfwd *b = (struct ssh_portfwd *) bv;\r
+    int i;\r
+    if (a->type > b->type)\r
+       return +1;\r
+    if (a->type < b->type)\r
+       return -1;\r
+    if (a->addressfamily > b->addressfamily)\r
+       return +1;\r
+    if (a->addressfamily < b->addressfamily)\r
+       return -1;\r
+    if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)\r
+       return i < 0 ? -1 : +1;\r
+    if (a->sport > b->sport)\r
+       return +1;\r
+    if (a->sport < b->sport)\r
+       return -1;\r
+    if (a->type != 'D') {\r
+       if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)\r
+           return i < 0 ? -1 : +1;\r
+       if (a->dport > b->dport)\r
+           return +1;\r
+       if (a->dport < b->dport)\r
+           return -1;\r
+    }\r
+    return 0;\r
+}\r
+\r
+static int alloc_channel_id(Ssh ssh)\r
+{\r
+    const unsigned CHANNEL_NUMBER_OFFSET = 256;\r
+    unsigned low, high, mid;\r
+    int tsize;\r
+    struct ssh_channel *c;\r
+\r
+    /*\r
+     * First-fit allocation of channel numbers: always pick the\r
+     * lowest unused one. To do this, binary-search using the\r
+     * counted B-tree to find the largest channel ID which is in a\r
+     * contiguous sequence from the beginning. (Precisely\r
+     * everything in that sequence must have ID equal to its tree\r
+     * index plus CHANNEL_NUMBER_OFFSET.)\r
+     */\r
+    tsize = count234(ssh->channels);\r
+\r
+    low = -1;\r
+    high = tsize;\r
+    while (high - low > 1) {\r
+       mid = (high + low) / 2;\r
+       c = index234(ssh->channels, mid);\r
+       if (c->localid == mid + CHANNEL_NUMBER_OFFSET)\r
+           low = mid;                 /* this one is fine */\r
+       else\r
+           high = mid;                /* this one is past it */\r
+    }\r
+    /*\r
+     * Now low points to either -1, or the tree index of the\r
+     * largest ID in the initial sequence.\r
+     */\r
+    {\r
+       unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET;\r
+       assert(NULL == find234(ssh->channels, &i, ssh_channelfind));\r
+    }\r
+    return low + 1 + CHANNEL_NUMBER_OFFSET;\r
+}\r
+\r
+static void c_write_stderr(int trusted, const char *buf, int len)\r
+{\r
+    int i;\r
+    for (i = 0; i < len; i++)\r
+       if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60)))\r
+           fputc(buf[i], stderr);\r
+}\r
+\r
+static void c_write(Ssh ssh, const char *buf, int len)\r
+{\r
+    if (flags & FLAG_STDERR)\r
+       c_write_stderr(1, buf, len);\r
+    else\r
+       from_backend(ssh->frontend, 1, buf, len);\r
+}\r
+\r
+static void c_write_untrusted(Ssh ssh, const char *buf, int len)\r
+{\r
+    if (flags & FLAG_STDERR)\r
+       c_write_stderr(0, buf, len);\r
+    else\r
+       from_backend_untrusted(ssh->frontend, buf, len);\r
+}\r
+\r
+static void c_write_str(Ssh ssh, const char *buf)\r
+{\r
+    c_write(ssh, buf, strlen(buf));\r
+}\r
+\r
+static void ssh_free_packet(struct Packet *pkt)\r
+{\r
+    sfree(pkt->data);\r
+    sfree(pkt);\r
+}\r
+static struct Packet *ssh_new_packet(void)\r
+{\r
+    struct Packet *pkt = snew(struct Packet);\r
+\r
+    pkt->body = pkt->data = NULL;\r
+    pkt->maxlen = 0;\r
+    pkt->logmode = PKTLOG_EMIT;\r
+    pkt->nblanks = 0;\r
+    pkt->blanks = NULL;\r
+\r
+    return pkt;\r
+}\r
+\r
+/*\r
+ * Collect incoming data in the incoming packet buffer.\r
+ * Decipher and verify the packet when it is completely read.\r
+ * Drop SSH1_MSG_DEBUG and SSH1_MSG_IGNORE packets.\r
+ * Update the *data and *datalen variables.\r
+ * Return a Packet structure when a packet is completed.\r
+ */\r
+static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)\r
+{\r
+    struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;\r
+\r
+    crBegin(ssh->ssh1_rdpkt_crstate);\r
+\r
+    st->pktin = ssh_new_packet();\r
+\r
+    st->pktin->type = 0;\r
+    st->pktin->length = 0;\r
+\r
+    for (st->i = st->len = 0; st->i < 4; st->i++) {\r
+       while ((*datalen) == 0)\r
+           crReturn(NULL);\r
+       st->len = (st->len << 8) + **data;\r
+       (*data)++, (*datalen)--;\r
+    }\r
+\r
+    st->pad = 8 - (st->len % 8);\r
+    st->biglen = st->len + st->pad;\r
+    st->pktin->length = st->len - 5;\r
+\r
+    if (st->biglen < 0) {\r
+        bombout(("Extremely large packet length from server suggests"\r
+                " data stream corruption"));\r
+       ssh_free_packet(st->pktin);\r
+        crStop(NULL);\r
+    }\r
+\r
+    st->pktin->maxlen = st->biglen;\r
+    st->pktin->data = snewn(st->biglen + APIEXTRA, unsigned char);\r
+\r
+    st->to_read = st->biglen;\r
+    st->p = st->pktin->data;\r
+    while (st->to_read > 0) {\r
+       st->chunk = st->to_read;\r
+       while ((*datalen) == 0)\r
+           crReturn(NULL);\r
+       if (st->chunk > (*datalen))\r
+           st->chunk = (*datalen);\r
+       memcpy(st->p, *data, st->chunk);\r
+       *data += st->chunk;\r
+       *datalen -= st->chunk;\r
+       st->p += st->chunk;\r
+       st->to_read -= st->chunk;\r
+    }\r
+\r
+    if (ssh->cipher && detect_attack(ssh->crcda_ctx, st->pktin->data,\r
+                                    st->biglen, NULL)) {\r
+        bombout(("Network attack (CRC compensation) detected!"));\r
+       ssh_free_packet(st->pktin);\r
+        crStop(NULL);\r
+    }\r
+\r
+    if (ssh->cipher)\r
+       ssh->cipher->decrypt(ssh->v1_cipher_ctx, st->pktin->data, st->biglen);\r
+\r
+    st->realcrc = crc32_compute(st->pktin->data, st->biglen - 4);\r
+    st->gotcrc = GET_32BIT(st->pktin->data + st->biglen - 4);\r
+    if (st->gotcrc != st->realcrc) {\r
+       bombout(("Incorrect CRC received on packet"));\r
+       ssh_free_packet(st->pktin);\r
+       crStop(NULL);\r
+    }\r
+\r
+    st->pktin->body = st->pktin->data + st->pad + 1;\r
+    st->pktin->savedpos = 0;\r
+\r
+    if (ssh->v1_compressing) {\r
+       unsigned char *decompblk;\r
+       int decomplen;\r
+       if (!zlib_decompress_block(ssh->sc_comp_ctx,\r
+                                  st->pktin->body - 1, st->pktin->length + 1,\r
+                                  &decompblk, &decomplen)) {\r
+           bombout(("Zlib decompression encountered invalid data"));\r
+           ssh_free_packet(st->pktin);\r
+           crStop(NULL);\r
+       }\r
+\r
+       if (st->pktin->maxlen < st->pad + decomplen) {\r
+           st->pktin->maxlen = st->pad + decomplen;\r
+           st->pktin->data = sresize(st->pktin->data,\r
+                                     st->pktin->maxlen + APIEXTRA,\r
+                                     unsigned char);\r
+           st->pktin->body = st->pktin->data + st->pad + 1;\r
+       }\r
+\r
+       memcpy(st->pktin->body - 1, decompblk, decomplen);\r
+       sfree(decompblk);\r
+       st->pktin->length = decomplen - 1;\r
+    }\r
+\r
+    st->pktin->type = st->pktin->body[-1];\r
+\r
+    /*\r
+     * Log incoming packet, possibly omitting sensitive fields.\r
+     */\r
+    if (ssh->logctx) {\r
+       int nblanks = 0;\r
+       struct logblank_t blank;\r
+       if (ssh->cfg.logomitdata) {\r
+           int do_blank = FALSE, blank_prefix = 0;\r
+           /* "Session data" packets - omit the data field */\r
+           if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) ||\r
+               (st->pktin->type == SSH1_SMSG_STDERR_DATA)) {\r
+               do_blank = TRUE; blank_prefix = 4;\r
+           } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) {\r
+               do_blank = TRUE; blank_prefix = 8;\r
+           }\r
+           if (do_blank) {\r
+               blank.offset = blank_prefix;\r
+               blank.len = st->pktin->length;\r
+               blank.type = PKTLOG_OMIT;\r
+               nblanks = 1;\r
+           }\r
+       }\r
+       log_packet(ssh->logctx,\r
+                  PKT_INCOMING, st->pktin->type,\r
+                  ssh1_pkt_type(st->pktin->type),\r
+                  st->pktin->body, st->pktin->length,\r
+                  nblanks, &blank, NULL);\r
+    }\r
+\r
+    crFinish(st->pktin);\r
+}\r
+\r
+static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)\r
+{\r
+    struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;\r
+\r
+    crBegin(ssh->ssh2_rdpkt_crstate);\r
+\r
+    st->pktin = ssh_new_packet();\r
+\r
+    st->pktin->type = 0;\r
+    st->pktin->length = 0;\r
+    if (ssh->sccipher)\r
+       st->cipherblk = ssh->sccipher->blksize;\r
+    else\r
+       st->cipherblk = 8;\r
+    if (st->cipherblk < 8)\r
+       st->cipherblk = 8;\r
+    st->maclen = ssh->scmac ? ssh->scmac->len : 0;\r
+\r
+    if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_IS_CBC) &&\r
+       ssh->scmac) {\r
+       /*\r
+        * When dealing with a CBC-mode cipher, we want to avoid the\r
+        * possibility of an attacker's tweaking the ciphertext stream\r
+        * so as to cause us to feed the same block to the block\r
+        * cipher more than once and thus leak information\r
+        * (VU#958563).  The way we do this is not to take any\r
+        * decisions on the basis of anything we've decrypted until\r
+        * we've verified it with a MAC.  That includes the packet\r
+        * length, so we just read data and check the MAC repeatedly,\r
+        * and when the MAC passes, see if the length we've got is\r
+        * plausible.\r
+        */\r
+\r
+       /* May as well allocate the whole lot now. */\r
+       st->pktin->data = snewn(OUR_V2_PACKETLIMIT + st->maclen + APIEXTRA,\r
+                               unsigned char);\r
+\r
+       /* Read an amount corresponding to the MAC. */\r
+       for (st->i = 0; st->i < st->maclen; st->i++) {\r
+           while ((*datalen) == 0)\r
+               crReturn(NULL);\r
+           st->pktin->data[st->i] = *(*data)++;\r
+           (*datalen)--;\r
+       }\r
+\r
+       st->packetlen = 0;\r
+       {\r
+           unsigned char seq[4];\r
+           ssh->scmac->start(ssh->sc_mac_ctx);\r
+           PUT_32BIT(seq, st->incoming_sequence);\r
+           ssh->scmac->bytes(ssh->sc_mac_ctx, seq, 4);\r
+       }\r
+\r
+       for (;;) { /* Once around this loop per cipher block. */\r
+           /* Read another cipher-block's worth, and tack it onto the end. */\r
+           for (st->i = 0; st->i < st->cipherblk; st->i++) {\r
+               while ((*datalen) == 0)\r
+                   crReturn(NULL);\r
+               st->pktin->data[st->packetlen+st->maclen+st->i] = *(*data)++;\r
+               (*datalen)--;\r
+           }\r
+           /* Decrypt one more block (a little further back in the stream). */\r
+           ssh->sccipher->decrypt(ssh->sc_cipher_ctx,\r
+                                  st->pktin->data + st->packetlen,\r
+                                  st->cipherblk);\r
+           /* Feed that block to the MAC. */\r
+           ssh->scmac->bytes(ssh->sc_mac_ctx,\r
+                             st->pktin->data + st->packetlen, st->cipherblk);\r
+           st->packetlen += st->cipherblk;\r
+           /* See if that gives us a valid packet. */\r
+           if (ssh->scmac->verresult(ssh->sc_mac_ctx,\r
+                                     st->pktin->data + st->packetlen) &&\r
+               (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen)\r
+                   break;\r
+           if (st->packetlen >= OUR_V2_PACKETLIMIT) {\r
+               bombout(("No valid incoming packet found"));\r
+               ssh_free_packet(st->pktin);\r
+               crStop(NULL);\r
+           }       \r
+       }\r
+       st->pktin->maxlen = st->packetlen + st->maclen;\r
+       st->pktin->data = sresize(st->pktin->data,\r
+                                 st->pktin->maxlen + APIEXTRA,\r
+                                 unsigned char);\r
+    } else {\r
+       st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);\r
+\r
+       /*\r
+        * Acquire and decrypt the first block of the packet. This will\r
+        * contain the length and padding details.\r
+        */\r
+       for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {\r
+           while ((*datalen) == 0)\r
+               crReturn(NULL);\r
+           st->pktin->data[st->i] = *(*data)++;\r
+           (*datalen)--;\r
+       }\r
+\r
+       if (ssh->sccipher)\r
+           ssh->sccipher->decrypt(ssh->sc_cipher_ctx,\r
+                                  st->pktin->data, st->cipherblk);\r
+\r
+       /*\r
+        * Now get the length figure.\r
+        */\r
+       st->len = GET_32BIT(st->pktin->data);\r
+\r
+       /*\r
+        * _Completely_ silly lengths should be stomped on before they\r
+        * do us any more damage.\r
+        */\r
+       if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||\r
+           (st->len + 4) % st->cipherblk != 0) {\r
+           bombout(("Incoming packet was garbled on decryption"));\r
+           ssh_free_packet(st->pktin);\r
+           crStop(NULL);\r
+       }\r
+\r
+       /*\r
+        * So now we can work out the total packet length.\r
+        */\r
+       st->packetlen = st->len + 4;\r
+\r
+       /*\r
+        * Allocate memory for the rest of the packet.\r
+        */\r
+       st->pktin->maxlen = st->packetlen + st->maclen;\r
+       st->pktin->data = sresize(st->pktin->data,\r
+                                 st->pktin->maxlen + APIEXTRA,\r
+                                 unsigned char);\r
+\r
+       /*\r
+        * Read and decrypt the remainder of the packet.\r
+        */\r
+       for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;\r
+            st->i++) {\r
+           while ((*datalen) == 0)\r
+               crReturn(NULL);\r
+           st->pktin->data[st->i] = *(*data)++;\r
+           (*datalen)--;\r
+       }\r
+       /* Decrypt everything _except_ the MAC. */\r
+       if (ssh->sccipher)\r
+           ssh->sccipher->decrypt(ssh->sc_cipher_ctx,\r
+                                  st->pktin->data + st->cipherblk,\r
+                                  st->packetlen - st->cipherblk);\r
+\r
+       /*\r
+        * Check the MAC.\r
+        */\r
+       if (ssh->scmac\r
+           && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data,\r
+                                  st->len + 4, st->incoming_sequence)) {\r
+           bombout(("Incorrect MAC received on packet"));\r
+           ssh_free_packet(st->pktin);\r
+           crStop(NULL);\r
+       }\r
+    }\r
+    /* Get and sanity-check the amount of random padding. */\r
+    st->pad = st->pktin->data[4];\r
+    if (st->pad < 4 || st->len - st->pad < 1) {\r
+       bombout(("Invalid padding length on received packet"));\r
+       ssh_free_packet(st->pktin);\r
+       crStop(NULL);\r
+    }\r
+    /*\r
+     * This enables us to deduce the payload length.\r
+     */\r
+    st->payload = st->len - st->pad - 1;\r
+\r
+    st->pktin->length = st->payload + 5;\r
+    st->pktin->encrypted_len = st->packetlen;\r
+\r
+    st->pktin->sequence = st->incoming_sequence++;\r
+\r
+    /*\r
+     * Decompress packet payload.\r
+     */\r
+    {\r
+       unsigned char *newpayload;\r
+       int newlen;\r
+       if (ssh->sccomp &&\r
+           ssh->sccomp->decompress(ssh->sc_comp_ctx,\r
+                                   st->pktin->data + 5, st->pktin->length - 5,\r
+                                   &newpayload, &newlen)) {\r
+           if (st->pktin->maxlen < newlen + 5) {\r
+               st->pktin->maxlen = newlen + 5;\r
+               st->pktin->data = sresize(st->pktin->data,\r
+                                         st->pktin->maxlen + APIEXTRA,\r
+                                         unsigned char);\r
+           }\r
+           st->pktin->length = 5 + newlen;\r
+           memcpy(st->pktin->data + 5, newpayload, newlen);\r
+           sfree(newpayload);\r
+       }\r
+    }\r
+\r
+    st->pktin->savedpos = 6;\r
+    st->pktin->body = st->pktin->data;\r
+    st->pktin->type = st->pktin->data[5];\r
+\r
+    /*\r
+     * Log incoming packet, possibly omitting sensitive fields.\r
+     */\r
+    if (ssh->logctx) {\r
+       int nblanks = 0;\r
+       struct logblank_t blank;\r
+       if (ssh->cfg.logomitdata) {\r
+           int do_blank = FALSE, blank_prefix = 0;\r
+           /* "Session data" packets - omit the data field */\r
+           if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) {\r
+               do_blank = TRUE; blank_prefix = 8;\r
+           } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {\r
+               do_blank = TRUE; blank_prefix = 12;\r
+           }\r
+           if (do_blank) {\r
+               blank.offset = blank_prefix;\r
+               blank.len = (st->pktin->length-6) - blank_prefix;\r
+               blank.type = PKTLOG_OMIT;\r
+               nblanks = 1;\r
+           }\r
+       }\r
+       log_packet(ssh->logctx, PKT_INCOMING, st->pktin->type,\r
+                  ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,\r
+                                st->pktin->type),\r
+                  st->pktin->data+6, st->pktin->length-6,\r
+                  nblanks, &blank, &st->pktin->sequence);\r
+    }\r
+\r
+    crFinish(st->pktin);\r
+}\r
+\r
+static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p)\r
+{\r
+    int pad, biglen, i, pktoffs;\r
+    unsigned long crc;\r
+#ifdef __SC__\r
+    /*\r
+     * XXX various versions of SC (including 8.8.4) screw up the\r
+     * register allocation in this function and use the same register\r
+     * (D6) for len and as a temporary, with predictable results.  The\r
+     * following sledgehammer prevents this.\r
+     */\r
+    volatile\r
+#endif\r
+    int len;\r
+\r
+    if (ssh->logctx)\r
+       log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],\r
+                  ssh1_pkt_type(pkt->data[12]),\r
+                  pkt->body, pkt->length - (pkt->body - pkt->data),\r
+                  pkt->nblanks, pkt->blanks, NULL);\r
+    sfree(pkt->blanks); pkt->blanks = NULL;\r
+    pkt->nblanks = 0;\r
+\r
+    if (ssh->v1_compressing) {\r
+       unsigned char *compblk;\r
+       int complen;\r
+       zlib_compress_block(ssh->cs_comp_ctx,\r
+                           pkt->data + 12, pkt->length - 12,\r
+                           &compblk, &complen);\r
+       ssh_pkt_ensure(pkt, complen + 2);   /* just in case it's got bigger */\r
+       memcpy(pkt->data + 12, compblk, complen);\r
+       sfree(compblk);\r
+       pkt->length = complen + 12;\r
+    }\r
+\r
+    ssh_pkt_ensure(pkt, pkt->length + 4); /* space for CRC */\r
+    pkt->length += 4;\r
+    len = pkt->length - 4 - 8; /* len(type+data+CRC) */\r
+    pad = 8 - (len % 8);\r
+    pktoffs = 8 - pad;\r
+    biglen = len + pad;                /* len(padding+type+data+CRC) */\r
+\r
+    for (i = pktoffs; i < 4+8; i++)\r
+       pkt->data[i] = random_byte();\r
+    crc = crc32_compute(pkt->data + pktoffs + 4, biglen - 4); /* all ex len */\r
+    PUT_32BIT(pkt->data + pktoffs + 4 + biglen - 4, crc);\r
+    PUT_32BIT(pkt->data + pktoffs, len);\r
+\r
+    if (ssh->cipher)\r
+       ssh->cipher->encrypt(ssh->v1_cipher_ctx,\r
+                            pkt->data + pktoffs + 4, biglen);\r
+\r
+    if (offset_p) *offset_p = pktoffs;\r
+    return biglen + 4;         /* len(length+padding+type+data+CRC) */\r
+}\r
+\r
+static int s_write(Ssh ssh, void *data, int len)\r
+{\r
+    if (ssh->logctx)\r
+       log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len,\r
+                  0, NULL, NULL);\r
+    return sk_write(ssh->s, (char *)data, len);\r
+}\r
+\r
+static void s_wrpkt(Ssh ssh, struct Packet *pkt)\r
+{\r
+    int len, backlog, offset;\r
+    len = s_wrpkt_prepare(ssh, pkt, &offset);\r
+    backlog = s_write(ssh, pkt->data + offset, len);\r
+    if (backlog > SSH_MAX_BACKLOG)\r
+       ssh_throttle_all(ssh, 1, backlog);\r
+    ssh_free_packet(pkt);\r
+}\r
+\r
+static void s_wrpkt_defer(Ssh ssh, struct Packet *pkt)\r
+{\r
+    int len, offset;\r
+    len = s_wrpkt_prepare(ssh, pkt, &offset);\r
+    if (ssh->deferred_len + len > ssh->deferred_size) {\r
+       ssh->deferred_size = ssh->deferred_len + len + 128;\r
+       ssh->deferred_send_data = sresize(ssh->deferred_send_data,\r
+                                         ssh->deferred_size,\r
+                                         unsigned char);\r
+    }\r
+    memcpy(ssh->deferred_send_data + ssh->deferred_len,\r
+          pkt->data + offset, len);\r
+    ssh->deferred_len += len;\r
+    ssh_free_packet(pkt);\r
+}\r
+\r
+/*\r
+ * Construct a SSH-1 packet with the specified contents.\r
+ * (This all-at-once interface used to be the only one, but now SSH-1\r
+ * packets can also be constructed incrementally.)\r
+ */\r
+static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)\r
+{\r
+    int argtype;\r
+    Bignum bn;\r
+    struct Packet *pkt;\r
+\r
+    pkt = ssh1_pkt_init(pkttype);\r
+\r
+    while ((argtype = va_arg(ap, int)) != PKT_END) {\r
+       unsigned char *argp, argchar;\r
+       char *sargp;\r
+       unsigned long argint;\r
+       int arglen;\r
+       switch (argtype) {\r
+         /* Actual fields in the packet */\r
+         case PKT_INT:\r
+           argint = va_arg(ap, int);\r
+           ssh_pkt_adduint32(pkt, argint);\r
+           break;\r
+         case PKT_CHAR:\r
+           argchar = (unsigned char) va_arg(ap, int);\r
+           ssh_pkt_addbyte(pkt, argchar);\r
+           break;\r
+         case PKT_DATA:\r
+           argp = va_arg(ap, unsigned char *);\r
+           arglen = va_arg(ap, int);\r
+           ssh_pkt_adddata(pkt, argp, arglen);\r
+           break;\r
+         case PKT_STR:\r
+           sargp = va_arg(ap, char *);\r
+           ssh_pkt_addstring(pkt, sargp);\r
+           break;\r
+         case PKT_BIGNUM:\r
+           bn = va_arg(ap, Bignum);\r
+           ssh1_pkt_addmp(pkt, bn);\r
+           break;\r
+         /* Tokens for modifications to packet logging */\r
+         case PKTT_PASSWORD:\r
+           dont_log_password(ssh, pkt, PKTLOG_BLANK);\r
+           break;\r
+         case PKTT_DATA:\r
+           dont_log_data(ssh, pkt, PKTLOG_OMIT);\r
+           break;\r
+         case PKTT_OTHER:\r
+           end_log_omission(ssh, pkt);\r
+           break;\r
+       }\r
+    }\r
+\r
+    return pkt;\r
+}\r
+\r
+static void send_packet(Ssh ssh, int pkttype, ...)\r
+{\r
+    struct Packet *pkt;\r
+    va_list ap;\r
+    va_start(ap, pkttype);\r
+    pkt = construct_packet(ssh, pkttype, ap);\r
+    va_end(ap);\r
+    s_wrpkt(ssh, pkt);\r
+}\r
+\r
+static void defer_packet(Ssh ssh, int pkttype, ...)\r
+{\r
+    struct Packet *pkt;\r
+    va_list ap;\r
+    va_start(ap, pkttype);\r
+    pkt = construct_packet(ssh, pkttype, ap);\r
+    va_end(ap);\r
+    s_wrpkt_defer(ssh, pkt);\r
+}\r
+\r
+static int ssh_versioncmp(char *a, char *b)\r
+{\r
+    char *ae, *be;\r
+    unsigned long av, bv;\r
+\r
+    av = strtoul(a, &ae, 10);\r
+    bv = strtoul(b, &be, 10);\r
+    if (av != bv)\r
+       return (av < bv ? -1 : +1);\r
+    if (*ae == '.')\r
+       ae++;\r
+    if (*be == '.')\r
+       be++;\r
+    av = strtoul(ae, &ae, 10);\r
+    bv = strtoul(be, &be, 10);\r
+    if (av != bv)\r
+       return (av < bv ? -1 : +1);\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Utility routines for putting an SSH-protocol `string' and\r
+ * `uint32' into a hash state.\r
+ */\r
+static void hash_string(const struct ssh_hash *h, void *s, void *str, int len)\r
+{\r
+    unsigned char lenblk[4];\r
+    PUT_32BIT(lenblk, len);\r
+    h->bytes(s, lenblk, 4);\r
+    h->bytes(s, str, len);\r
+}\r
+\r
+static void hash_uint32(const struct ssh_hash *h, void *s, unsigned i)\r
+{\r
+    unsigned char intblk[4];\r
+    PUT_32BIT(intblk, i);\r
+    h->bytes(s, intblk, 4);\r
+}\r
+\r
+/*\r
+ * Packet construction functions. Mostly shared between SSH-1 and SSH-2.\r
+ */\r
+static void ssh_pkt_ensure(struct Packet *pkt, int length)\r
+{\r
+    if (pkt->maxlen < length) {\r
+       unsigned char *body = pkt->body;\r
+       int offset = body ? body - pkt->data : 0;\r
+       pkt->maxlen = length + 256;\r
+       pkt->data = sresize(pkt->data, pkt->maxlen + APIEXTRA, unsigned char);\r
+       if (body) pkt->body = pkt->data + offset;\r
+    }\r
+}\r
+static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len)\r
+{\r
+    if (pkt->logmode != PKTLOG_EMIT) {\r
+       pkt->nblanks++;\r
+       pkt->blanks = sresize(pkt->blanks, pkt->nblanks, struct logblank_t);\r
+       assert(pkt->body);\r
+       pkt->blanks[pkt->nblanks-1].offset = pkt->length -\r
+                                            (pkt->body - pkt->data);\r
+       pkt->blanks[pkt->nblanks-1].len = len;\r
+       pkt->blanks[pkt->nblanks-1].type = pkt->logmode;\r
+    }\r
+    pkt->length += len;\r
+    ssh_pkt_ensure(pkt, pkt->length);\r
+    memcpy(pkt->data + pkt->length - len, data, len);\r
+}\r
+static void ssh_pkt_addbyte(struct Packet *pkt, unsigned char byte)\r
+{\r
+    ssh_pkt_adddata(pkt, &byte, 1);\r
+}\r
+static void ssh2_pkt_addbool(struct Packet *pkt, unsigned char value)\r
+{\r
+    ssh_pkt_adddata(pkt, &value, 1);\r
+}\r
+static void ssh_pkt_adduint32(struct Packet *pkt, unsigned long value)\r
+{\r
+    unsigned char x[4];\r
+    PUT_32BIT(x, value);\r
+    ssh_pkt_adddata(pkt, x, 4);\r
+}\r
+static void ssh_pkt_addstring_start(struct Packet *pkt)\r
+{\r
+    ssh_pkt_adduint32(pkt, 0);\r
+    pkt->savedpos = pkt->length;\r
+}\r
+static void ssh_pkt_addstring_str(struct Packet *pkt, char *data)\r
+{\r
+    ssh_pkt_adddata(pkt, data, strlen(data));\r
+    PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);\r
+}\r
+static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len)\r
+{\r
+    ssh_pkt_adddata(pkt, data, len);\r
+    PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);\r
+}\r
+static void ssh_pkt_addstring(struct Packet *pkt, char *data)\r
+{\r
+    ssh_pkt_addstring_start(pkt);\r
+    ssh_pkt_addstring_str(pkt, data);\r
+}\r
+static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b)\r
+{\r
+    int len = ssh1_bignum_length(b);\r
+    unsigned char *data = snewn(len, unsigned char);\r
+    (void) ssh1_write_bignum(data, b);\r
+    ssh_pkt_adddata(pkt, data, len);\r
+    sfree(data);\r
+}\r
+static unsigned char *ssh2_mpint_fmt(Bignum b, int *len)\r
+{\r
+    unsigned char *p;\r
+    int i, n = (bignum_bitcount(b) + 7) / 8;\r
+    p = snewn(n + 1, unsigned char);\r
+    p[0] = 0;\r
+    for (i = 1; i <= n; i++)\r
+       p[i] = bignum_byte(b, n - i);\r
+    i = 0;\r
+    while (i <= n && p[i] == 0 && (p[i + 1] & 0x80) == 0)\r
+       i++;\r
+    memmove(p, p + i, n + 1 - i);\r
+    *len = n + 1 - i;\r
+    return p;\r
+}\r
+static void ssh2_pkt_addmp(struct Packet *pkt, Bignum b)\r
+{\r
+    unsigned char *p;\r
+    int len;\r
+    p = ssh2_mpint_fmt(b, &len);\r
+    ssh_pkt_addstring_start(pkt);\r
+    ssh_pkt_addstring_data(pkt, (char *)p, len);\r
+    sfree(p);\r
+}\r
+\r
+static struct Packet *ssh1_pkt_init(int pkt_type)\r
+{\r
+    struct Packet *pkt = ssh_new_packet();\r
+    pkt->length = 4 + 8;           /* space for length + max padding */\r
+    ssh_pkt_addbyte(pkt, pkt_type);\r
+    pkt->body = pkt->data + pkt->length;\r
+    return pkt;\r
+}\r
+\r
+/* For legacy code (SSH-1 and -2 packet construction used to be separate) */\r
+#define ssh2_pkt_ensure(pkt, length) ssh_pkt_ensure(pkt, length)\r
+#define ssh2_pkt_adddata(pkt, data, len) ssh_pkt_adddata(pkt, data, len)\r
+#define ssh2_pkt_addbyte(pkt, byte) ssh_pkt_addbyte(pkt, byte)\r
+#define ssh2_pkt_adduint32(pkt, value) ssh_pkt_adduint32(pkt, value)\r
+#define ssh2_pkt_addstring_start(pkt) ssh_pkt_addstring_start(pkt)\r
+#define ssh2_pkt_addstring_str(pkt, data) ssh_pkt_addstring_str(pkt, data)\r
+#define ssh2_pkt_addstring_data(pkt, data, len) ssh_pkt_addstring_data(pkt, data, len)\r
+#define ssh2_pkt_addstring(pkt, data) ssh_pkt_addstring(pkt, data)\r
+\r
+static struct Packet *ssh2_pkt_init(int pkt_type)\r
+{\r
+    struct Packet *pkt = ssh_new_packet();\r
+    pkt->length = 5; /* space for packet length + padding length */\r
+    pkt->forcepad = 0;\r
+    ssh_pkt_addbyte(pkt, (unsigned char) pkt_type);\r
+    pkt->body = pkt->data + pkt->length; /* after packet type */\r
+    return pkt;\r
+}\r
+\r
+/*\r
+ * Construct an SSH-2 final-form packet: compress it, encrypt it,\r
+ * put the MAC on it. Final packet, ready to be sent, is stored in\r
+ * pkt->data. Total length is returned.\r
+ */\r
+static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt)\r
+{\r
+    int cipherblk, maclen, padding, i;\r
+\r
+    if (ssh->logctx)\r
+       log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],\r
+                  ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),\r
+                  pkt->body, pkt->length - (pkt->body - pkt->data),\r
+                  pkt->nblanks, pkt->blanks, &ssh->v2_outgoing_sequence);\r
+    sfree(pkt->blanks); pkt->blanks = NULL;\r
+    pkt->nblanks = 0;\r
+\r
+    /*\r
+     * Compress packet payload.\r
+     */\r
+    {\r
+       unsigned char *newpayload;\r
+       int newlen;\r
+       if (ssh->cscomp &&\r
+           ssh->cscomp->compress(ssh->cs_comp_ctx, pkt->data + 5,\r
+                                 pkt->length - 5,\r
+                                 &newpayload, &newlen)) {\r
+           pkt->length = 5;\r
+           ssh2_pkt_adddata(pkt, newpayload, newlen);\r
+           sfree(newpayload);\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Add padding. At least four bytes, and must also bring total\r
+     * length (minus MAC) up to a multiple of the block size.\r
+     * If pkt->forcepad is set, make sure the packet is at least that size\r
+     * after padding.\r
+     */\r
+    cipherblk = ssh->cscipher ? ssh->cscipher->blksize : 8;  /* block size */\r
+    cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */\r
+    padding = 4;\r
+    if (pkt->length + padding < pkt->forcepad)\r
+       padding = pkt->forcepad - pkt->length;\r
+    padding +=\r
+       (cipherblk - (pkt->length + padding) % cipherblk) % cipherblk;\r
+    assert(padding <= 255);\r
+    maclen = ssh->csmac ? ssh->csmac->len : 0;\r
+    ssh2_pkt_ensure(pkt, pkt->length + padding + maclen);\r
+    pkt->data[4] = padding;\r
+    for (i = 0; i < padding; i++)\r
+       pkt->data[pkt->length + i] = random_byte();\r
+    PUT_32BIT(pkt->data, pkt->length + padding - 4);\r
+    if (ssh->csmac)\r
+       ssh->csmac->generate(ssh->cs_mac_ctx, pkt->data,\r
+                            pkt->length + padding,\r
+                            ssh->v2_outgoing_sequence);\r
+    ssh->v2_outgoing_sequence++;       /* whether or not we MACed */\r
+\r
+    if (ssh->cscipher)\r
+       ssh->cscipher->encrypt(ssh->cs_cipher_ctx,\r
+                              pkt->data, pkt->length + padding);\r
+\r
+    pkt->encrypted_len = pkt->length + padding;\r
+\r
+    /* Ready-to-send packet starts at pkt->data. We return length. */\r
+    return pkt->length + padding + maclen;\r
+}\r
+\r
+/*\r
+ * Routines called from the main SSH code to send packets. There\r
+ * are quite a few of these, because we have two separate\r
+ * mechanisms for delaying the sending of packets:\r
+ * \r
+ *  - In order to send an IGNORE message and a password message in\r
+ *    a single fixed-length blob, we require the ability to\r
+ *    concatenate the encrypted forms of those two packets _into_ a\r
+ *    single blob and then pass it to our <network.h> transport\r
+ *    layer in one go. Hence, there's a deferment mechanism which\r
+ *    works after packet encryption.\r
+ * \r
+ *  - In order to avoid sending any connection-layer messages\r
+ *    during repeat key exchange, we have to queue up any such\r
+ *    outgoing messages _before_ they are encrypted (and in\r
+ *    particular before they're allocated sequence numbers), and\r
+ *    then send them once we've finished.\r
+ * \r
+ * I call these mechanisms `defer' and `queue' respectively, so as\r
+ * to distinguish them reasonably easily.\r
+ * \r
+ * The functions send_noqueue() and defer_noqueue() free the packet\r
+ * structure they are passed. Every outgoing packet goes through\r
+ * precisely one of these functions in its life; packets passed to\r
+ * ssh2_pkt_send() or ssh2_pkt_defer() either go straight to one of\r
+ * these or get queued, and then when the queue is later emptied\r
+ * the packets are all passed to defer_noqueue().\r
+ *\r
+ * When using a CBC-mode cipher, it's necessary to ensure that an\r
+ * attacker can't provide data to be encrypted using an IV that they\r
+ * know.  We ensure this by prefixing each packet that might contain\r
+ * user data with an SSH_MSG_IGNORE.  This is done using the deferral\r
+ * mechanism, so in this case send_noqueue() ends up redirecting to\r
+ * defer_noqueue().  If you don't like this inefficiency, don't use\r
+ * CBC.\r
+ */\r
+\r
+static void ssh2_pkt_defer_noqueue(Ssh, struct Packet *, int);\r
+static void ssh_pkt_defersend(Ssh);\r
+\r
+/*\r
+ * Send an SSH-2 packet immediately, without queuing or deferring.\r
+ */\r
+static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt)\r
+{\r
+    int len;\r
+    int backlog;\r
+    if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) {\r
+       /* We need to send two packets, so use the deferral mechanism. */\r
+       ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);\r
+       ssh_pkt_defersend(ssh);\r
+       return;\r
+    }\r
+    len = ssh2_pkt_construct(ssh, pkt);\r
+    backlog = s_write(ssh, pkt->data, len);\r
+    if (backlog > SSH_MAX_BACKLOG)\r
+       ssh_throttle_all(ssh, 1, backlog);\r
+\r
+    ssh->outgoing_data_size += pkt->encrypted_len;\r
+    if (!ssh->kex_in_progress &&\r
+       ssh->max_data_size != 0 &&\r
+       ssh->outgoing_data_size > ssh->max_data_size)\r
+       do_ssh2_transport(ssh, "too much data sent", -1, NULL);\r
+\r
+    ssh_free_packet(pkt);\r
+}\r
+\r
+/*\r
+ * Defer an SSH-2 packet.\r
+ */\r
+static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)\r
+{\r
+    int len;\r
+    if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&\r
+       ssh->deferred_len == 0 && !noignore &&\r
+       !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {\r
+       /*\r
+        * Interpose an SSH_MSG_IGNORE to ensure that user data don't\r
+        * get encrypted with a known IV.\r
+        */\r
+       struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);\r
+       ssh2_pkt_addstring_start(ipkt);\r
+       ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);\r
+    }\r
+    len = ssh2_pkt_construct(ssh, pkt);\r
+    if (ssh->deferred_len + len > ssh->deferred_size) {\r
+       ssh->deferred_size = ssh->deferred_len + len + 128;\r
+       ssh->deferred_send_data = sresize(ssh->deferred_send_data,\r
+                                         ssh->deferred_size,\r
+                                         unsigned char);\r
+    }\r
+    memcpy(ssh->deferred_send_data + ssh->deferred_len, pkt->data, len);\r
+    ssh->deferred_len += len;\r
+    ssh->deferred_data_size += pkt->encrypted_len;\r
+    ssh_free_packet(pkt);\r
+}\r
+\r
+/*\r
+ * Queue an SSH-2 packet.\r
+ */\r
+static void ssh2_pkt_queue(Ssh ssh, struct Packet *pkt)\r
+{\r
+    assert(ssh->queueing);\r
+\r
+    if (ssh->queuelen >= ssh->queuesize) {\r
+       ssh->queuesize = ssh->queuelen + 32;\r
+       ssh->queue = sresize(ssh->queue, ssh->queuesize, struct Packet *);\r
+    }\r
+\r
+    ssh->queue[ssh->queuelen++] = pkt;\r
+}\r
+\r
+/*\r
+ * Either queue or send a packet, depending on whether queueing is\r
+ * set.\r
+ */\r
+static void ssh2_pkt_send(Ssh ssh, struct Packet *pkt)\r
+{\r
+    if (ssh->queueing)\r
+       ssh2_pkt_queue(ssh, pkt);\r
+    else\r
+       ssh2_pkt_send_noqueue(ssh, pkt);\r
+}\r
+\r
+/*\r
+ * Either queue or defer a packet, depending on whether queueing is\r
+ * set.\r
+ */\r
+static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt)\r
+{\r
+    if (ssh->queueing)\r
+       ssh2_pkt_queue(ssh, pkt);\r
+    else\r
+       ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);\r
+}\r
+\r
+/*\r
+ * Send the whole deferred data block constructed by\r
+ * ssh2_pkt_defer() or SSH-1's defer_packet().\r
+ * \r
+ * The expected use of the defer mechanism is that you call\r
+ * ssh2_pkt_defer() a few times, then call ssh_pkt_defersend(). If\r
+ * not currently queueing, this simply sets up deferred_send_data\r
+ * and then sends it. If we _are_ currently queueing, the calls to\r
+ * ssh2_pkt_defer() put the deferred packets on to the queue\r
+ * instead, and therefore ssh_pkt_defersend() has no deferred data\r
+ * to send. Hence, there's no need to make it conditional on\r
+ * ssh->queueing.\r
+ */\r
+static void ssh_pkt_defersend(Ssh ssh)\r
+{\r
+    int backlog;\r
+    backlog = s_write(ssh, ssh->deferred_send_data, ssh->deferred_len);\r
+    ssh->deferred_len = ssh->deferred_size = 0;\r
+    sfree(ssh->deferred_send_data);\r
+    ssh->deferred_send_data = NULL;\r
+    if (backlog > SSH_MAX_BACKLOG)\r
+       ssh_throttle_all(ssh, 1, backlog);\r
+\r
+    ssh->outgoing_data_size += ssh->deferred_data_size;\r
+    if (!ssh->kex_in_progress &&\r
+       ssh->max_data_size != 0 &&\r
+       ssh->outgoing_data_size > ssh->max_data_size)\r
+       do_ssh2_transport(ssh, "too much data sent", -1, NULL);\r
+    ssh->deferred_data_size = 0;\r
+}\r
+\r
+/*\r
+ * Send a packet whose length needs to be disguised (typically\r
+ * passwords or keyboard-interactive responses).\r
+ */\r
+static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,\r
+                                      int padsize)\r
+{\r
+#if 0\r
+    if (0) {\r
+       /*\r
+        * The simplest way to do this is to adjust the\r
+        * variable-length padding field in the outgoing packet.\r
+        * \r
+        * Currently compiled out, because some Cisco SSH servers\r
+        * don't like excessively padded packets (bah, why's it\r
+        * always Cisco?)\r
+        */\r
+       pkt->forcepad = padsize;\r
+       ssh2_pkt_send(ssh, pkt);\r
+    } else\r
+#endif\r
+    {\r
+       /*\r
+        * If we can't do that, however, an alternative approach is\r
+        * to use the pkt_defer mechanism to bundle the packet\r
+        * tightly together with an SSH_MSG_IGNORE such that their\r
+        * combined length is a constant. So first we construct the\r
+        * final form of this packet and defer its sending.\r
+        */\r
+       ssh2_pkt_defer(ssh, pkt);\r
+\r
+       /*\r
+        * Now construct an SSH_MSG_IGNORE which includes a string\r
+        * that's an exact multiple of the cipher block size. (If\r
+        * the cipher is NULL so that the block size is\r
+        * unavailable, we don't do this trick at all, because we\r
+        * gain nothing by it.)\r
+        */\r
+       if (ssh->cscipher &&\r
+           !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {\r
+           int stringlen, i;\r
+\r
+           stringlen = (256 - ssh->deferred_len);\r
+           stringlen += ssh->cscipher->blksize - 1;\r
+           stringlen -= (stringlen % ssh->cscipher->blksize);\r
+           if (ssh->cscomp) {\r
+               /*\r
+                * Temporarily disable actual compression, so we\r
+                * can guarantee to get this string exactly the\r
+                * length we want it. The compression-disabling\r
+                * routine should return an integer indicating how\r
+                * many bytes we should adjust our string length\r
+                * by.\r
+                */\r
+               stringlen -=\r
+                   ssh->cscomp->disable_compression(ssh->cs_comp_ctx);\r
+           }\r
+           pkt = ssh2_pkt_init(SSH2_MSG_IGNORE);\r
+           ssh2_pkt_addstring_start(pkt);\r
+           for (i = 0; i < stringlen; i++) {\r
+               char c = (char) random_byte();\r
+               ssh2_pkt_addstring_data(pkt, &c, 1);\r
+           }\r
+           ssh2_pkt_defer(ssh, pkt);\r
+       }\r
+       ssh_pkt_defersend(ssh);\r
+    }\r
+}\r
+\r
+/*\r
+ * Send all queued SSH-2 packets. We send them by means of\r
+ * ssh2_pkt_defer_noqueue(), in case they included a pair of\r
+ * packets that needed to be lumped together.\r
+ */\r
+static void ssh2_pkt_queuesend(Ssh ssh)\r
+{\r
+    int i;\r
+\r
+    assert(!ssh->queueing);\r
+\r
+    for (i = 0; i < ssh->queuelen; i++)\r
+       ssh2_pkt_defer_noqueue(ssh, ssh->queue[i], FALSE);\r
+    ssh->queuelen = 0;\r
+\r
+    ssh_pkt_defersend(ssh);\r
+}\r
+\r
+#if 0\r
+void bndebug(char *string, Bignum b)\r
+{\r
+    unsigned char *p;\r
+    int i, len;\r
+    p = ssh2_mpint_fmt(b, &len);\r
+    debug(("%s", string));\r
+    for (i = 0; i < len; i++)\r
+       debug((" %02x", p[i]));\r
+    debug(("\n"));\r
+    sfree(p);\r
+}\r
+#endif\r
+\r
+static void hash_mpint(const struct ssh_hash *h, void *s, Bignum b)\r
+{\r
+    unsigned char *p;\r
+    int len;\r
+    p = ssh2_mpint_fmt(b, &len);\r
+    hash_string(h, s, p, len);\r
+    sfree(p);\r
+}\r
+\r
+/*\r
+ * Packet decode functions for both SSH-1 and SSH-2.\r
+ */\r
+static unsigned long ssh_pkt_getuint32(struct Packet *pkt)\r
+{\r
+    unsigned long value;\r
+    if (pkt->length - pkt->savedpos < 4)\r
+       return 0;                      /* arrgh, no way to decline (FIXME?) */\r
+    value = GET_32BIT(pkt->body + pkt->savedpos);\r
+    pkt->savedpos += 4;\r
+    return value;\r
+}\r
+static int ssh2_pkt_getbool(struct Packet *pkt)\r
+{\r
+    unsigned long value;\r
+    if (pkt->length - pkt->savedpos < 1)\r
+       return 0;                      /* arrgh, no way to decline (FIXME?) */\r
+    value = pkt->body[pkt->savedpos] != 0;\r
+    pkt->savedpos++;\r
+    return value;\r
+}\r
+static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length)\r
+{\r
+    int len;\r
+    *p = NULL;\r
+    *length = 0;\r
+    if (pkt->length - pkt->savedpos < 4)\r
+       return;\r
+    len = GET_32BIT(pkt->body + pkt->savedpos);\r
+    if (len < 0)\r
+       return;\r
+    *length = len;\r
+    pkt->savedpos += 4;\r
+    if (pkt->length - pkt->savedpos < *length)\r
+       return;\r
+    *p = (char *)(pkt->body + pkt->savedpos);\r
+    pkt->savedpos += *length;\r
+}\r
+static void *ssh_pkt_getdata(struct Packet *pkt, int length)\r
+{\r
+    if (pkt->length - pkt->savedpos < length)\r
+       return NULL;\r
+    pkt->savedpos += length;\r
+    return pkt->body + (pkt->savedpos - length);\r
+}\r
+static int ssh1_pkt_getrsakey(struct Packet *pkt, struct RSAKey *key,\r
+                             unsigned char **keystr)\r
+{\r
+    int j;\r
+\r
+    j = makekey(pkt->body + pkt->savedpos,\r
+               pkt->length - pkt->savedpos,\r
+               key, keystr, 0);\r
+\r
+    if (j < 0)\r
+       return FALSE;\r
+    \r
+    pkt->savedpos += j;\r
+    assert(pkt->savedpos < pkt->length);\r
+\r
+    return TRUE;\r
+}\r
+static Bignum ssh1_pkt_getmp(struct Packet *pkt)\r
+{\r
+    int j;\r
+    Bignum b;\r
+\r
+    j = ssh1_read_bignum(pkt->body + pkt->savedpos,\r
+                        pkt->length - pkt->savedpos, &b);\r
+\r
+    if (j < 0)\r
+       return NULL;\r
+\r
+    pkt->savedpos += j;\r
+    return b;\r
+}\r
+static Bignum ssh2_pkt_getmp(struct Packet *pkt)\r
+{\r
+    char *p;\r
+    int length;\r
+    Bignum b;\r
+\r
+    ssh_pkt_getstring(pkt, &p, &length);\r
+    if (!p)\r
+       return NULL;\r
+    if (p[0] & 0x80)\r
+       return NULL;\r
+    b = bignum_from_bytes((unsigned char *)p, length);\r
+    return b;\r
+}\r
+\r
+/*\r
+ * Helper function to add an SSH-2 signature blob to a packet.\r
+ * Expects to be shown the public key blob as well as the signature\r
+ * blob. Normally works just like ssh2_pkt_addstring, but will\r
+ * fiddle with the signature packet if necessary for\r
+ * BUG_SSH2_RSA_PADDING.\r
+ */\r
+static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,\r
+                            void *pkblob_v, int pkblob_len,\r
+                            void *sigblob_v, int sigblob_len)\r
+{\r
+    unsigned char *pkblob = (unsigned char *)pkblob_v;\r
+    unsigned char *sigblob = (unsigned char *)sigblob_v;\r
+\r
+    /* dmemdump(pkblob, pkblob_len); */\r
+    /* dmemdump(sigblob, sigblob_len); */\r
+\r
+    /*\r
+     * See if this is in fact an ssh-rsa signature and a buggy\r
+     * server; otherwise we can just do this the easy way.\r
+     */\r
+    if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) &&\r
+       (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) {\r
+       int pos, len, siglen;\r
+\r
+       /*\r
+        * Find the byte length of the modulus.\r
+        */\r
+\r
+       pos = 4+7;                     /* skip over "ssh-rsa" */\r
+       pos += 4 + GET_32BIT(pkblob+pos);   /* skip over exponent */\r
+       len = GET_32BIT(pkblob+pos);   /* find length of modulus */\r
+       pos += 4;                      /* find modulus itself */\r
+       while (len > 0 && pkblob[pos] == 0)\r
+           len--, pos++;\r
+       /* debug(("modulus length is %d\n", len)); */\r
+\r
+       /*\r
+        * Now find the signature integer.\r
+        */\r
+       pos = 4+7;                     /* skip over "ssh-rsa" */\r
+       siglen = GET_32BIT(sigblob+pos);\r
+       /* debug(("signature length is %d\n", siglen)); */\r
+\r
+       if (len != siglen) {\r
+           unsigned char newlen[4];\r
+           ssh2_pkt_addstring_start(pkt);\r
+           ssh2_pkt_addstring_data(pkt, (char *)sigblob, pos);\r
+           /* dmemdump(sigblob, pos); */\r
+           pos += 4;                  /* point to start of actual sig */\r
+           PUT_32BIT(newlen, len);\r
+           ssh2_pkt_addstring_data(pkt, (char *)newlen, 4);\r
+           /* dmemdump(newlen, 4); */\r
+           newlen[0] = 0;\r
+           while (len-- > siglen) {\r
+               ssh2_pkt_addstring_data(pkt, (char *)newlen, 1);\r
+               /* dmemdump(newlen, 1); */\r
+           }\r
+           ssh2_pkt_addstring_data(pkt, (char *)(sigblob+pos), siglen);\r
+           /* dmemdump(sigblob+pos, siglen); */\r
+           return;\r
+       }\r
+\r
+       /* Otherwise fall through and do it the easy way. */\r
+    }\r
+\r
+    ssh2_pkt_addstring_start(pkt);\r
+    ssh2_pkt_addstring_data(pkt, (char *)sigblob, sigblob_len);\r
+}\r
+\r
+/*\r
+ * Examine the remote side's version string and compare it against\r
+ * a list of known buggy implementations.\r
+ */\r
+static void ssh_detect_bugs(Ssh ssh, char *vstring)\r
+{\r
+    char *imp;                        /* pointer to implementation part */\r
+    imp = vstring;\r
+    imp += strcspn(imp, "-");\r
+    if (*imp) imp++;\r
+    imp += strcspn(imp, "-");\r
+    if (*imp) imp++;\r
+\r
+    ssh->remote_bugs = 0;\r
+\r
+    /*\r
+     * General notes on server version strings:\r
+     *  - Not all servers reporting "Cisco-1.25" have all the bugs listed\r
+     *    here -- in particular, we've heard of one that's perfectly happy\r
+     *    with SSH1_MSG_IGNOREs -- but this string never seems to change,\r
+     *    so we can't distinguish them.\r
+     */\r
+    if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_ignore1 == AUTO &&\r
+        (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||\r
+         !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||\r
+         !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||\r
+         !strcmp(imp, "OSU_1.4alpha3") || !strcmp(imp, "OSU_1.5alpha4")))) {\r
+       /*\r
+        * These versions don't support SSH1_MSG_IGNORE, so we have\r
+        * to use a different defence against password length\r
+        * sniffing.\r
+        */\r
+       ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;\r
+       logevent("We believe remote version has SSH-1 ignore bug");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_plainpw1 == AUTO &&\r
+        (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {\r
+       /*\r
+        * These versions need a plain password sent; they can't\r
+        * handle having a null and a random length of data after\r
+        * the password.\r
+        */\r
+       ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;\r
+       logevent("We believe remote version needs a plain SSH-1 password");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_rsa1 == AUTO &&\r
+        (!strcmp(imp, "Cisco-1.25")))) {\r
+       /*\r
+        * These versions apparently have no clue whatever about\r
+        * RSA authentication and will panic and die if they see\r
+        * an AUTH_RSA message.\r
+        */\r
+       ssh->remote_bugs |= BUG_CHOKES_ON_RSA;\r
+       logevent("We believe remote version can't handle SSH-1 RSA authentication");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_hmac2 == AUTO &&\r
+        !wc_match("* VShell", imp) &&\r
+        (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||\r
+         wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||\r
+         wc_match("2.1 *", imp)))) {\r
+       /*\r
+        * These versions have the HMAC bug.\r
+        */\r
+       ssh->remote_bugs |= BUG_SSH2_HMAC;\r
+       logevent("We believe remote version has SSH-2 HMAC bug");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_derivekey2 == AUTO &&\r
+        !wc_match("* VShell", imp) &&\r
+        (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {\r
+       /*\r
+        * These versions have the key-derivation bug (failing to\r
+        * include the literal shared secret in the hashes that\r
+        * generate the keys).\r
+        */\r
+       ssh->remote_bugs |= BUG_SSH2_DERIVEKEY;\r
+       logevent("We believe remote version has SSH-2 key-derivation bug");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_rsapad2 == AUTO &&\r
+        (wc_match("OpenSSH_2.[5-9]*", imp) ||\r
+         wc_match("OpenSSH_3.[0-2]*", imp)))) {\r
+       /*\r
+        * These versions have the SSH-2 RSA padding bug.\r
+        */\r
+       ssh->remote_bugs |= BUG_SSH2_RSA_PADDING;\r
+       logevent("We believe remote version has SSH-2 RSA padding bug");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_pksessid2 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_pksessid2 == AUTO &&\r
+        wc_match("OpenSSH_2.[0-2]*", imp))) {\r
+       /*\r
+        * These versions have the SSH-2 session-ID bug in\r
+        * public-key authentication.\r
+        */\r
+       ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;\r
+       logevent("We believe remote version has SSH-2 public-key-session-ID bug");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_rekey2 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_rekey2 == AUTO &&\r
+        (wc_match("DigiSSH_2.0", imp) ||\r
+         wc_match("OpenSSH_2.[0-4]*", imp) ||\r
+         wc_match("OpenSSH_2.5.[0-3]*", imp) ||\r
+         wc_match("Sun_SSH_1.0", imp) ||\r
+         wc_match("Sun_SSH_1.0.1", imp) ||\r
+         /* All versions <= 1.2.6 (they changed their format in 1.2.7) */\r
+         wc_match("WeOnlyDo-*", imp)))) {\r
+       /*\r
+        * These versions have the SSH-2 rekey bug.\r
+        */\r
+       ssh->remote_bugs |= BUG_SSH2_REKEY;\r
+       logevent("We believe remote version has SSH-2 rekey bug");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON ||\r
+       (ssh->cfg.sshbug_maxpkt2 == AUTO &&\r
+        (wc_match("1.36_sshlib GlobalSCAPE", imp) ||\r
+          wc_match("1.36 sshlib: GlobalScape", imp)))) {\r
+       /*\r
+        * This version ignores our makpkt and needs to be throttled.\r
+        */\r
+       ssh->remote_bugs |= BUG_SSH2_MAXPKT;\r
+       logevent("We believe remote version ignores SSH-2 maximum packet size");\r
+    }\r
+\r
+    if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {\r
+       /*\r
+        * Servers that don't support SSH2_MSG_IGNORE. Currently,\r
+        * none detected automatically.\r
+        */\r
+       ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;\r
+       logevent("We believe remote version has SSH-2 ignore bug");\r
+    }\r
+}\r
+\r
+/*\r
+ * The `software version' part of an SSH version string is required\r
+ * to contain no spaces or minus signs.\r
+ */\r
+static void ssh_fix_verstring(char *str)\r
+{\r
+    /* Eat "SSH-<protoversion>-". */\r
+    assert(*str == 'S'); str++;\r
+    assert(*str == 'S'); str++;\r
+    assert(*str == 'H'); str++;\r
+    assert(*str == '-'); str++;\r
+    while (*str && *str != '-') str++;\r
+    assert(*str == '-'); str++;\r
+\r
+    /* Convert minus signs and spaces in the remaining string into\r
+     * underscores. */\r
+    while (*str) {\r
+        if (*str == '-' || *str == ' ')\r
+            *str = '_';\r
+        str++;\r
+    }\r
+}\r
+\r
+/*\r
+ * Send an appropriate SSH version string.\r
+ */\r
+static void ssh_send_verstring(Ssh ssh, char *svers)\r
+{\r
+    char *verstring;\r
+\r
+    if (ssh->version == 2) {\r
+       /*\r
+        * Construct a v2 version string.\r
+        */\r
+       verstring = dupprintf("SSH-2.0-%s\015\012", sshver);\r
+    } else {\r
+       /*\r
+        * Construct a v1 version string.\r
+        */\r
+       verstring = dupprintf("SSH-%s-%s\012",\r
+                             (ssh_versioncmp(svers, "1.5") <= 0 ?\r
+                              svers : "1.5"),\r
+                             sshver);\r
+    }\r
+\r
+    ssh_fix_verstring(verstring);\r
+\r
+    if (ssh->version == 2) {\r
+       size_t len;\r
+       /*\r
+        * Record our version string.\r
+        */\r
+       len = strcspn(verstring, "\015\012");\r
+       ssh->v_c = snewn(len + 1, char);\r
+       memcpy(ssh->v_c, verstring, len);\r
+       ssh->v_c[len] = 0;\r
+    }\r
+\r
+    logeventf(ssh, "We claim version: %.*s",\r
+             strcspn(verstring, "\015\012"), verstring);\r
+    s_write(ssh, verstring, strlen(verstring));\r
+    sfree(verstring);\r
+}\r
+\r
+static int do_ssh_init(Ssh ssh, unsigned char c)\r
+{\r
+    struct do_ssh_init_state {\r
+       int vslen;\r
+       char version[10];\r
+       char *vstring;\r
+       int vstrsize;\r
+       int i;\r
+       int proto1, proto2;\r
+    };\r
+    crState(do_ssh_init_state);\r
+\r
+    crBegin(ssh->do_ssh_init_crstate);\r
+\r
+    /* Search for a line beginning with the string "SSH-" in the input. */\r
+    for (;;) {\r
+       if (c != 'S') goto no;\r
+       crReturn(1);\r
+       if (c != 'S') goto no;\r
+       crReturn(1);\r
+       if (c != 'H') goto no;\r
+       crReturn(1);\r
+       if (c != '-') goto no;\r
+       break;\r
+      no:\r
+       while (c != '\012')\r
+           crReturn(1);\r
+       crReturn(1);\r
+    }\r
+\r
+    s->vstrsize = 16;\r
+    s->vstring = snewn(s->vstrsize, char);\r
+    strcpy(s->vstring, "SSH-");\r
+    s->vslen = 4;\r
+    s->i = 0;\r
+    while (1) {\r
+       crReturn(1);                   /* get another char */\r
+       if (s->vslen >= s->vstrsize - 1) {\r
+           s->vstrsize += 16;\r
+           s->vstring = sresize(s->vstring, s->vstrsize, char);\r
+       }\r
+       s->vstring[s->vslen++] = c;\r
+       if (s->i >= 0) {\r
+           if (c == '-') {\r
+               s->version[s->i] = '\0';\r
+               s->i = -1;\r
+           } else if (s->i < sizeof(s->version) - 1)\r
+               s->version[s->i++] = c;\r
+       } else if (c == '\012')\r
+           break;\r
+    }\r
+\r
+    ssh->agentfwd_enabled = FALSE;\r
+    ssh->rdpkt2_state.incoming_sequence = 0;\r
+\r
+    s->vstring[s->vslen] = 0;\r
+    s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */\r
+    logeventf(ssh, "Server version: %s", s->vstring);\r
+    ssh_detect_bugs(ssh, s->vstring);\r
+\r
+    /*\r
+     * Decide which SSH protocol version to support.\r
+     */\r
+\r
+    /* Anything strictly below "2.0" means protocol 1 is supported. */\r
+    s->proto1 = ssh_versioncmp(s->version, "2.0") < 0;\r
+    /* Anything greater or equal to "1.99" means protocol 2 is supported. */\r
+    s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0;\r
+\r
+    if (ssh->cfg.sshprot == 0 && !s->proto1) {\r
+       bombout(("SSH protocol version 1 required by user but not provided by server"));\r
+       crStop(0);\r
+    }\r
+    if (ssh->cfg.sshprot == 3 && !s->proto2) {\r
+       bombout(("SSH protocol version 2 required by user but not provided by server"));\r
+       crStop(0);\r
+    }\r
+\r
+    if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1))\r
+       ssh->version = 2;\r
+    else\r
+       ssh->version = 1;\r
+\r
+    logeventf(ssh, "Using SSH protocol version %d", ssh->version);\r
+\r
+    /* Send the version string, if we haven't already */\r
+    if (ssh->cfg.sshprot != 3)\r
+       ssh_send_verstring(ssh, s->version);\r
+\r
+    if (ssh->version == 2) {\r
+       size_t len;\r
+       /*\r
+        * Record their version string.\r
+        */\r
+       len = strcspn(s->vstring, "\015\012");\r
+       ssh->v_s = snewn(len + 1, char);\r
+       memcpy(ssh->v_s, s->vstring, len);\r
+       ssh->v_s[len] = 0;\r
+           \r
+       /*\r
+        * Initialise SSH-2 protocol.\r
+        */\r
+       ssh->protocol = ssh2_protocol;\r
+       ssh2_protocol_setup(ssh);\r
+       ssh->s_rdpkt = ssh2_rdpkt;\r
+    } else {\r
+       /*\r
+        * Initialise SSH-1 protocol.\r
+        */\r
+       ssh->protocol = ssh1_protocol;\r
+       ssh1_protocol_setup(ssh);\r
+       ssh->s_rdpkt = ssh1_rdpkt;\r
+    }\r
+    if (ssh->version == 2)\r
+       do_ssh2_transport(ssh, NULL, -1, NULL);\r
+\r
+    update_specials_menu(ssh->frontend);\r
+    ssh->state = SSH_STATE_BEFORE_SIZE;\r
+    ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh);\r
+\r
+    sfree(s->vstring);\r
+\r
+    crFinish(0);\r
+}\r
+\r
+static void ssh_process_incoming_data(Ssh ssh,\r
+                                     unsigned char **data, int *datalen)\r
+{\r
+    struct Packet *pktin;\r
+\r
+    pktin = ssh->s_rdpkt(ssh, data, datalen);\r
+    if (pktin) {\r
+       ssh->protocol(ssh, NULL, 0, pktin);\r
+       ssh_free_packet(pktin);\r
+    }\r
+}\r
+\r
+static void ssh_queue_incoming_data(Ssh ssh,\r
+                                   unsigned char **data, int *datalen)\r
+{\r
+    bufchain_add(&ssh->queued_incoming_data, *data, *datalen);\r
+    *data += *datalen;\r
+    *datalen = 0;\r
+}\r
+\r
+static void ssh_process_queued_incoming_data(Ssh ssh)\r
+{\r
+    void *vdata;\r
+    unsigned char *data;\r
+    int len, origlen;\r
+\r
+    while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {\r
+       bufchain_prefix(&ssh->queued_incoming_data, &vdata, &len);\r
+       data = vdata;\r
+       origlen = len;\r
+\r
+       while (!ssh->frozen && len > 0)\r
+           ssh_process_incoming_data(ssh, &data, &len);\r
+\r
+       if (origlen > len)\r
+           bufchain_consume(&ssh->queued_incoming_data, origlen - len);\r
+    }\r
+}\r
+\r
+static void ssh_set_frozen(Ssh ssh, int frozen)\r
+{\r
+    if (ssh->s)\r
+       sk_set_frozen(ssh->s, frozen);\r
+    ssh->frozen = frozen;\r
+}\r
+\r
+static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)\r
+{\r
+    /* Log raw data, if we're in that mode. */\r
+    if (ssh->logctx)\r
+       log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen,\r
+                  0, NULL, NULL);\r
+\r
+    crBegin(ssh->ssh_gotdata_crstate);\r
+\r
+    /*\r
+     * To begin with, feed the characters one by one to the\r
+     * protocol initialisation / selection function do_ssh_init().\r
+     * When that returns 0, we're done with the initial greeting\r
+     * exchange and can move on to packet discipline.\r
+     */\r
+    while (1) {\r
+       int ret;                       /* need not be kept across crReturn */\r
+       if (datalen == 0)\r
+           crReturnV;                 /* more data please */\r
+       ret = do_ssh_init(ssh, *data);\r
+       data++;\r
+       datalen--;\r
+       if (ret == 0)\r
+           break;\r
+    }\r
+\r
+    /*\r
+     * We emerge from that loop when the initial negotiation is\r
+     * over and we have selected an s_rdpkt function. Now pass\r
+     * everything to s_rdpkt, and then pass the resulting packets\r
+     * to the proper protocol handler.\r
+     */\r
+\r
+    while (1) {\r
+       while (bufchain_size(&ssh->queued_incoming_data) > 0 || datalen > 0) {\r
+           if (ssh->frozen) {\r
+               ssh_queue_incoming_data(ssh, &data, &datalen);\r
+               /* This uses up all data and cannot cause anything interesting\r
+                * to happen; indeed, for anything to happen at all, we must\r
+                * return, so break out. */\r
+               break;\r
+           } else if (bufchain_size(&ssh->queued_incoming_data) > 0) {\r
+               /* This uses up some or all data, and may freeze the\r
+                * session. */\r
+               ssh_process_queued_incoming_data(ssh);\r
+           } else {\r
+               /* This uses up some or all data, and may freeze the\r
+                * session. */\r
+               ssh_process_incoming_data(ssh, &data, &datalen);\r
+           }\r
+           /* FIXME this is probably EBW. */\r
+           if (ssh->state == SSH_STATE_CLOSED)\r
+               return;\r
+       }\r
+       /* We're out of data. Go and get some more. */\r
+       crReturnV;\r
+    }\r
+    crFinishV;\r
+}\r
+\r
+static int ssh_do_close(Ssh ssh, int notify_exit)\r
+{\r
+    int ret = 0;\r
+    struct ssh_channel *c;\r
+\r
+    ssh->state = SSH_STATE_CLOSED;\r
+    expire_timer_context(ssh);\r
+    if (ssh->s) {\r
+        sk_close(ssh->s);\r
+        ssh->s = NULL;\r
+        if (notify_exit)\r
+            notify_remote_exit(ssh->frontend);\r
+        else\r
+            ret = 1;\r
+    }\r
+    /*\r
+     * Now we must shut down any port- and X-forwarded channels going\r
+     * through this connection.\r
+     */\r
+    if (ssh->channels) {\r
+       while (NULL != (c = index234(ssh->channels, 0))) {\r
+           switch (c->type) {\r
+             case CHAN_X11:\r
+               x11_close(c->u.x11.s);\r
+               break;\r
+             case CHAN_SOCKDATA:\r
+             case CHAN_SOCKDATA_DORMANT:\r
+               pfd_close(c->u.pfd.s);\r
+               break;\r
+           }\r
+           del234(ssh->channels, c); /* moving next one to index 0 */\r
+           if (ssh->version == 2)\r
+               bufchain_clear(&c->v.v2.outbuffer);\r
+           sfree(c);\r
+       }\r
+    }\r
+    /*\r
+     * Go through port-forwardings, and close any associated\r
+     * listening sockets.\r
+     */\r
+    if (ssh->portfwds) {\r
+       struct ssh_portfwd *pf;\r
+       while (NULL != (pf = index234(ssh->portfwds, 0))) {\r
+           /* Dispose of any listening socket. */\r
+           if (pf->local)\r
+               pfd_terminate(pf->local);\r
+           del234(ssh->portfwds, pf); /* moving next one to index 0 */\r
+           free_portfwd(pf);\r
+       }\r
+       freetree234(ssh->portfwds);\r
+       ssh->portfwds = NULL;\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+static void ssh_log(Plug plug, int type, SockAddr addr, int port,\r
+                   const char *error_msg, int error_code)\r
+{\r
+    Ssh ssh = (Ssh) plug;\r
+    char addrbuf[256], *msg;\r
+\r
+    sk_getaddr(addr, addrbuf, lenof(addrbuf));\r
+\r
+    if (type == 0)\r
+       msg = dupprintf("Connecting to %s port %d", addrbuf, port);\r
+    else\r
+       msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);\r
+\r
+    logevent(msg);\r
+    sfree(msg);\r
+}\r
+\r
+static int ssh_closing(Plug plug, const char *error_msg, int error_code,\r
+                      int calling_back)\r
+{\r
+    Ssh ssh = (Ssh) plug;\r
+    int need_notify = ssh_do_close(ssh, FALSE);\r
+\r
+    if (!error_msg) {\r
+       if (!ssh->close_expected)\r
+           error_msg = "Server unexpectedly closed network connection";\r
+       else\r
+           error_msg = "Server closed network connection";\r
+    }\r
+\r
+    if (ssh->close_expected && ssh->clean_exit && ssh->exitcode < 0)\r
+       ssh->exitcode = 0;\r
+\r
+    if (need_notify)\r
+        notify_remote_exit(ssh->frontend);\r
+\r
+    if (error_msg)\r
+       logevent(error_msg);\r
+    if (!ssh->close_expected || !ssh->clean_exit)\r
+       connection_fatal(ssh->frontend, "%s", error_msg);\r
+    return 0;\r
+}\r
+\r
+static int ssh_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    Ssh ssh = (Ssh) plug;\r
+    ssh_gotdata(ssh, (unsigned char *)data, len);\r
+    if (ssh->state == SSH_STATE_CLOSED) {\r
+       ssh_do_close(ssh, TRUE);\r
+       return 0;\r
+    }\r
+    return 1;\r
+}\r
+\r
+static void ssh_sent(Plug plug, int bufsize)\r
+{\r
+    Ssh ssh = (Ssh) plug;\r
+    /*\r
+     * If the send backlog on the SSH socket itself clears, we\r
+     * should unthrottle the whole world if it was throttled.\r
+     */\r
+    if (bufsize < SSH_MAX_BACKLOG)\r
+       ssh_throttle_all(ssh, 0, bufsize);\r
+}\r
+\r
+/*\r
+ * Connect to specified host and port.\r
+ * Returns an error message, or NULL on success.\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *connect_to_host(Ssh ssh, char *host, int port,\r
+                                  char **realhost, int nodelay, int keepalive)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       ssh_log,\r
+       ssh_closing,\r
+       ssh_receive,\r
+       ssh_sent,\r
+       NULL\r
+    };\r
+\r
+    SockAddr addr;\r
+    const char *err;\r
+\r
+    if (*ssh->cfg.loghost) {\r
+       char *colon;\r
+\r
+       ssh->savedhost = dupstr(ssh->cfg.loghost);\r
+       ssh->savedport = 22;           /* default ssh port */\r
+\r
+       /*\r
+        * A colon suffix on savedhost also lets us affect\r
+        * savedport.\r
+        * \r
+        * (FIXME: do something about IPv6 address literals here.)\r
+        */\r
+       colon = strrchr(ssh->savedhost, ':');\r
+       if (colon) {\r
+           *colon++ = '\0';\r
+           if (*colon)\r
+               ssh->savedport = atoi(colon);\r
+       }\r
+    } else {\r
+       ssh->savedhost = dupstr(host);\r
+       if (port < 0)\r
+           port = 22;                 /* default ssh port */\r
+       ssh->savedport = port;\r
+    }\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    logeventf(ssh, "Looking up host \"%s\"%s", host,\r
+             (ssh->cfg.addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
+              (ssh->cfg.addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));\r
+    addr = name_lookup(host, port, realhost, &ssh->cfg,\r
+                      ssh->cfg.addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+    ssh->fullhostname = dupstr(*realhost);   /* save in case of GSSAPI */\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    ssh->fn = &fn_table;\r
+    ssh->s = new_connection(addr, *realhost, port,\r
+                           0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg);\r
+    if ((err = sk_socket_error(ssh->s)) != NULL) {\r
+       ssh->s = NULL;\r
+       notify_remote_exit(ssh->frontend);\r
+       return err;\r
+    }\r
+\r
+    /*\r
+     * If the SSH version number's fixed, set it now, and if it's SSH-2,\r
+     * send the version string too.\r
+     */\r
+    if (ssh->cfg.sshprot == 0)\r
+       ssh->version = 1;\r
+    if (ssh->cfg.sshprot == 3) {\r
+       ssh->version = 2;\r
+       ssh_send_verstring(ssh, NULL);\r
+    }\r
+\r
+    /*\r
+     * loghost, if configured, overrides realhost.\r
+     */\r
+    if (*ssh->cfg.loghost) {\r
+       sfree(*realhost);\r
+       *realhost = dupstr(ssh->cfg.loghost);\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Throttle or unthrottle the SSH connection.\r
+ */\r
+static void ssh_throttle_conn(Ssh ssh, int adjust)\r
+{\r
+    int old_count = ssh->conn_throttle_count;\r
+    ssh->conn_throttle_count += adjust;\r
+    assert(ssh->conn_throttle_count >= 0);\r
+    if (ssh->conn_throttle_count && !old_count) {\r
+       ssh_set_frozen(ssh, 1);\r
+    } else if (!ssh->conn_throttle_count && old_count) {\r
+       ssh_set_frozen(ssh, 0);\r
+    }\r
+}\r
+\r
+/*\r
+ * Throttle or unthrottle _all_ local data streams (for when sends\r
+ * on the SSH connection itself back up).\r
+ */\r
+static void ssh_throttle_all(Ssh ssh, int enable, int bufsize)\r
+{\r
+    int i;\r
+    struct ssh_channel *c;\r
+\r
+    if (enable == ssh->throttled_all)\r
+       return;\r
+    ssh->throttled_all = enable;\r
+    ssh->overall_bufsize = bufsize;\r
+    if (!ssh->channels)\r
+       return;\r
+    for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) {\r
+       switch (c->type) {\r
+         case CHAN_MAINSESSION:\r
+           /*\r
+            * This is treated separately, outside the switch.\r
+            */\r
+           break;\r
+         case CHAN_X11:\r
+           x11_override_throttle(c->u.x11.s, enable);\r
+           break;\r
+         case CHAN_AGENT:\r
+           /* Agent channels require no buffer management. */\r
+           break;\r
+         case CHAN_SOCKDATA:\r
+           pfd_override_throttle(c->u.pfd.s, enable);\r
+           break;\r
+       }\r
+    }\r
+}\r
+\r
+static void ssh_agent_callback(void *sshv, void *reply, int replylen)\r
+{\r
+    Ssh ssh = (Ssh) sshv;\r
+\r
+    ssh->agent_response = reply;\r
+    ssh->agent_response_len = replylen;\r
+\r
+    if (ssh->version == 1)\r
+       do_ssh1_login(ssh, NULL, -1, NULL);\r
+    else\r
+       do_ssh2_authconn(ssh, NULL, -1, NULL);\r
+}\r
+\r
+static void ssh_dialog_callback(void *sshv, int ret)\r
+{\r
+    Ssh ssh = (Ssh) sshv;\r
+\r
+    ssh->user_response = ret;\r
+\r
+    if (ssh->version == 1)\r
+       do_ssh1_login(ssh, NULL, -1, NULL);\r
+    else\r
+       do_ssh2_transport(ssh, NULL, -1, NULL);\r
+\r
+    /*\r
+     * This may have unfrozen the SSH connection, so do a\r
+     * queued-data run.\r
+     */\r
+    ssh_process_queued_incoming_data(ssh);\r
+}\r
+\r
+static void ssh_agentf_callback(void *cv, void *reply, int replylen)\r
+{\r
+    struct ssh_channel *c = (struct ssh_channel *)cv;\r
+    Ssh ssh = c->ssh;\r
+    void *sentreply = reply;\r
+\r
+    if (!sentreply) {\r
+       /* Fake SSH_AGENT_FAILURE. */\r
+       sentreply = "\0\0\0\1\5";\r
+       replylen = 5;\r
+    }\r
+    if (ssh->version == 2) {\r
+       ssh2_add_channel_data(c, sentreply, replylen);\r
+       ssh2_try_send(c);\r
+    } else {\r
+       send_packet(ssh, SSH1_MSG_CHANNEL_DATA,\r
+                   PKT_INT, c->remoteid,\r
+                   PKT_INT, replylen,\r
+                   PKTT_DATA,\r
+                   PKT_DATA, sentreply, replylen,\r
+                   PKTT_OTHER,\r
+                   PKT_END);\r
+    }\r
+    if (reply)\r
+       sfree(reply);\r
+}\r
+\r
+/*\r
+ * Client-initiated disconnection. Send a DISCONNECT if `wire_reason'\r
+ * non-NULL, otherwise just close the connection. `client_reason' == NULL\r
+ * => log `wire_reason'.\r
+ */\r
+static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason,\r
+                          int code, int clean_exit)\r
+{\r
+    char *error;\r
+    if (!client_reason)\r
+       client_reason = wire_reason;\r
+    if (client_reason)\r
+       error = dupprintf("Disconnected: %s", client_reason);\r
+    else\r
+       error = dupstr("Disconnected");\r
+    if (wire_reason) {\r
+       if (ssh->version == 1) {\r
+           send_packet(ssh, SSH1_MSG_DISCONNECT, PKT_STR, wire_reason,\r
+                       PKT_END);\r
+       } else if (ssh->version == 2) {\r
+           struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);\r
+           ssh2_pkt_adduint32(pktout, code);\r
+           ssh2_pkt_addstring(pktout, wire_reason);\r
+           ssh2_pkt_addstring(pktout, "en");   /* language tag */\r
+           ssh2_pkt_send_noqueue(ssh, pktout);\r
+       }\r
+    }\r
+    ssh->close_expected = TRUE;\r
+    ssh->clean_exit = clean_exit;\r
+    ssh_closing((Plug)ssh, error, 0, 0);\r
+    sfree(error);\r
+}\r
+\r
+/*\r
+ * Handle the key exchange and user authentication phases.\r
+ */\r
+static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,\r
+                        struct Packet *pktin)\r
+{\r
+    int i, j, ret;\r
+    unsigned char cookie[8], *ptr;\r
+    struct RSAKey servkey, hostkey;\r
+    struct MD5Context md5c;\r
+    struct do_ssh1_login_state {\r
+       int len;\r
+       unsigned char *rsabuf, *keystr1, *keystr2;\r
+       unsigned long supported_ciphers_mask, supported_auths_mask;\r
+       int tried_publickey, tried_agent;\r
+       int tis_auth_refused, ccard_auth_refused;\r
+       unsigned char session_id[16];\r
+       int cipher_type;\r
+       char username[100];\r
+       void *publickey_blob;\r
+       int publickey_bloblen;\r
+       char *publickey_comment;\r
+       int publickey_encrypted;\r
+       prompts_t *cur_prompt;\r
+       char c;\r
+       int pwpkt_type;\r
+       unsigned char request[5], *response, *p;\r
+       int responselen;\r
+       int keyi, nkeys;\r
+       int authed;\r
+       struct RSAKey key;\r
+       Bignum challenge;\r
+       char *commentp;\r
+       int commentlen;\r
+        int dlgret;\r
+    };\r
+    crState(do_ssh1_login_state);\r
+\r
+    crBegin(ssh->do_ssh1_login_crstate);\r
+\r
+    if (!pktin)\r
+       crWaitUntil(pktin);\r
+\r
+    if (pktin->type != SSH1_SMSG_PUBLIC_KEY) {\r
+       bombout(("Public key packet not received"));\r
+       crStop(0);\r
+    }\r
+\r
+    logevent("Received public keys");\r
+\r
+    ptr = ssh_pkt_getdata(pktin, 8);\r
+    if (!ptr) {\r
+       bombout(("SSH-1 public key packet stopped before random cookie"));\r
+       crStop(0);\r
+    }\r
+    memcpy(cookie, ptr, 8);\r
+\r
+    if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) ||\r
+       !ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) {    \r
+       bombout(("Failed to read SSH-1 public keys from public key packet"));\r
+       crStop(0);\r
+    }\r
+\r
+    /*\r
+     * Log the host key fingerprint.\r
+     */\r
+    {\r
+       char logmsg[80];\r
+       logevent("Host key fingerprint is:");\r
+       strcpy(logmsg, "      ");\r
+       hostkey.comment = NULL;\r
+       rsa_fingerprint(logmsg + strlen(logmsg),\r
+                       sizeof(logmsg) - strlen(logmsg), &hostkey);\r
+       logevent(logmsg);\r
+    }\r
+\r
+    ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin);\r
+    s->supported_ciphers_mask = ssh_pkt_getuint32(pktin);\r
+    s->supported_auths_mask = ssh_pkt_getuint32(pktin);\r
+    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA))\r
+       s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA);\r
+\r
+    ssh->v1_local_protoflags =\r
+       ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;\r
+    ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;\r
+\r
+    MD5Init(&md5c);\r
+    MD5Update(&md5c, s->keystr2, hostkey.bytes);\r
+    MD5Update(&md5c, s->keystr1, servkey.bytes);\r
+    MD5Update(&md5c, cookie, 8);\r
+    MD5Final(s->session_id, &md5c);\r
+\r
+    for (i = 0; i < 32; i++)\r
+       ssh->session_key[i] = random_byte();\r
+\r
+    /*\r
+     * Verify that the `bits' and `bytes' parameters match.\r
+     */\r
+    if (hostkey.bits > hostkey.bytes * 8 ||\r
+       servkey.bits > servkey.bytes * 8) {\r
+       bombout(("SSH-1 public keys were badly formatted"));\r
+       crStop(0);\r
+    }\r
+\r
+    s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);\r
+\r
+    s->rsabuf = snewn(s->len, unsigned char);\r
+\r
+    /*\r
+     * Verify the host key.\r
+     */\r
+    {\r
+       /*\r
+        * First format the key into a string.\r
+        */\r
+       int len = rsastr_len(&hostkey);\r
+       char fingerprint[100];\r
+       char *keystr = snewn(len, char);\r
+       rsastr_fmt(keystr, &hostkey);\r
+       rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);\r
+\r
+        ssh_set_frozen(ssh, 1);\r
+       s->dlgret = verify_ssh_host_key(ssh->frontend,\r
+                                        ssh->savedhost, ssh->savedport,\r
+                                        "rsa", keystr, fingerprint,\r
+                                        ssh_dialog_callback, ssh);\r
+       sfree(keystr);\r
+        if (s->dlgret < 0) {\r
+            do {\r
+                crReturn(0);\r
+                if (pktin) {\r
+                    bombout(("Unexpected data from server while waiting"\r
+                             " for user host key response"));\r
+                    crStop(0);\r
+                }\r
+            } while (pktin || inlen > 0);\r
+            s->dlgret = ssh->user_response;\r
+        }\r
+        ssh_set_frozen(ssh, 0);\r
+\r
+        if (s->dlgret == 0) {\r
+           ssh_disconnect(ssh, "User aborted at host key verification",\r
+                          NULL, 0, TRUE);\r
+           crStop(0);\r
+        }\r
+    }\r
+\r
+    for (i = 0; i < 32; i++) {\r
+       s->rsabuf[i] = ssh->session_key[i];\r
+       if (i < 16)\r
+           s->rsabuf[i] ^= s->session_id[i];\r
+    }\r
+\r
+    if (hostkey.bytes > servkey.bytes) {\r
+       ret = rsaencrypt(s->rsabuf, 32, &servkey);\r
+       if (ret)\r
+           ret = rsaencrypt(s->rsabuf, servkey.bytes, &hostkey);\r
+    } else {\r
+       ret = rsaencrypt(s->rsabuf, 32, &hostkey);\r
+       if (ret)\r
+           ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey);\r
+    }\r
+    if (!ret) {\r
+       bombout(("SSH-1 public key encryptions failed due to bad formatting"));\r
+       crStop(0);      \r
+    }\r
+\r
+    logevent("Encrypted session key");\r
+\r
+    {\r
+       int cipher_chosen = 0, warn = 0;\r
+       char *cipher_string = NULL;\r
+       int i;\r
+       for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {\r
+           int next_cipher = ssh->cfg.ssh_cipherlist[i];\r
+           if (next_cipher == CIPHER_WARN) {\r
+               /* If/when we choose a cipher, warn about it */\r
+               warn = 1;\r
+           } else if (next_cipher == CIPHER_AES) {\r
+               /* XXX Probably don't need to mention this. */\r
+               logevent("AES not supported in SSH-1, skipping");\r
+           } else {\r
+               switch (next_cipher) {\r
+                 case CIPHER_3DES:     s->cipher_type = SSH_CIPHER_3DES;\r
+                                       cipher_string = "3DES"; break;\r
+                 case CIPHER_BLOWFISH: s->cipher_type = SSH_CIPHER_BLOWFISH;\r
+                                       cipher_string = "Blowfish"; break;\r
+                 case CIPHER_DES:      s->cipher_type = SSH_CIPHER_DES;\r
+                                       cipher_string = "single-DES"; break;\r
+               }\r
+               if (s->supported_ciphers_mask & (1 << s->cipher_type))\r
+                   cipher_chosen = 1;\r
+           }\r
+       }\r
+       if (!cipher_chosen) {\r
+           if ((s->supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)\r
+               bombout(("Server violates SSH-1 protocol by not "\r
+                        "supporting 3DES encryption"));\r
+           else\r
+               /* shouldn't happen */\r
+               bombout(("No supported ciphers found"));\r
+           crStop(0);\r
+       }\r
+\r
+       /* Warn about chosen cipher if necessary. */\r
+       if (warn) {\r
+            ssh_set_frozen(ssh, 1);\r
+           s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,\r
+                              ssh_dialog_callback, ssh);\r
+           if (s->dlgret < 0) {\r
+               do {\r
+                   crReturn(0);\r
+                   if (pktin) {\r
+                       bombout(("Unexpected data from server while waiting"\r
+                                " for user response"));\r
+                       crStop(0);\r
+                   }\r
+               } while (pktin || inlen > 0);\r
+               s->dlgret = ssh->user_response;\r
+           }\r
+            ssh_set_frozen(ssh, 0);\r
+           if (s->dlgret == 0) {\r
+               ssh_disconnect(ssh, "User aborted at cipher warning", NULL,\r
+                              0, TRUE);\r
+               crStop(0);\r
+           }\r
+        }\r
+    }\r
+\r
+    switch (s->cipher_type) {\r
+      case SSH_CIPHER_3DES:\r
+       logevent("Using 3DES encryption");\r
+       break;\r
+      case SSH_CIPHER_DES:\r
+       logevent("Using single-DES encryption");\r
+       break;\r
+      case SSH_CIPHER_BLOWFISH:\r
+       logevent("Using Blowfish encryption");\r
+       break;\r
+    }\r
+\r
+    send_packet(ssh, SSH1_CMSG_SESSION_KEY,\r
+               PKT_CHAR, s->cipher_type,\r
+               PKT_DATA, cookie, 8,\r
+               PKT_CHAR, (s->len * 8) >> 8, PKT_CHAR, (s->len * 8) & 0xFF,\r
+               PKT_DATA, s->rsabuf, s->len,\r
+               PKT_INT, ssh->v1_local_protoflags, PKT_END);\r
+\r
+    logevent("Trying to enable encryption...");\r
+\r
+    sfree(s->rsabuf);\r
+\r
+    ssh->cipher = (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :\r
+                  s->cipher_type == SSH_CIPHER_DES ? &ssh_des :\r
+                  &ssh_3des);\r
+    ssh->v1_cipher_ctx = ssh->cipher->make_context();\r
+    ssh->cipher->sesskey(ssh->v1_cipher_ctx, ssh->session_key);\r
+    logeventf(ssh, "Initialised %s encryption", ssh->cipher->text_name);\r
+\r
+    ssh->crcda_ctx = crcda_make_context();\r
+    logevent("Installing CRC compensation attack detector");\r
+\r
+    if (servkey.modulus) {\r
+       sfree(servkey.modulus);\r
+       servkey.modulus = NULL;\r
+    }\r
+    if (servkey.exponent) {\r
+       sfree(servkey.exponent);\r
+       servkey.exponent = NULL;\r
+    }\r
+    if (hostkey.modulus) {\r
+       sfree(hostkey.modulus);\r
+       hostkey.modulus = NULL;\r
+    }\r
+    if (hostkey.exponent) {\r
+       sfree(hostkey.exponent);\r
+       hostkey.exponent = NULL;\r
+    }\r
+    crWaitUntil(pktin);\r
+\r
+    if (pktin->type != SSH1_SMSG_SUCCESS) {\r
+       bombout(("Encryption not successfully enabled"));\r
+       crStop(0);\r
+    }\r
+\r
+    logevent("Successfully started encryption");\r
+\r
+    fflush(stdout); /* FIXME eh? */\r
+    {\r
+       if (!get_remote_username(&ssh->cfg, s->username,\r
+                                sizeof(s->username))) {\r
+           int ret; /* need not be kept over crReturn */\r
+           s->cur_prompt = new_prompts(ssh->frontend);\r
+           s->cur_prompt->to_server = TRUE;\r
+           s->cur_prompt->name = dupstr("SSH login name");\r
+           add_prompt(s->cur_prompt, dupstr("login as: "), TRUE,\r
+                      lenof(s->username)); \r
+           ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+           while (ret < 0) {\r
+               ssh->send_ok = 1;\r
+               crWaitUntil(!pktin);\r
+               ret = get_userpass_input(s->cur_prompt, in, inlen);\r
+               ssh->send_ok = 0;\r
+           }\r
+           if (!ret) {\r
+               /*\r
+                * Failed to get a username. Terminate.\r
+                */\r
+               free_prompts(s->cur_prompt);\r
+               ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);\r
+               crStop(0);\r
+           }\r
+           memcpy(s->username, s->cur_prompt->prompts[0]->result,\r
+                  lenof(s->username));\r
+           free_prompts(s->cur_prompt);\r
+       }\r
+\r
+       send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END);\r
+       {\r
+           char *userlog = dupprintf("Sent username \"%s\"", s->username);\r
+           logevent(userlog);\r
+           if (flags & FLAG_INTERACTIVE &&\r
+               (!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) {\r
+               c_write_str(ssh, userlog);\r
+               c_write_str(ssh, "\r\n");\r
+           }\r
+           sfree(userlog);\r
+       }\r
+    }\r
+\r
+    crWaitUntil(pktin);\r
+\r
+    if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) {\r
+       /* We must not attempt PK auth. Pretend we've already tried it. */\r
+       s->tried_publickey = s->tried_agent = 1;\r
+    } else {\r
+       s->tried_publickey = s->tried_agent = 0;\r
+    }\r
+    s->tis_auth_refused = s->ccard_auth_refused = 0;\r
+    /*\r
+     * Load the public half of any configured keyfile for later use.\r
+     */\r
+    if (!filename_is_null(ssh->cfg.keyfile)) {\r
+       int keytype;\r
+       logeventf(ssh, "Reading private key file \"%.150s\"",\r
+                 filename_to_str(&ssh->cfg.keyfile));\r
+       keytype = key_type(&ssh->cfg.keyfile);\r
+       if (keytype == SSH_KEYTYPE_SSH1) {\r
+           const char *error;\r
+           if (rsakey_pubblob(&ssh->cfg.keyfile,\r
+                              &s->publickey_blob, &s->publickey_bloblen,\r
+                              &s->publickey_comment, &error)) {\r
+               s->publickey_encrypted = rsakey_encrypted(&ssh->cfg.keyfile,\r
+                                                         NULL);\r
+           } else {\r
+               char *msgbuf;\r
+               logeventf(ssh, "Unable to load private key (%s)", error);\r
+               msgbuf = dupprintf("Unable to load private key file "\r
+                                  "\"%.150s\" (%s)\r\n",\r
+                                  filename_to_str(&ssh->cfg.keyfile),\r
+                                  error);\r
+               c_write_str(ssh, msgbuf);\r
+               sfree(msgbuf);\r
+               s->publickey_blob = NULL;\r
+           }\r
+       } else {\r
+           char *msgbuf;\r
+           logeventf(ssh, "Unable to use this key file (%s)",\r
+                     key_type_to_str(keytype));\r
+           msgbuf = dupprintf("Unable to use key file \"%.150s\""\r
+                              " (%s)\r\n",\r
+                              filename_to_str(&ssh->cfg.keyfile),\r
+                              key_type_to_str(keytype));\r
+           c_write_str(ssh, msgbuf);\r
+           sfree(msgbuf);\r
+           s->publickey_blob = NULL;\r
+       }\r
+    } else\r
+       s->publickey_blob = NULL;\r
+\r
+    while (pktin->type == SSH1_SMSG_FAILURE) {\r
+       s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;\r
+\r
+       if (ssh->cfg.tryagent && agent_exists() && !s->tried_agent) {\r
+           /*\r
+            * Attempt RSA authentication using Pageant.\r
+            */\r
+           void *r;\r
+\r
+           s->authed = FALSE;\r
+           s->tried_agent = 1;\r
+           logevent("Pageant is running. Requesting keys.");\r
+\r
+           /* Request the keys held by the agent. */\r
+           PUT_32BIT(s->request, 1);\r
+           s->request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;\r
+           if (!agent_query(s->request, 5, &r, &s->responselen,\r
+                            ssh_agent_callback, ssh)) {\r
+               do {\r
+                   crReturn(0);\r
+                   if (pktin) {\r
+                       bombout(("Unexpected data from server while waiting"\r
+                                " for agent response"));\r
+                       crStop(0);\r
+                   }\r
+               } while (pktin || inlen > 0);\r
+               r = ssh->agent_response;\r
+               s->responselen = ssh->agent_response_len;\r
+           }\r
+           s->response = (unsigned char *) r;\r
+           if (s->response && s->responselen >= 5 &&\r
+               s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {\r
+               s->p = s->response + 5;\r
+               s->nkeys = GET_32BIT(s->p);\r
+               s->p += 4;\r
+               logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);\r
+               for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {\r
+                   unsigned char *pkblob = s->p;\r
+                   s->p += 4;\r
+                   {\r
+                       int n, ok = FALSE;\r
+                       do {           /* do while (0) to make breaking easy */\r
+                           n = ssh1_read_bignum\r
+                               (s->p, s->responselen-(s->p-s->response),\r
+                                &s->key.exponent);\r
+                           if (n < 0)\r
+                               break;\r
+                           s->p += n;\r
+                           n = ssh1_read_bignum\r
+                               (s->p, s->responselen-(s->p-s->response),\r
+                                &s->key.modulus);\r
+                           if (n < 0)\r
+                           break;\r
+                           s->p += n;\r
+                           if (s->responselen - (s->p-s->response) < 4)\r
+                               break;\r
+                           s->commentlen = GET_32BIT(s->p);\r
+                           s->p += 4;\r
+                           if (s->responselen - (s->p-s->response) <\r
+                               s->commentlen)\r
+                               break;\r
+                           s->commentp = (char *)s->p;\r
+                           s->p += s->commentlen;\r
+                           ok = TRUE;\r
+                       } while (0);\r
+                       if (!ok) {\r
+                           logevent("Pageant key list packet was truncated");\r
+                           break;\r
+                       }\r
+                   }\r
+                   if (s->publickey_blob) {\r
+                       if (!memcmp(pkblob, s->publickey_blob,\r
+                                   s->publickey_bloblen)) {\r
+                           logeventf(ssh, "Pageant key #%d matches "\r
+                                     "configured key file", s->keyi);\r
+                           s->tried_publickey = 1;\r
+                       } else\r
+                           /* Skip non-configured key */\r
+                           continue;\r
+                   }\r
+                   logeventf(ssh, "Trying Pageant key #%d", s->keyi);\r
+                   send_packet(ssh, SSH1_CMSG_AUTH_RSA,\r
+                               PKT_BIGNUM, s->key.modulus, PKT_END);\r
+                   crWaitUntil(pktin);\r
+                   if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {\r
+                       logevent("Key refused");\r
+                       continue;\r
+                   }\r
+                   logevent("Received RSA challenge");\r
+                   if ((s->challenge = ssh1_pkt_getmp(pktin)) == NULL) {\r
+                       bombout(("Server's RSA challenge was badly formatted"));\r
+                       crStop(0);\r
+                   }\r
+\r
+                   {\r
+                       char *agentreq, *q, *ret;\r
+                       void *vret;\r
+                       int len, retlen;\r
+                       len = 1 + 4;   /* message type, bit count */\r
+                       len += ssh1_bignum_length(s->key.exponent);\r
+                       len += ssh1_bignum_length(s->key.modulus);\r
+                       len += ssh1_bignum_length(s->challenge);\r
+                       len += 16;     /* session id */\r
+                       len += 4;      /* response format */\r
+                       agentreq = snewn(4 + len, char);\r
+                       PUT_32BIT(agentreq, len);\r
+                       q = agentreq + 4;\r
+                       *q++ = SSH1_AGENTC_RSA_CHALLENGE;\r
+                       PUT_32BIT(q, bignum_bitcount(s->key.modulus));\r
+                       q += 4;\r
+                       q += ssh1_write_bignum(q, s->key.exponent);\r
+                       q += ssh1_write_bignum(q, s->key.modulus);\r
+                       q += ssh1_write_bignum(q, s->challenge);\r
+                       memcpy(q, s->session_id, 16);\r
+                       q += 16;\r
+                       PUT_32BIT(q, 1);        /* response format */\r
+                       if (!agent_query(agentreq, len + 4, &vret, &retlen,\r
+                                        ssh_agent_callback, ssh)) {\r
+                           sfree(agentreq);\r
+                           do {\r
+                               crReturn(0);\r
+                               if (pktin) {\r
+                                   bombout(("Unexpected data from server"\r
+                                            " while waiting for agent"\r
+                                            " response"));\r
+                                   crStop(0);\r
+                               }\r
+                           } while (pktin || inlen > 0);\r
+                           vret = ssh->agent_response;\r
+                           retlen = ssh->agent_response_len;\r
+                       } else\r
+                           sfree(agentreq);\r
+                       ret = vret;\r
+                       if (ret) {\r
+                           if (ret[4] == SSH1_AGENT_RSA_RESPONSE) {\r
+                               logevent("Sending Pageant's response");\r
+                               send_packet(ssh, SSH1_CMSG_AUTH_RSA_RESPONSE,\r
+                                           PKT_DATA, ret + 5, 16,\r
+                                           PKT_END);\r
+                               sfree(ret);\r
+                               crWaitUntil(pktin);\r
+                               if (pktin->type == SSH1_SMSG_SUCCESS) {\r
+                                   logevent\r
+                                       ("Pageant's response accepted");\r
+                                   if (flags & FLAG_VERBOSE) {\r
+                                       c_write_str(ssh, "Authenticated using"\r
+                                                   " RSA key \"");\r
+                                       c_write(ssh, s->commentp,\r
+                                               s->commentlen);\r
+                                       c_write_str(ssh, "\" from agent\r\n");\r
+                                   }\r
+                                   s->authed = TRUE;\r
+                               } else\r
+                                   logevent\r
+                                       ("Pageant's response not accepted");\r
+                           } else {\r
+                               logevent\r
+                                   ("Pageant failed to answer challenge");\r
+                               sfree(ret);\r
+                           }\r
+                       } else {\r
+                           logevent("No reply received from Pageant");\r
+                       }\r
+                   }\r
+                   freebn(s->key.exponent);\r
+                   freebn(s->key.modulus);\r
+                   freebn(s->challenge);\r
+                   if (s->authed)\r
+                       break;\r
+               }\r
+               sfree(s->response);\r
+               if (s->publickey_blob && !s->tried_publickey)\r
+                   logevent("Configured key file not in Pageant");\r
+           } else {\r
+                logevent("Failed to get reply from Pageant");\r
+            }\r
+           if (s->authed)\r
+               break;\r
+       }\r
+       if (s->publickey_blob && !s->tried_publickey) {\r
+           /*\r
+            * Try public key authentication with the specified\r
+            * key file.\r
+            */\r
+           int got_passphrase; /* need not be kept over crReturn */\r
+           if (flags & FLAG_VERBOSE)\r
+               c_write_str(ssh, "Trying public key authentication.\r\n");\r
+           logeventf(ssh, "Trying public key \"%s\"",\r
+                     filename_to_str(&ssh->cfg.keyfile));\r
+           s->tried_publickey = 1;\r
+           got_passphrase = FALSE;\r
+           while (!got_passphrase) {\r
+               /*\r
+                * Get a passphrase, if necessary.\r
+                */\r
+               char *passphrase = NULL;    /* only written after crReturn */\r
+               const char *error;\r
+               if (!s->publickey_encrypted) {\r
+                   if (flags & FLAG_VERBOSE)\r
+                       c_write_str(ssh, "No passphrase required.\r\n");\r
+                   passphrase = NULL;\r
+               } else {\r
+                   int ret; /* need not be kept over crReturn */\r
+                   s->cur_prompt = new_prompts(ssh->frontend);\r
+                   s->cur_prompt->to_server = FALSE;\r
+                   s->cur_prompt->name = dupstr("SSH key passphrase");\r
+                   add_prompt(s->cur_prompt,\r
+                              dupprintf("Passphrase for key \"%.100s\": ",\r
+                                        s->publickey_comment),\r
+                              FALSE, SSH_MAX_PASSWORD_LEN);\r
+                   ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+                   while (ret < 0) {\r
+                       ssh->send_ok = 1;\r
+                       crWaitUntil(!pktin);\r
+                       ret = get_userpass_input(s->cur_prompt, in, inlen);\r
+                       ssh->send_ok = 0;\r
+                   }\r
+                   if (!ret) {\r
+                       /* Failed to get a passphrase. Terminate. */\r
+                       free_prompts(s->cur_prompt);\r
+                       ssh_disconnect(ssh, NULL, "Unable to authenticate",\r
+                                      0, TRUE);\r
+                       crStop(0);\r
+                   }\r
+                   passphrase = dupstr(s->cur_prompt->prompts[0]->result);\r
+                   free_prompts(s->cur_prompt);\r
+               }\r
+               /*\r
+                * Try decrypting key with passphrase.\r
+                */\r
+               ret = loadrsakey(&ssh->cfg.keyfile, &s->key, passphrase,\r
+                                &error);\r
+               if (passphrase) {\r
+                   memset(passphrase, 0, strlen(passphrase));\r
+                   sfree(passphrase);\r
+               }\r
+               if (ret == 1) {\r
+                   /* Correct passphrase. */\r
+                   got_passphrase = TRUE;\r
+               } else if (ret == 0) {\r
+                   c_write_str(ssh, "Couldn't load private key from ");\r
+                   c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile));\r
+                   c_write_str(ssh, " (");\r
+                   c_write_str(ssh, error);\r
+                   c_write_str(ssh, ").\r\n");\r
+                   got_passphrase = FALSE;\r
+                   break;             /* go and try something else */\r
+               } else if (ret == -1) {\r
+                   c_write_str(ssh, "Wrong passphrase.\r\n"); /* FIXME */\r
+                   got_passphrase = FALSE;\r
+                   /* and try again */\r
+               } else {\r
+                   assert(0 && "unexpected return from loadrsakey()");\r
+                   got_passphrase = FALSE;   /* placate optimisers */\r
+               }\r
+           }\r
+\r
+           if (got_passphrase) {\r
+\r
+               /*\r
+                * Send a public key attempt.\r
+                */\r
+               send_packet(ssh, SSH1_CMSG_AUTH_RSA,\r
+                           PKT_BIGNUM, s->key.modulus, PKT_END);\r
+\r
+               crWaitUntil(pktin);\r
+               if (pktin->type == SSH1_SMSG_FAILURE) {\r
+                   c_write_str(ssh, "Server refused our public key.\r\n");\r
+                   continue;          /* go and try something else */\r
+               }\r
+               if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {\r
+                   bombout(("Bizarre response to offer of public key"));\r
+                   crStop(0);\r
+               }\r
+\r
+               {\r
+                   int i;\r
+                   unsigned char buffer[32];\r
+                   Bignum challenge, response;\r
+\r
+                   if ((challenge = ssh1_pkt_getmp(pktin)) == NULL) {\r
+                       bombout(("Server's RSA challenge was badly formatted"));\r
+                       crStop(0);\r
+                   }\r
+                   response = rsadecrypt(challenge, &s->key);\r
+                   freebn(s->key.private_exponent);/* burn the evidence */\r
+\r
+                   for (i = 0; i < 32; i++) {\r
+                       buffer[i] = bignum_byte(response, 31 - i);\r
+                   }\r
+\r
+                   MD5Init(&md5c);\r
+                   MD5Update(&md5c, buffer, 32);\r
+                   MD5Update(&md5c, s->session_id, 16);\r
+                   MD5Final(buffer, &md5c);\r
+\r
+                   send_packet(ssh, SSH1_CMSG_AUTH_RSA_RESPONSE,\r
+                               PKT_DATA, buffer, 16, PKT_END);\r
+\r
+                   freebn(challenge);\r
+                   freebn(response);\r
+               }\r
+\r
+               crWaitUntil(pktin);\r
+               if (pktin->type == SSH1_SMSG_FAILURE) {\r
+                   if (flags & FLAG_VERBOSE)\r
+                       c_write_str(ssh, "Failed to authenticate with"\r
+                                   " our public key.\r\n");\r
+                   continue;          /* go and try something else */\r
+               } else if (pktin->type != SSH1_SMSG_SUCCESS) {\r
+                   bombout(("Bizarre response to RSA authentication response"));\r
+                   crStop(0);\r
+               }\r
+\r
+               break;                 /* we're through! */\r
+           }\r
+\r
+       }\r
+\r
+       /*\r
+        * Otherwise, try various forms of password-like authentication.\r
+        */\r
+       s->cur_prompt = new_prompts(ssh->frontend);\r
+\r
+       if (ssh->cfg.try_tis_auth &&\r
+           (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&\r
+           !s->tis_auth_refused) {\r
+           s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;\r
+           logevent("Requested TIS authentication");\r
+           send_packet(ssh, SSH1_CMSG_AUTH_TIS, PKT_END);\r
+           crWaitUntil(pktin);\r
+           if (pktin->type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {\r
+               logevent("TIS authentication declined");\r
+               if (flags & FLAG_INTERACTIVE)\r
+                   c_write_str(ssh, "TIS authentication refused.\r\n");\r
+               s->tis_auth_refused = 1;\r
+               continue;\r
+           } else {\r
+               char *challenge;\r
+               int challengelen;\r
+               char *instr_suf, *prompt;\r
+\r
+               ssh_pkt_getstring(pktin, &challenge, &challengelen);\r
+               if (!challenge) {\r
+                   bombout(("TIS challenge packet was badly formed"));\r
+                   crStop(0);\r
+               }\r
+               logevent("Received TIS challenge");\r
+               s->cur_prompt->to_server = TRUE;\r
+               s->cur_prompt->name = dupstr("SSH TIS authentication");\r
+               /* Prompt heuristic comes from OpenSSH */\r
+               if (memchr(challenge, '\n', challengelen)) {\r
+                   instr_suf = dupstr("");\r
+                   prompt = dupprintf("%.*s", challengelen, challenge);\r
+               } else {\r
+                   instr_suf = dupprintf("%.*s", challengelen, challenge);\r
+                   prompt = dupstr("Response: ");\r
+               }\r
+               s->cur_prompt->instruction =\r
+                   dupprintf("Using TIS authentication.%s%s",\r
+                             (*instr_suf) ? "\n" : "",\r
+                             instr_suf);\r
+               s->cur_prompt->instr_reqd = TRUE;\r
+               add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN);\r
+               sfree(instr_suf);\r
+           }\r
+       }\r
+       if (ssh->cfg.try_tis_auth &&\r
+           (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&\r
+           !s->ccard_auth_refused) {\r
+           s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;\r
+           logevent("Requested CryptoCard authentication");\r
+           send_packet(ssh, SSH1_CMSG_AUTH_CCARD, PKT_END);\r
+           crWaitUntil(pktin);\r
+           if (pktin->type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {\r
+               logevent("CryptoCard authentication declined");\r
+               c_write_str(ssh, "CryptoCard authentication refused.\r\n");\r
+               s->ccard_auth_refused = 1;\r
+               continue;\r
+           } else {\r
+               char *challenge;\r
+               int challengelen;\r
+               char *instr_suf, *prompt;\r
+\r
+               ssh_pkt_getstring(pktin, &challenge, &challengelen);\r
+               if (!challenge) {\r
+                   bombout(("CryptoCard challenge packet was badly formed"));\r
+                   crStop(0);\r
+               }\r
+               logevent("Received CryptoCard challenge");\r
+               s->cur_prompt->to_server = TRUE;\r
+               s->cur_prompt->name = dupstr("SSH CryptoCard authentication");\r
+               s->cur_prompt->name_reqd = FALSE;\r
+               /* Prompt heuristic comes from OpenSSH */\r
+               if (memchr(challenge, '\n', challengelen)) {\r
+                   instr_suf = dupstr("");\r
+                   prompt = dupprintf("%.*s", challengelen, challenge);\r
+               } else {\r
+                   instr_suf = dupprintf("%.*s", challengelen, challenge);\r
+                   prompt = dupstr("Response: ");\r
+               }\r
+               s->cur_prompt->instruction =\r
+                   dupprintf("Using CryptoCard authentication.%s%s",\r
+                             (*instr_suf) ? "\n" : "",\r
+                             instr_suf);\r
+               s->cur_prompt->instr_reqd = TRUE;\r
+               add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN);\r
+               sfree(instr_suf);\r
+           }\r
+       }\r
+       if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {\r
+           if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) {\r
+               bombout(("No supported authentication methods available"));\r
+               crStop(0);\r
+           }\r
+           s->cur_prompt->to_server = TRUE;\r
+           s->cur_prompt->name = dupstr("SSH password");\r
+           add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",\r
+                                               s->username, ssh->savedhost),\r
+                      FALSE, SSH_MAX_PASSWORD_LEN);\r
+       }\r
+\r
+       /*\r
+        * Show password prompt, having first obtained it via a TIS\r
+        * or CryptoCard exchange if we're doing TIS or CryptoCard\r
+        * authentication.\r
+        */\r
+       {\r
+           int ret; /* need not be kept over crReturn */\r
+           ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+           while (ret < 0) {\r
+               ssh->send_ok = 1;\r
+               crWaitUntil(!pktin);\r
+               ret = get_userpass_input(s->cur_prompt, in, inlen);\r
+               ssh->send_ok = 0;\r
+           }\r
+           if (!ret) {\r
+               /*\r
+                * Failed to get a password (for example\r
+                * because one was supplied on the command line\r
+                * which has already failed to work). Terminate.\r
+                */\r
+               free_prompts(s->cur_prompt);\r
+               ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, TRUE);\r
+               crStop(0);\r
+           }\r
+       }\r
+\r
+       if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {\r
+           /*\r
+            * Defence against traffic analysis: we send a\r
+            * whole bunch of packets containing strings of\r
+            * different lengths. One of these strings is the\r
+            * password, in a SSH1_CMSG_AUTH_PASSWORD packet.\r
+            * The others are all random data in\r
+            * SSH1_MSG_IGNORE packets. This way a passive\r
+            * listener can't tell which is the password, and\r
+            * hence can't deduce the password length.\r
+            * \r
+            * Anybody with a password length greater than 16\r
+            * bytes is going to have enough entropy in their\r
+            * password that a listener won't find it _that_\r
+            * much help to know how long it is. So what we'll\r
+            * do is:\r
+            * \r
+            *  - if password length < 16, we send 15 packets\r
+            *    containing string lengths 1 through 15\r
+            * \r
+            *  - otherwise, we let N be the nearest multiple\r
+            *    of 8 below the password length, and send 8\r
+            *    packets containing string lengths N through\r
+            *    N+7. This won't obscure the order of\r
+            *    magnitude of the password length, but it will\r
+            *    introduce a bit of extra uncertainty.\r
+            * \r
+            * A few servers can't deal with SSH1_MSG_IGNORE, at\r
+            * least in this context. For these servers, we need\r
+            * an alternative defence. We make use of the fact\r
+            * that the password is interpreted as a C string:\r
+            * so we can append a NUL, then some random data.\r
+            * \r
+            * A few servers can deal with neither SSH1_MSG_IGNORE\r
+            * here _nor_ a padded password string.\r
+            * For these servers we are left with no defences\r
+            * against password length sniffing.\r
+            */\r
+           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) &&\r
+               !(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {\r
+               /*\r
+                * The server can deal with SSH1_MSG_IGNORE, so\r
+                * we can use the primary defence.\r
+                */\r
+               int bottom, top, pwlen, i;\r
+               char *randomstr;\r
+\r
+               pwlen = strlen(s->cur_prompt->prompts[0]->result);\r
+               if (pwlen < 16) {\r
+                   bottom = 0;    /* zero length passwords are OK! :-) */\r
+                   top = 15;\r
+               } else {\r
+                   bottom = pwlen & ~7;\r
+                   top = bottom + 7;\r
+               }\r
+\r
+               assert(pwlen >= bottom && pwlen <= top);\r
+\r
+               randomstr = snewn(top + 1, char);\r
+\r
+               for (i = bottom; i <= top; i++) {\r
+                   if (i == pwlen) {\r
+                       defer_packet(ssh, s->pwpkt_type,\r
+                                    PKTT_PASSWORD, PKT_STR,\r
+                                    s->cur_prompt->prompts[0]->result,\r
+                                    PKTT_OTHER, PKT_END);\r
+                   } else {\r
+                       for (j = 0; j < i; j++) {\r
+                           do {\r
+                               randomstr[j] = random_byte();\r
+                           } while (randomstr[j] == '\0');\r
+                       }\r
+                       randomstr[i] = '\0';\r
+                       defer_packet(ssh, SSH1_MSG_IGNORE,\r
+                                    PKT_STR, randomstr, PKT_END);\r
+                   }\r
+               }\r
+               logevent("Sending password with camouflage packets");\r
+               ssh_pkt_defersend(ssh);\r
+               sfree(randomstr);\r
+           } \r
+           else if (!(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {\r
+               /*\r
+                * The server can't deal with SSH1_MSG_IGNORE\r
+                * but can deal with padded passwords, so we\r
+                * can use the secondary defence.\r
+                */\r
+               char string[64];\r
+               char *ss;\r
+               int len;\r
+\r
+               len = strlen(s->cur_prompt->prompts[0]->result);\r
+               if (len < sizeof(string)) {\r
+                   ss = string;\r
+                   strcpy(string, s->cur_prompt->prompts[0]->result);\r
+                   len++;             /* cover the zero byte */\r
+                   while (len < sizeof(string)) {\r
+                       string[len++] = (char) random_byte();\r
+                   }\r
+               } else {\r
+                   ss = s->cur_prompt->prompts[0]->result;\r
+               }\r
+               logevent("Sending length-padded password");\r
+               send_packet(ssh, s->pwpkt_type, PKTT_PASSWORD,\r
+                           PKT_INT, len, PKT_DATA, ss, len,\r
+                           PKTT_OTHER, PKT_END);\r
+           } else {\r
+               /*\r
+                * The server is believed unable to cope with\r
+                * any of our password camouflage methods.\r
+                */\r
+               int len;\r
+               len = strlen(s->cur_prompt->prompts[0]->result);\r
+               logevent("Sending unpadded password");\r
+               send_packet(ssh, s->pwpkt_type,\r
+                           PKTT_PASSWORD, PKT_INT, len,\r
+                           PKT_DATA, s->cur_prompt->prompts[0]->result, len,\r
+                           PKTT_OTHER, PKT_END);\r
+           }\r
+       } else {\r
+           send_packet(ssh, s->pwpkt_type, PKTT_PASSWORD,\r
+                       PKT_STR, s->cur_prompt->prompts[0]->result,\r
+                       PKTT_OTHER, PKT_END);\r
+       }\r
+       logevent("Sent password");\r
+       free_prompts(s->cur_prompt);\r
+       crWaitUntil(pktin);\r
+       if (pktin->type == SSH1_SMSG_FAILURE) {\r
+           if (flags & FLAG_VERBOSE)\r
+               c_write_str(ssh, "Access denied\r\n");\r
+           logevent("Authentication refused");\r
+       } else if (pktin->type != SSH1_SMSG_SUCCESS) {\r
+           bombout(("Strange packet received, type %d", pktin->type));\r
+           crStop(0);\r
+       }\r
+    }\r
+\r
+    /* Clear up */\r
+    if (s->publickey_blob) {\r
+       sfree(s->publickey_blob);\r
+       sfree(s->publickey_comment);\r
+    }\r
+\r
+    logevent("Authentication successful");\r
+\r
+    crFinish(1);\r
+}\r
+\r
+void sshfwd_close(struct ssh_channel *c)\r
+{\r
+    Ssh ssh = c->ssh;\r
+\r
+    if (ssh->state == SSH_STATE_CLOSED)\r
+       return;\r
+\r
+    if (!c->closes) {\r
+       /*\r
+        * If halfopen is true, we have sent\r
+        * CHANNEL_OPEN for this channel, but it hasn't even been\r
+        * acknowledged by the server. So we must set a close flag\r
+        * on it now, and then when the server acks the channel\r
+        * open, we can close it then.\r
+        */\r
+       if (!c->halfopen) {\r
+           if (ssh->version == 1) {\r
+               send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,\r
+                           PKT_END);\r
+               c->closes = 1;                 /* sent MSG_CLOSE */\r
+           } else {\r
+               int bytes_to_send = bufchain_size(&c->v.v2.outbuffer);\r
+               if (bytes_to_send > 0) {\r
+                   /*\r
+                    * If we still have unsent data in our outgoing\r
+                    * buffer for this channel, we can't actually\r
+                    * initiate a close operation yet or that data\r
+                    * will be lost. Instead, set the pending_close\r
+                    * flag so that when we do clear the buffer\r
+                    * we'll start closing the channel.\r
+                    */\r
+                   char logmsg[160] = {'\0'};\r
+                   sprintf(\r
+                           logmsg,\r
+                           "Forwarded port pending to be closed : "\r
+                           "%d bytes remaining",\r
+                           bytes_to_send);\r
+                   logevent(logmsg);\r
+\r
+                   c->pending_close = TRUE;\r
+               } else {\r
+                   /*\r
+                    * No locally buffered data, so we can send the\r
+                    * close message immediately.\r
+                    */\r
+                   struct Packet *pktout;\r
+                   pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);\r
+                   ssh2_pkt_adduint32(pktout, c->remoteid);\r
+                   ssh2_pkt_send(ssh, pktout);\r
+                   c->closes = 1;                     /* sent MSG_CLOSE */\r
+                   logevent("Nothing left to send, closing channel");\r
+               }\r
+           }\r
+       }\r
+\r
+       if (c->type == CHAN_X11) {\r
+           c->u.x11.s = NULL;\r
+           logevent("Forwarded X11 connection terminated");\r
+       } else if (c->type == CHAN_SOCKDATA ||\r
+                  c->type == CHAN_SOCKDATA_DORMANT) {\r
+           c->u.pfd.s = NULL;\r
+           logevent("Forwarded port closed");\r
+       }\r
+    }\r
+}\r
+\r
+int sshfwd_write(struct ssh_channel *c, char *buf, int len)\r
+{\r
+    Ssh ssh = c->ssh;\r
+\r
+    if (ssh->state == SSH_STATE_CLOSED)\r
+       return 0;\r
+\r
+    if (ssh->version == 1) {\r
+       send_packet(ssh, SSH1_MSG_CHANNEL_DATA,\r
+                   PKT_INT, c->remoteid,\r
+                   PKT_INT, len, PKTT_DATA, PKT_DATA, buf, len,\r
+                   PKTT_OTHER, PKT_END);\r
+       /*\r
+        * In SSH-1 we can return 0 here - implying that forwarded\r
+        * connections are never individually throttled - because\r
+        * the only circumstance that can cause throttling will be\r
+        * the whole SSH connection backing up, in which case\r
+        * _everything_ will be throttled as a whole.\r
+        */\r
+       return 0;\r
+    } else {\r
+       ssh2_add_channel_data(c, buf, len);\r
+       return ssh2_try_send(c);\r
+    }\r
+}\r
+\r
+void sshfwd_unthrottle(struct ssh_channel *c, int bufsize)\r
+{\r
+    Ssh ssh = c->ssh;\r
+    int buflimit;\r
+\r
+    if (ssh->state == SSH_STATE_CLOSED)\r
+       return;\r
+\r
+    if (ssh->version == 1) {\r
+       buflimit = SSH1_BUFFER_LIMIT;\r
+    } else {\r
+       buflimit = c->v.v2.locmaxwin;\r
+       ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0);\r
+    }\r
+    if (c->throttling_conn && bufsize <= buflimit) {\r
+       c->throttling_conn = 0;\r
+       ssh_throttle_conn(ssh, -1);\r
+    }\r
+}\r
+\r
+static void ssh_queueing_handler(Ssh ssh, struct Packet *pktin)\r
+{\r
+    struct queued_handler *qh = ssh->qhead;\r
+\r
+    assert(qh != NULL);\r
+\r
+    assert(pktin->type == qh->msg1 || pktin->type == qh->msg2);\r
+\r
+    if (qh->msg1 > 0) {\r
+       assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler);\r
+       ssh->packet_dispatch[qh->msg1] = NULL;\r
+    }\r
+    if (qh->msg2 > 0) {\r
+       assert(ssh->packet_dispatch[qh->msg2] == ssh_queueing_handler);\r
+       ssh->packet_dispatch[qh->msg2] = NULL;\r
+    }\r
+\r
+    if (qh->next) {\r
+       ssh->qhead = qh->next;\r
+\r
+       if (ssh->qhead->msg1 > 0) {\r
+           assert(ssh->packet_dispatch[ssh->qhead->msg1] == NULL);\r
+           ssh->packet_dispatch[ssh->qhead->msg1] = ssh_queueing_handler;\r
+       }\r
+       if (ssh->qhead->msg2 > 0) {\r
+           assert(ssh->packet_dispatch[ssh->qhead->msg2] == NULL);\r
+           ssh->packet_dispatch[ssh->qhead->msg2] = ssh_queueing_handler;\r
+       }\r
+    } else {\r
+       ssh->qhead = ssh->qtail = NULL;\r
+       ssh->packet_dispatch[pktin->type] = NULL;\r
+    }\r
+\r
+    qh->handler(ssh, pktin, qh->ctx);\r
+\r
+    sfree(qh);\r
+}\r
+\r
+static void ssh_queue_handler(Ssh ssh, int msg1, int msg2,\r
+                             chandler_fn_t handler, void *ctx)\r
+{\r
+    struct queued_handler *qh;\r
+\r
+    qh = snew(struct queued_handler);\r
+    qh->msg1 = msg1;\r
+    qh->msg2 = msg2;\r
+    qh->handler = handler;\r
+    qh->ctx = ctx;\r
+    qh->next = NULL;\r
+\r
+    if (ssh->qtail == NULL) {\r
+       ssh->qhead = qh;\r
+\r
+       if (qh->msg1 > 0) {\r
+           assert(ssh->packet_dispatch[qh->msg1] == NULL);\r
+           ssh->packet_dispatch[qh->msg1] = ssh_queueing_handler;\r
+       }\r
+       if (qh->msg2 > 0) {\r
+           assert(ssh->packet_dispatch[qh->msg2] == NULL);\r
+           ssh->packet_dispatch[qh->msg2] = ssh_queueing_handler;\r
+       }\r
+    } else {\r
+       ssh->qtail->next = qh;\r
+    }\r
+    ssh->qtail = qh;\r
+}\r
+\r
+static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx)\r
+{\r
+    struct ssh_rportfwd *rpf, *pf = (struct ssh_rportfwd *)ctx;\r
+\r
+    if (pktin->type == (ssh->version == 1 ? SSH1_SMSG_SUCCESS :\r
+                       SSH2_MSG_REQUEST_SUCCESS)) {\r
+       logeventf(ssh, "Remote port forwarding from %s enabled",\r
+                 pf->sportdesc);\r
+    } else {\r
+       logeventf(ssh, "Remote port forwarding from %s refused",\r
+                 pf->sportdesc);\r
+\r
+       rpf = del234(ssh->rportfwds, pf);\r
+       assert(rpf == pf);\r
+       pf->pfrec->remote = NULL;\r
+       free_rportfwd(pf);\r
+    }\r
+}\r
+\r
+static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)\r
+{\r
+    const char *portfwd_strptr = cfg->portfwd;\r
+    struct ssh_portfwd *epf;\r
+    int i;\r
+\r
+    if (!ssh->portfwds) {\r
+       ssh->portfwds = newtree234(ssh_portcmp);\r
+    } else {\r
+       /*\r
+        * Go through the existing port forwardings and tag them\r
+        * with status==DESTROY. Any that we want to keep will be\r
+        * re-enabled (status==KEEP) as we go through the\r
+        * configuration and find out which bits are the same as\r
+        * they were before.\r
+        */\r
+       struct ssh_portfwd *epf;\r
+       int i;\r
+       for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)\r
+           epf->status = DESTROY;\r
+    }\r
+\r
+    while (*portfwd_strptr) {\r
+       char address_family, type;\r
+       int sport,dport,sserv,dserv;\r
+       char sports[256], dports[256], saddr[256], host[256];\r
+       int n;\r
+\r
+       address_family = 'A';\r
+       type = 'L';\r
+       if (*portfwd_strptr == 'A' ||\r
+           *portfwd_strptr == '4' ||\r
+           *portfwd_strptr == '6')\r
+           address_family = *portfwd_strptr++;\r
+       if (*portfwd_strptr == 'L' ||\r
+           *portfwd_strptr == 'R' ||\r
+           *portfwd_strptr == 'D')\r
+           type = *portfwd_strptr++;\r
+\r
+       saddr[0] = '\0';\r
+\r
+       n = 0;\r
+       while (*portfwd_strptr && *portfwd_strptr != '\t') {\r
+           if (*portfwd_strptr == ':') {\r
+               /*\r
+                * We've seen a colon in the middle of the\r
+                * source port number. This means that\r
+                * everything we've seen until now is the\r
+                * source _address_, so we'll move it into\r
+                * saddr and start sports from the beginning\r
+                * again.\r
+                */\r
+               portfwd_strptr++;\r
+               sports[n] = '\0';\r
+               if (ssh->version == 1 && type == 'R') {\r
+                   logeventf(ssh, "SSH-1 cannot handle remote source address "\r
+                             "spec \"%s\"; ignoring", sports);\r
+               } else\r
+                   strcpy(saddr, sports);\r
+               n = 0;\r
+           }\r
+           if (n < lenof(sports)-1) sports[n++] = *portfwd_strptr++;\r
+       }\r
+       sports[n] = 0;\r
+       if (type != 'D') {\r
+           if (*portfwd_strptr == '\t')\r
+               portfwd_strptr++;\r
+           n = 0;\r
+           while (*portfwd_strptr && *portfwd_strptr != ':') {\r
+               if (n < lenof(host)-1) host[n++] = *portfwd_strptr++;\r
+           }\r
+           host[n] = 0;\r
+           if (*portfwd_strptr == ':')\r
+               portfwd_strptr++;\r
+           n = 0;\r
+           while (*portfwd_strptr) {\r
+               if (n < lenof(dports)-1) dports[n++] = *portfwd_strptr++;\r
+           }\r
+           dports[n] = 0;\r
+           portfwd_strptr++;\r
+           dport = atoi(dports);\r
+           dserv = 0;\r
+           if (dport == 0) {\r
+               dserv = 1;\r
+               dport = net_service_lookup(dports);\r
+               if (!dport) {\r
+                   logeventf(ssh, "Service lookup failed for destination"\r
+                             " port \"%s\"", dports);\r
+               }\r
+           }\r
+       } else {\r
+           while (*portfwd_strptr) portfwd_strptr++;\r
+           host[0] = 0;\r
+           dports[0] = 0;\r
+           dport = dserv = -1;\r
+           portfwd_strptr++;          /* eat the NUL and move to next one */\r
+       }\r
+       sport = atoi(sports);\r
+       sserv = 0;\r
+       if (sport == 0) {\r
+           sserv = 1;\r
+           sport = net_service_lookup(sports);\r
+           if (!sport) {\r
+               logeventf(ssh, "Service lookup failed for source"\r
+                         " port \"%s\"", sports);\r
+           }\r
+       }\r
+       if (sport && dport) {\r
+           /* Set up a description of the source port. */\r
+           struct ssh_portfwd *pfrec, *epfrec;\r
+\r
+           pfrec = snew(struct ssh_portfwd);\r
+           pfrec->type = type;\r
+           pfrec->saddr = *saddr ? dupstr(saddr) : NULL;\r
+           pfrec->sserv = sserv ? dupstr(sports) : NULL;\r
+           pfrec->sport = sport;\r
+           pfrec->daddr = *host ? dupstr(host) : NULL;\r
+           pfrec->dserv = dserv ? dupstr(dports) : NULL;\r
+           pfrec->dport = dport;\r
+           pfrec->local = NULL;\r
+           pfrec->remote = NULL;\r
+           pfrec->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :\r
+                                   address_family == '6' ? ADDRTYPE_IPV6 :\r
+                                   ADDRTYPE_UNSPEC);\r
+\r
+           epfrec = add234(ssh->portfwds, pfrec);\r
+           if (epfrec != pfrec) {\r
+               if (epfrec->status == DESTROY) {\r
+                   /*\r
+                    * We already have a port forwarding up and running\r
+                    * with precisely these parameters. Hence, no need\r
+                    * to do anything; simply re-tag the existing one\r
+                    * as KEEP.\r
+                    */\r
+                   epfrec->status = KEEP;\r
+               }\r
+               /*\r
+                * Anything else indicates that there was a duplicate\r
+                * in our input, which we'll silently ignore.\r
+                */\r
+               free_portfwd(pfrec);\r
+           } else {\r
+               pfrec->status = CREATE;\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Now go through and destroy any port forwardings which were\r
+     * not re-enabled.\r
+     */\r
+    for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)\r
+       if (epf->status == DESTROY) {\r
+           char *message;\r
+\r
+           message = dupprintf("%s port forwarding from %s%s%d",\r
+                               epf->type == 'L' ? "local" :\r
+                               epf->type == 'R' ? "remote" : "dynamic",\r
+                               epf->saddr ? epf->saddr : "",\r
+                               epf->saddr ? ":" : "",\r
+                               epf->sport);\r
+\r
+           if (epf->type != 'D') {\r
+               char *msg2 = dupprintf("%s to %s:%d", message,\r
+                                      epf->daddr, epf->dport);\r
+               sfree(message);\r
+               message = msg2;\r
+           }\r
+\r
+           logeventf(ssh, "Cancelling %s", message);\r
+           sfree(message);\r
+\r
+           /* epf->remote or epf->local may be NULL if setting up a\r
+            * forwarding failed. */\r
+           if (epf->remote) {\r
+               struct ssh_rportfwd *rpf = epf->remote;\r
+               struct Packet *pktout;\r
+\r
+               /*\r
+                * Cancel the port forwarding at the server\r
+                * end.\r
+                */\r
+               if (ssh->version == 1) {\r
+                   /*\r
+                    * We cannot cancel listening ports on the\r
+                    * server side in SSH-1! There's no message\r
+                    * to support it. Instead, we simply remove\r
+                    * the rportfwd record from the local end\r
+                    * so that any connections the server tries\r
+                    * to make on it are rejected.\r
+                    */\r
+               } else {\r
+                   pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);\r
+                   ssh2_pkt_addstring(pktout, "cancel-tcpip-forward");\r
+                   ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */\r
+                   if (epf->saddr) {\r
+                       ssh2_pkt_addstring(pktout, epf->saddr);\r
+                   } else if (ssh->cfg.rport_acceptall) {\r
+                       /* XXX: ssh->cfg.rport_acceptall may not represent\r
+                        * what was used to open the original connection,\r
+                        * since it's reconfigurable. */\r
+                       ssh2_pkt_addstring(pktout, "0.0.0.0");\r
+                   } else {\r
+                       ssh2_pkt_addstring(pktout, "127.0.0.1");\r
+                   }\r
+                   ssh2_pkt_adduint32(pktout, epf->sport);\r
+                   ssh2_pkt_send(ssh, pktout);\r
+               }\r
+\r
+               del234(ssh->rportfwds, rpf);\r
+               free_rportfwd(rpf);\r
+           } else if (epf->local) {\r
+               pfd_terminate(epf->local);\r
+           }\r
+\r
+           delpos234(ssh->portfwds, i);\r
+           free_portfwd(epf);\r
+           i--;                       /* so we don't skip one in the list */\r
+       }\r
+\r
+    /*\r
+     * And finally, set up any new port forwardings (status==CREATE).\r
+     */\r
+    for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)\r
+       if (epf->status == CREATE) {\r
+           char *sportdesc, *dportdesc;\r
+           sportdesc = dupprintf("%s%s%s%s%d%s",\r
+                                 epf->saddr ? epf->saddr : "",\r
+                                 epf->saddr ? ":" : "",\r
+                                 epf->sserv ? epf->sserv : "",\r
+                                 epf->sserv ? "(" : "",\r
+                                 epf->sport,\r
+                                 epf->sserv ? ")" : "");\r
+           if (epf->type == 'D') {\r
+               dportdesc = NULL;\r
+           } else {\r
+               dportdesc = dupprintf("%s:%s%s%d%s",\r
+                                     epf->daddr,\r
+                                     epf->dserv ? epf->dserv : "",\r
+                                     epf->dserv ? "(" : "",\r
+                                     epf->dport,\r
+                                     epf->dserv ? ")" : "");\r
+           }\r
+\r
+           if (epf->type == 'L') {\r
+               const char *err = pfd_addforward(epf->daddr, epf->dport,\r
+                                                epf->saddr, epf->sport,\r
+                                                ssh, cfg,\r
+                                                &epf->local,\r
+                                                epf->addressfamily);\r
+\r
+               logeventf(ssh, "Local %sport %s forwarding to %s%s%s",\r
+                         epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :\r
+                         epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",\r
+                         sportdesc, dportdesc,\r
+                         err ? " failed: " : "", err ? err : "");\r
+           } else if (epf->type == 'D') {\r
+               const char *err = pfd_addforward(NULL, -1,\r
+                                                epf->saddr, epf->sport,\r
+                                                ssh, cfg,\r
+                                                &epf->local,\r
+                                                epf->addressfamily);\r
+\r
+               logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s",\r
+                         epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :\r
+                         epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",\r
+                         sportdesc,\r
+                         err ? " failed: " : "", err ? err : "");\r
+           } else {\r
+               struct ssh_rportfwd *pf;\r
+\r
+               /*\r
+                * Ensure the remote port forwardings tree exists.\r
+                */\r
+               if (!ssh->rportfwds) {\r
+                   if (ssh->version == 1)\r
+                       ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);\r
+                   else\r
+                       ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);\r
+               }\r
+\r
+               pf = snew(struct ssh_rportfwd);\r
+               strncpy(pf->dhost, epf->daddr, lenof(pf->dhost)-1);\r
+               pf->dhost[lenof(pf->dhost)-1] = '\0';\r
+               pf->dport = epf->dport;\r
+               pf->sport = epf->sport;\r
+               if (add234(ssh->rportfwds, pf) != pf) {\r
+                   logeventf(ssh, "Duplicate remote port forwarding to %s:%d",\r
+                             epf->daddr, epf->dport);\r
+                   sfree(pf);\r
+               } else {\r
+                   logeventf(ssh, "Requesting remote port %s"\r
+                             " forward to %s", sportdesc, dportdesc);\r
+\r
+                   pf->sportdesc = sportdesc;\r
+                   sportdesc = NULL;\r
+                   epf->remote = pf;\r
+                   pf->pfrec = epf;\r
+\r
+                   if (ssh->version == 1) {\r
+                       send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST,\r
+                                   PKT_INT, epf->sport,\r
+                                   PKT_STR, epf->daddr,\r
+                                   PKT_INT, epf->dport,\r
+                                   PKT_END);\r
+                       ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,\r
+                                         SSH1_SMSG_FAILURE,\r
+                                         ssh_rportfwd_succfail, pf);\r
+                   } else {\r
+                       struct Packet *pktout;\r
+                       pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);\r
+                       ssh2_pkt_addstring(pktout, "tcpip-forward");\r
+                       ssh2_pkt_addbool(pktout, 1);/* want reply */\r
+                       if (epf->saddr) {\r
+                           ssh2_pkt_addstring(pktout, epf->saddr);\r
+                       } else if (cfg->rport_acceptall) {\r
+                           ssh2_pkt_addstring(pktout, "0.0.0.0");\r
+                       } else {\r
+                           ssh2_pkt_addstring(pktout, "127.0.0.1");\r
+                       }\r
+                       ssh2_pkt_adduint32(pktout, epf->sport);\r
+                       ssh2_pkt_send(ssh, pktout);\r
+\r
+                       ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,\r
+                                         SSH2_MSG_REQUEST_FAILURE,\r
+                                         ssh_rportfwd_succfail, pf);\r
+                   }\r
+               }\r
+           }\r
+           sfree(sportdesc);\r
+           sfree(dportdesc);\r
+       }\r
+}\r
+\r
+static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin)\r
+{\r
+    char *string;\r
+    int stringlen, bufsize;\r
+\r
+    ssh_pkt_getstring(pktin, &string, &stringlen);\r
+    if (string == NULL) {\r
+       bombout(("Incoming terminal data packet was badly formed"));\r
+       return;\r
+    }\r
+\r
+    bufsize = from_backend(ssh->frontend, pktin->type == SSH1_SMSG_STDERR_DATA,\r
+                          string, stringlen);\r
+    if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {\r
+       ssh->v1_stdout_throttling = 1;\r
+       ssh_throttle_conn(ssh, +1);\r
+    }\r
+}\r
+\r
+static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* Remote side is trying to open a channel to talk to our\r
+     * X-Server. Give them back a local channel number. */\r
+    struct ssh_channel *c;\r
+    int remoteid = ssh_pkt_getuint32(pktin);\r
+\r
+    logevent("Received X11 connect request");\r
+    /* Refuse if X11 forwarding is disabled. */\r
+    if (!ssh->X11_fwd_enabled) {\r
+       send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,\r
+                   PKT_INT, remoteid, PKT_END);\r
+       logevent("Rejected X11 connect request");\r
+    } else {\r
+       c = snew(struct ssh_channel);\r
+       c->ssh = ssh;\r
+\r
+       if (x11_init(&c->u.x11.s, ssh->x11disp, c,\r
+                    NULL, -1, &ssh->cfg) != NULL) {\r
+           logevent("Opening X11 forward connection failed");\r
+           sfree(c);\r
+           send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,\r
+                       PKT_INT, remoteid, PKT_END);\r
+       } else {\r
+           logevent\r
+               ("Opening X11 forward connection succeeded");\r
+           c->remoteid = remoteid;\r
+           c->halfopen = FALSE;\r
+           c->localid = alloc_channel_id(ssh);\r
+           c->closes = 0;\r
+           c->pending_close = FALSE;\r
+           c->throttling_conn = 0;\r
+           c->type = CHAN_X11; /* identify channel type */\r
+           add234(ssh->channels, c);\r
+           send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,\r
+                       PKT_INT, c->remoteid, PKT_INT,\r
+                       c->localid, PKT_END);\r
+           logevent("Opened X11 forward channel");\r
+       }\r
+    }\r
+}\r
+\r
+static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* Remote side is trying to open a channel to talk to our\r
+     * agent. Give them back a local channel number. */\r
+    struct ssh_channel *c;\r
+    int remoteid = ssh_pkt_getuint32(pktin);\r
+\r
+    /* Refuse if agent forwarding is disabled. */\r
+    if (!ssh->agentfwd_enabled) {\r
+       send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,\r
+                   PKT_INT, remoteid, PKT_END);\r
+    } else {\r
+       c = snew(struct ssh_channel);\r
+       c->ssh = ssh;\r
+       c->remoteid = remoteid;\r
+       c->halfopen = FALSE;\r
+       c->localid = alloc_channel_id(ssh);\r
+       c->closes = 0;\r
+       c->pending_close = FALSE;\r
+       c->throttling_conn = 0;\r
+       c->type = CHAN_AGENT;   /* identify channel type */\r
+       c->u.a.lensofar = 0;\r
+       add234(ssh->channels, c);\r
+       send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,\r
+                   PKT_INT, c->remoteid, PKT_INT, c->localid,\r
+                   PKT_END);\r
+    }\r
+}\r
+\r
+static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* Remote side is trying to open a channel to talk to a\r
+     * forwarded port. Give them back a local channel number. */\r
+    struct ssh_channel *c;\r
+    struct ssh_rportfwd pf, *pfp;\r
+    int remoteid;\r
+    int hostsize, port;\r
+    char *host;\r
+    const char *e;\r
+    c = snew(struct ssh_channel);\r
+    c->ssh = ssh;\r
+\r
+    remoteid = ssh_pkt_getuint32(pktin);\r
+    ssh_pkt_getstring(pktin, &host, &hostsize);\r
+    port = ssh_pkt_getuint32(pktin);\r
+\r
+    if (hostsize >= lenof(pf.dhost))\r
+       hostsize = lenof(pf.dhost)-1;\r
+    memcpy(pf.dhost, host, hostsize);\r
+    pf.dhost[hostsize] = '\0';\r
+    pf.dport = port;\r
+    pfp = find234(ssh->rportfwds, &pf, NULL);\r
+\r
+    if (pfp == NULL) {\r
+       logeventf(ssh, "Rejected remote port open request for %s:%d",\r
+                 pf.dhost, port);\r
+       send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,\r
+                   PKT_INT, remoteid, PKT_END);\r
+    } else {\r
+       logeventf(ssh, "Received remote port open request for %s:%d",\r
+                 pf.dhost, port);\r
+       e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,\r
+                          c, &ssh->cfg, pfp->pfrec->addressfamily);\r
+       if (e != NULL) {\r
+           logeventf(ssh, "Port open failed: %s", e);\r
+           sfree(c);\r
+           send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,\r
+                       PKT_INT, remoteid, PKT_END);\r
+       } else {\r
+           c->remoteid = remoteid;\r
+           c->halfopen = FALSE;\r
+           c->localid = alloc_channel_id(ssh);\r
+           c->closes = 0;\r
+           c->pending_close = FALSE;\r
+           c->throttling_conn = 0;\r
+           c->type = CHAN_SOCKDATA;    /* identify channel type */\r
+           add234(ssh->channels, c);\r
+           send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,\r
+                       PKT_INT, c->remoteid, PKT_INT,\r
+                       c->localid, PKT_END);\r
+           logevent("Forwarded port opened successfully");\r
+       }\r
+    }\r
+}\r
+\r
+static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)\r
+{\r
+    unsigned int remoteid = ssh_pkt_getuint32(pktin);\r
+    unsigned int localid = ssh_pkt_getuint32(pktin);\r
+    struct ssh_channel *c;\r
+\r
+    c = find234(ssh->channels, &remoteid, ssh_channelfind);\r
+    if (c && c->type == CHAN_SOCKDATA_DORMANT) {\r
+       c->remoteid = localid;\r
+       c->halfopen = FALSE;\r
+       c->type = CHAN_SOCKDATA;\r
+       c->throttling_conn = 0;\r
+       pfd_confirm(c->u.pfd.s);\r
+    }\r
+\r
+    if (c && c->closes) {\r
+       /*\r
+        * We have a pending close on this channel,\r
+        * which we decided on before the server acked\r
+        * the channel open. So now we know the\r
+        * remoteid, we can close it again.\r
+        */\r
+       send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE,\r
+                   PKT_INT, c->remoteid, PKT_END);\r
+    }\r
+}\r
+\r
+static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)\r
+{\r
+    unsigned int remoteid = ssh_pkt_getuint32(pktin);\r
+    struct ssh_channel *c;\r
+\r
+    c = find234(ssh->channels, &remoteid, ssh_channelfind);\r
+    if (c && c->type == CHAN_SOCKDATA_DORMANT) {\r
+       logevent("Forwarded connection refused by server");\r
+       pfd_close(c->u.pfd.s);\r
+       del234(ssh->channels, c);\r
+       sfree(c);\r
+    }\r
+}\r
+\r
+static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* Remote side closes a channel. */\r
+    unsigned i = ssh_pkt_getuint32(pktin);\r
+    struct ssh_channel *c;\r
+    c = find234(ssh->channels, &i, ssh_channelfind);\r
+    if (c && !c->halfopen) {\r
+       int closetype;\r
+       closetype =\r
+           (pktin->type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);\r
+\r
+       if ((c->closes == 0) && (c->type == CHAN_X11)) {\r
+           logevent("Forwarded X11 connection terminated");\r
+           assert(c->u.x11.s != NULL);\r
+           x11_close(c->u.x11.s);\r
+           c->u.x11.s = NULL;\r
+       }\r
+       if ((c->closes == 0) && (c->type == CHAN_SOCKDATA)) {\r
+           logevent("Forwarded port closed");\r
+           assert(c->u.pfd.s != NULL);\r
+           pfd_close(c->u.pfd.s);\r
+           c->u.pfd.s = NULL;\r
+       }\r
+\r
+       c->closes |= (closetype << 2);   /* seen this message */\r
+       if (!(c->closes & closetype)) {\r
+           send_packet(ssh, pktin->type, PKT_INT, c->remoteid,\r
+                       PKT_END);\r
+           c->closes |= closetype;      /* sent it too */\r
+       }\r
+\r
+       if (c->closes == 15) {\r
+           del234(ssh->channels, c);\r
+           sfree(c);\r
+       }\r
+    } else {\r
+       bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",\r
+                pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" :\r
+                "_CONFIRMATION", c ? "half-open" : "nonexistent",\r
+                i));\r
+    }\r
+}\r
+\r
+static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* Data sent down one of our channels. */\r
+    int i = ssh_pkt_getuint32(pktin);\r
+    char *p;\r
+    int len;\r
+    struct ssh_channel *c;\r
+\r
+    ssh_pkt_getstring(pktin, &p, &len);\r
+\r
+    c = find234(ssh->channels, &i, ssh_channelfind);\r
+    if (c) {\r
+       int bufsize = 0;\r
+       switch (c->type) {\r
+         case CHAN_X11:\r
+           bufsize = x11_send(c->u.x11.s, p, len);\r
+           break;\r
+         case CHAN_SOCKDATA:\r
+           bufsize = pfd_send(c->u.pfd.s, p, len);\r
+           break;\r
+         case CHAN_AGENT:\r
+           /* Data for an agent message. Buffer it. */\r
+           while (len > 0) {\r
+               if (c->u.a.lensofar < 4) {\r
+                   unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len);\r
+                   memcpy(c->u.a.msglen + c->u.a.lensofar, p,\r
+                          l);\r
+                   p += l;\r
+                   len -= l;\r
+                   c->u.a.lensofar += l;\r
+               }\r
+               if (c->u.a.lensofar == 4) {\r
+                   c->u.a.totallen =\r
+                       4 + GET_32BIT(c->u.a.msglen);\r
+                   c->u.a.message = snewn(c->u.a.totallen,\r
+                                          unsigned char);\r
+                   memcpy(c->u.a.message, c->u.a.msglen, 4);\r
+               }\r
+               if (c->u.a.lensofar >= 4 && len > 0) {\r
+                   unsigned int l =\r
+                       min(c->u.a.totallen - c->u.a.lensofar,\r
+                           (unsigned)len);\r
+                   memcpy(c->u.a.message + c->u.a.lensofar, p,\r
+                          l);\r
+                   p += l;\r
+                   len -= l;\r
+                   c->u.a.lensofar += l;\r
+               }\r
+               if (c->u.a.lensofar == c->u.a.totallen) {\r
+                   void *reply;\r
+                   int replylen;\r
+                   if (agent_query(c->u.a.message,\r
+                                   c->u.a.totallen,\r
+                                   &reply, &replylen,\r
+                                   ssh_agentf_callback, c))\r
+                       ssh_agentf_callback(c, reply, replylen);\r
+                   sfree(c->u.a.message);\r
+                   c->u.a.lensofar = 0;\r
+               }\r
+           }\r
+           bufsize = 0;   /* agent channels never back up */\r
+           break;\r
+       }\r
+       if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {\r
+           c->throttling_conn = 1;\r
+           ssh_throttle_conn(ssh, +1);\r
+       }\r
+    }\r
+}\r
+\r
+static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin)\r
+{\r
+    ssh->exitcode = ssh_pkt_getuint32(pktin);\r
+    logeventf(ssh, "Server sent command exit status %d", ssh->exitcode);\r
+    send_packet(ssh, SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);\r
+    /*\r
+     * In case `helpful' firewalls or proxies tack\r
+     * extra human-readable text on the end of the\r
+     * session which we might mistake for another\r
+     * encrypted packet, we close the session once\r
+     * we've sent EXIT_CONFIRMATION.\r
+     */\r
+    ssh_disconnect(ssh, NULL, NULL, 0, TRUE);\r
+}\r
+\r
+/* Helper function to deal with sending tty modes for REQUEST_PTY */\r
+static void ssh1_send_ttymode(void *data, char *mode, char *val)\r
+{\r
+    struct Packet *pktout = (struct Packet *)data;\r
+    int i = 0;\r
+    unsigned int arg = 0;\r
+    while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++;\r
+    if (i == lenof(ssh_ttymodes)) return;\r
+    switch (ssh_ttymodes[i].type) {\r
+      case TTY_OP_CHAR:\r
+       arg = ssh_tty_parse_specchar(val);\r
+       break;\r
+      case TTY_OP_BOOL:\r
+       arg = ssh_tty_parse_boolean(val);\r
+       break;\r
+    }\r
+    ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);\r
+    ssh2_pkt_addbyte(pktout, arg);\r
+}\r
+\r
+\r
+static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,\r
+                              struct Packet *pktin)\r
+{\r
+    crBegin(ssh->do_ssh1_connection_crstate);\r
+\r
+    ssh->packet_dispatch[SSH1_SMSG_STDOUT_DATA] = \r
+       ssh->packet_dispatch[SSH1_SMSG_STDERR_DATA] =\r
+       ssh1_smsg_stdout_stderr_data;\r
+\r
+    ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_CONFIRMATION] =\r
+       ssh1_msg_channel_open_confirmation;\r
+    ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_FAILURE] =\r
+       ssh1_msg_channel_open_failure;\r
+    ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE] =\r
+       ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION] =\r
+       ssh1_msg_channel_close;\r
+    ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data;\r
+    ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status;\r
+\r
+    if (ssh->cfg.agentfwd && agent_exists()) {\r
+       logevent("Requesting agent forwarding");\r
+       send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);\r
+       do {\r
+           crReturnV;\r
+       } while (!pktin);\r
+       if (pktin->type != SSH1_SMSG_SUCCESS\r
+           && pktin->type != SSH1_SMSG_FAILURE) {\r
+           bombout(("Protocol confusion"));\r
+           crStopV;\r
+       } else if (pktin->type == SSH1_SMSG_FAILURE) {\r
+           logevent("Agent forwarding refused");\r
+       } else {\r
+           logevent("Agent forwarding enabled");\r
+           ssh->agentfwd_enabled = TRUE;\r
+           ssh->packet_dispatch[SSH1_SMSG_AGENT_OPEN] = ssh1_smsg_agent_open;\r
+       }\r
+    }\r
+\r
+    if (ssh->cfg.x11_forward &&\r
+       (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,\r
+                                         ssh->cfg.x11_auth, &ssh->cfg))) {\r
+       logevent("Requesting X11 forwarding");\r
+       /*\r
+        * Note that while we blank the X authentication data here, we don't\r
+        * take any special action to blank the start of an X11 channel,\r
+        * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection\r
+        * without having session blanking enabled is likely to leak your\r
+        * cookie into the log.\r
+        */\r
+       if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {\r
+           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,\r
+                       PKT_STR, ssh->x11disp->remoteauthprotoname,\r
+                       PKTT_PASSWORD,\r
+                       PKT_STR, ssh->x11disp->remoteauthdatastring,\r
+                       PKTT_OTHER,\r
+                       PKT_INT, ssh->x11disp->screennum,\r
+                       PKT_END);\r
+       } else {\r
+           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,\r
+                       PKT_STR, ssh->x11disp->remoteauthprotoname,\r
+                       PKTT_PASSWORD,\r
+                       PKT_STR, ssh->x11disp->remoteauthdatastring,\r
+                       PKTT_OTHER,\r
+                       PKT_END);\r
+       }\r
+       do {\r
+           crReturnV;\r
+       } while (!pktin);\r
+       if (pktin->type != SSH1_SMSG_SUCCESS\r
+           && pktin->type != SSH1_SMSG_FAILURE) {\r
+           bombout(("Protocol confusion"));\r
+           crStopV;\r
+       } else if (pktin->type == SSH1_SMSG_FAILURE) {\r
+           logevent("X11 forwarding refused");\r
+       } else {\r
+           logevent("X11 forwarding enabled");\r
+           ssh->X11_fwd_enabled = TRUE;\r
+           ssh->packet_dispatch[SSH1_SMSG_X11_OPEN] = ssh1_smsg_x11_open;\r
+       }\r
+    }\r
+\r
+    ssh_setup_portfwd(ssh, &ssh->cfg);\r
+    ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;\r
+\r
+    if (!ssh->cfg.nopty) {\r
+       struct Packet *pkt;\r
+       /* Unpick the terminal-speed string. */\r
+       /* XXX perhaps we should allow no speeds to be sent. */\r
+       ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */\r
+       sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed);\r
+       /* Send the pty request. */\r
+       pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY);\r
+       ssh_pkt_addstring(pkt, ssh->cfg.termtype);\r
+       ssh_pkt_adduint32(pkt, ssh->term_height);\r
+       ssh_pkt_adduint32(pkt, ssh->term_width);\r
+       ssh_pkt_adduint32(pkt, 0); /* width in pixels */\r
+       ssh_pkt_adduint32(pkt, 0); /* height in pixels */\r
+       parse_ttymodes(ssh, ssh->cfg.ttymodes,\r
+                      ssh1_send_ttymode, (void *)pkt);\r
+       ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED);\r
+       ssh_pkt_adduint32(pkt, ssh->ispeed);\r
+       ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED);\r
+       ssh_pkt_adduint32(pkt, ssh->ospeed);\r
+       ssh_pkt_addbyte(pkt, SSH_TTY_OP_END);\r
+       s_wrpkt(ssh, pkt);\r
+       ssh->state = SSH_STATE_INTERMED;\r
+       do {\r
+           crReturnV;\r
+       } while (!pktin);\r
+       if (pktin->type != SSH1_SMSG_SUCCESS\r
+           && pktin->type != SSH1_SMSG_FAILURE) {\r
+           bombout(("Protocol confusion"));\r
+           crStopV;\r
+       } else if (pktin->type == SSH1_SMSG_FAILURE) {\r
+           c_write_str(ssh, "Server refused to allocate pty\r\n");\r
+           ssh->editing = ssh->echoing = 1;\r
+       }\r
+       logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",\r
+                 ssh->ospeed, ssh->ispeed);\r
+    } else {\r
+       ssh->editing = ssh->echoing = 1;\r
+    }\r
+\r
+    if (ssh->cfg.compression) {\r
+       send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END);\r
+       do {\r
+           crReturnV;\r
+       } while (!pktin);\r
+       if (pktin->type != SSH1_SMSG_SUCCESS\r
+           && pktin->type != SSH1_SMSG_FAILURE) {\r
+           bombout(("Protocol confusion"));\r
+           crStopV;\r
+       } else if (pktin->type == SSH1_SMSG_FAILURE) {\r
+           c_write_str(ssh, "Server refused to compress\r\n");\r
+       }\r
+       logevent("Started compression");\r
+       ssh->v1_compressing = TRUE;\r
+       ssh->cs_comp_ctx = zlib_compress_init();\r
+       logevent("Initialised zlib (RFC1950) compression");\r
+       ssh->sc_comp_ctx = zlib_decompress_init();\r
+       logevent("Initialised zlib (RFC1950) decompression");\r
+    }\r
+\r
+    /*\r
+     * Start the shell or command.\r
+     * \r
+     * Special case: if the first-choice command is an SSH-2\r
+     * subsystem (hence not usable here) and the second choice\r
+     * exists, we fall straight back to that.\r
+     */\r
+    {\r
+       char *cmd = ssh->cfg.remote_cmd_ptr;\r
+\r
+       if (!cmd) cmd = ssh->cfg.remote_cmd;\r
+       \r
+       if (ssh->cfg.ssh_subsys && ssh->cfg.remote_cmd_ptr2) {\r
+           cmd = ssh->cfg.remote_cmd_ptr2;\r
+           ssh->fallback_cmd = TRUE;\r
+       }\r
+       if (*cmd)\r
+           send_packet(ssh, SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END);\r
+       else\r
+           send_packet(ssh, SSH1_CMSG_EXEC_SHELL, PKT_END);\r
+       logevent("Started session");\r
+    }\r
+\r
+    ssh->state = SSH_STATE_SESSION;\r
+    if (ssh->size_needed)\r
+       ssh_size(ssh, ssh->term_width, ssh->term_height);\r
+    if (ssh->eof_needed)\r
+       ssh_special(ssh, TS_EOF);\r
+\r
+    if (ssh->ldisc)\r
+       ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */\r
+    ssh->send_ok = 1;\r
+    ssh->channels = newtree234(ssh_channelcmp);\r
+    while (1) {\r
+\r
+       /*\r
+        * By this point, most incoming packets are already being\r
+        * handled by the dispatch table, and we need only pay\r
+        * attention to the unusual ones.\r
+        */\r
+\r
+       crReturnV;\r
+       if (pktin) {\r
+           if (pktin->type == SSH1_SMSG_SUCCESS) {\r
+               /* may be from EXEC_SHELL on some servers */\r
+           } else if (pktin->type == SSH1_SMSG_FAILURE) {\r
+               /* may be from EXEC_SHELL on some servers\r
+                * if no pty is available or in other odd cases. Ignore */\r
+           } else {\r
+               bombout(("Strange packet received: type %d", pktin->type));\r
+               crStopV;\r
+           }\r
+       } else {\r
+           while (inlen > 0) {\r
+               int len = min(inlen, 512);\r
+               send_packet(ssh, SSH1_CMSG_STDIN_DATA,\r
+                           PKT_INT, len,  PKTT_DATA, PKT_DATA, in, len,\r
+                           PKTT_OTHER, PKT_END);\r
+               in += len;\r
+               inlen -= len;\r
+           }\r
+       }\r
+    }\r
+\r
+    crFinishV;\r
+}\r
+\r
+/*\r
+ * Handle the top-level SSH-2 protocol.\r
+ */\r
+static void ssh1_msg_debug(Ssh ssh, struct Packet *pktin)\r
+{\r
+    char *msg;\r
+    int msglen;\r
+\r
+    ssh_pkt_getstring(pktin, &msg, &msglen);\r
+    logeventf(ssh, "Remote debug message: %.*s", msglen, msg);\r
+}\r
+\r
+static void ssh1_msg_disconnect(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* log reason code in disconnect message */\r
+    char *msg;\r
+    int msglen;\r
+\r
+    ssh_pkt_getstring(pktin, &msg, &msglen);\r
+    bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg));\r
+}\r
+\r
+static void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* Do nothing, because we're ignoring it! Duhh. */\r
+}\r
+\r
+static void ssh1_protocol_setup(Ssh ssh)\r
+{\r
+    int i;\r
+\r
+    /*\r
+     * Most messages are handled by the coroutines.\r
+     */\r
+    for (i = 0; i < 256; i++)\r
+       ssh->packet_dispatch[i] = NULL;\r
+\r
+    /*\r
+     * These special message types we install handlers for.\r
+     */\r
+    ssh->packet_dispatch[SSH1_MSG_DISCONNECT] = ssh1_msg_disconnect;\r
+    ssh->packet_dispatch[SSH1_MSG_IGNORE] = ssh_msg_ignore;\r
+    ssh->packet_dispatch[SSH1_MSG_DEBUG] = ssh1_msg_debug;\r
+}\r
+\r
+static void ssh1_protocol(Ssh ssh, void *vin, int inlen,\r
+                         struct Packet *pktin)\r
+{\r
+    unsigned char *in=(unsigned char*)vin;\r
+    if (ssh->state == SSH_STATE_CLOSED)\r
+       return;\r
+\r
+    if (pktin && ssh->packet_dispatch[pktin->type]) {\r
+       ssh->packet_dispatch[pktin->type](ssh, pktin);\r
+       return;\r
+    }\r
+\r
+    if (!ssh->protocol_initial_phase_done) {\r
+       if (do_ssh1_login(ssh, in, inlen, pktin))\r
+           ssh->protocol_initial_phase_done = TRUE;\r
+       else\r
+           return;\r
+    }\r
+\r
+    do_ssh1_connection(ssh, in, inlen, pktin);\r
+}\r
+\r
+/*\r
+ * Utility routine for decoding comma-separated strings in KEXINIT.\r
+ */\r
+static int in_commasep_string(char *needle, char *haystack, int haylen)\r
+{\r
+    int needlen;\r
+    if (!needle || !haystack)         /* protect against null pointers */\r
+       return 0;\r
+    needlen = strlen(needle);\r
+    while (1) {\r
+       /*\r
+        * Is it at the start of the string?\r
+        */\r
+       if (haylen >= needlen &&       /* haystack is long enough */\r
+           !memcmp(needle, haystack, needlen) &&       /* initial match */\r
+           (haylen == needlen || haystack[needlen] == ',')\r
+           /* either , or EOS follows */\r
+           )\r
+           return 1;\r
+       /*\r
+        * If not, search for the next comma and resume after that.\r
+        * If no comma found, terminate.\r
+        */\r
+       while (haylen > 0 && *haystack != ',')\r
+           haylen--, haystack++;\r
+       if (haylen == 0)\r
+           return 0;\r
+       haylen--, haystack++;          /* skip over comma itself */\r
+    }\r
+}\r
+\r
+/*\r
+ * Similar routine for checking whether we have the first string in a list.\r
+ */\r
+static int first_in_commasep_string(char *needle, char *haystack, int haylen)\r
+{\r
+    int needlen;\r
+    if (!needle || !haystack)         /* protect against null pointers */\r
+       return 0;\r
+    needlen = strlen(needle);\r
+    /*\r
+     * Is it at the start of the string?\r
+     */\r
+    if (haylen >= needlen &&       /* haystack is long enough */\r
+       !memcmp(needle, haystack, needlen) &&   /* initial match */\r
+       (haylen == needlen || haystack[needlen] == ',')\r
+       /* either , or EOS follows */\r
+       )\r
+       return 1;\r
+    return 0;\r
+}\r
+\r
+\r
+/*\r
+ * SSH-2 key creation method.\r
+ * (Currently assumes 2 lots of any hash are sufficient to generate\r
+ * keys/IVs for any cipher/MAC. SSH2_MKKEY_ITERS documents this assumption.)\r
+ */\r
+#define SSH2_MKKEY_ITERS (2)\r
+static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr,\r
+                      unsigned char *keyspace)\r
+{\r
+    const struct ssh_hash *h = ssh->kex->hash;\r
+    void *s;\r
+    /* First hlen bytes. */\r
+    s = h->init();\r
+    if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))\r
+       hash_mpint(h, s, K);\r
+    h->bytes(s, H, h->hlen);\r
+    h->bytes(s, &chr, 1);\r
+    h->bytes(s, ssh->v2_session_id, ssh->v2_session_id_len);\r
+    h->final(s, keyspace);\r
+    /* Next hlen bytes. */\r
+    s = h->init();\r
+    if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))\r
+       hash_mpint(h, s, K);\r
+    h->bytes(s, H, h->hlen);\r
+    h->bytes(s, keyspace, h->hlen);\r
+    h->final(s, keyspace + h->hlen);\r
+}\r
+\r
+/*\r
+ * Handle the SSH-2 transport layer.\r
+ */\r
+static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,\r
+                            struct Packet *pktin)\r
+{\r
+    unsigned char *in = (unsigned char *)vin;\r
+    struct do_ssh2_transport_state {\r
+       int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher;\r
+       Bignum p, g, e, f, K;\r
+       void *our_kexinit;\r
+       int our_kexinitlen;\r
+       int kex_init_value, kex_reply_value;\r
+       const struct ssh_mac **maclist;\r
+       int nmacs;\r
+       const struct ssh2_cipher *cscipher_tobe;\r
+       const struct ssh2_cipher *sccipher_tobe;\r
+       const struct ssh_mac *csmac_tobe;\r
+       const struct ssh_mac *scmac_tobe;\r
+       const struct ssh_compress *cscomp_tobe;\r
+       const struct ssh_compress *sccomp_tobe;\r
+       char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;\r
+       int hostkeylen, siglen, rsakeylen;\r
+       void *hkey;                    /* actual host key */\r
+       void *rsakey;                  /* for RSA kex */\r
+       unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];\r
+       int n_preferred_kex;\r
+       const struct ssh_kexes *preferred_kex[KEX_MAX];\r
+       int n_preferred_ciphers;\r
+       const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];\r
+       const struct ssh_compress *preferred_comp;\r
+       int userauth_succeeded;     /* for delayed compression */\r
+       int pending_compression;\r
+       int got_session_id, activated_authconn;\r
+       struct Packet *pktout;\r
+        int dlgret;\r
+       int guessok;\r
+       int ignorepkt;\r
+    };\r
+    crState(do_ssh2_transport_state);\r
+\r
+    crBegin(ssh->do_ssh2_transport_crstate);\r
+\r
+    s->cscipher_tobe = s->sccipher_tobe = NULL;\r
+    s->csmac_tobe = s->scmac_tobe = NULL;\r
+    s->cscomp_tobe = s->sccomp_tobe = NULL;\r
+\r
+    s->got_session_id = s->activated_authconn = FALSE;\r
+    s->userauth_succeeded = FALSE;\r
+    s->pending_compression = FALSE;\r
+\r
+    /*\r
+     * Be prepared to work around the buggy MAC problem.\r
+     */\r
+    if (ssh->remote_bugs & BUG_SSH2_HMAC)\r
+       s->maclist = buggymacs, s->nmacs = lenof(buggymacs);\r
+    else\r
+       s->maclist = macs, s->nmacs = lenof(macs);\r
+\r
+  begin_key_exchange:\r
+    ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;\r
+    {\r
+       int i, j, commalist_started;\r
+\r
+       /*\r
+        * Set up the preferred key exchange. (NULL => warn below here)\r
+        */\r
+       s->n_preferred_kex = 0;\r
+       for (i = 0; i < KEX_MAX; i++) {\r
+           switch (ssh->cfg.ssh_kexlist[i]) {\r
+             case KEX_DHGEX:\r
+               s->preferred_kex[s->n_preferred_kex++] =\r
+                   &ssh_diffiehellman_gex;\r
+               break;\r
+             case KEX_DHGROUP14:\r
+               s->preferred_kex[s->n_preferred_kex++] =\r
+                   &ssh_diffiehellman_group14;\r
+               break;\r
+             case KEX_DHGROUP1:\r
+               s->preferred_kex[s->n_preferred_kex++] =\r
+                   &ssh_diffiehellman_group1;\r
+               break;\r
+             case KEX_RSA:\r
+               s->preferred_kex[s->n_preferred_kex++] =\r
+                   &ssh_rsa_kex;\r
+               break;\r
+             case KEX_WARN:\r
+               /* Flag for later. Don't bother if it's the last in\r
+                * the list. */\r
+               if (i < KEX_MAX - 1) {\r
+                   s->preferred_kex[s->n_preferred_kex++] = NULL;\r
+               }\r
+               break;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Set up the preferred ciphers. (NULL => warn below here)\r
+        */\r
+       s->n_preferred_ciphers = 0;\r
+       for (i = 0; i < CIPHER_MAX; i++) {\r
+           switch (ssh->cfg.ssh_cipherlist[i]) {\r
+             case CIPHER_BLOWFISH:\r
+               s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish;\r
+               break;\r
+             case CIPHER_DES:\r
+               if (ssh->cfg.ssh2_des_cbc) {\r
+                   s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des;\r
+               }\r
+               break;\r
+             case CIPHER_3DES:\r
+               s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_3des;\r
+               break;\r
+             case CIPHER_AES:\r
+               s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_aes;\r
+               break;\r
+             case CIPHER_ARCFOUR:\r
+               s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour;\r
+               break;\r
+             case CIPHER_WARN:\r
+               /* Flag for later. Don't bother if it's the last in\r
+                * the list. */\r
+               if (i < CIPHER_MAX - 1) {\r
+                   s->preferred_ciphers[s->n_preferred_ciphers++] = NULL;\r
+               }\r
+               break;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Set up preferred compression.\r
+        */\r
+       if (ssh->cfg.compression)\r
+           s->preferred_comp = &ssh_zlib;\r
+       else\r
+           s->preferred_comp = &ssh_comp_none;\r
+\r
+       /*\r
+        * Enable queueing of outgoing auth- or connection-layer\r
+        * packets while we are in the middle of a key exchange.\r
+        */\r
+       ssh->queueing = TRUE;\r
+\r
+       /*\r
+        * Flag that KEX is in progress.\r
+        */\r
+       ssh->kex_in_progress = TRUE;\r
+\r
+       /*\r
+        * Construct and send our key exchange packet.\r
+        */\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_KEXINIT);\r
+       for (i = 0; i < 16; i++)\r
+           ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte());\r
+       /* List key exchange algorithms. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       commalist_started = 0;\r
+       for (i = 0; i < s->n_preferred_kex; i++) {\r
+           const struct ssh_kexes *k = s->preferred_kex[i];\r
+           if (!k) continue;          /* warning flag */\r
+           for (j = 0; j < k->nkexes; j++) {\r
+               if (commalist_started)\r
+                   ssh2_pkt_addstring_str(s->pktout, ",");\r
+               ssh2_pkt_addstring_str(s->pktout, k->list[j]->name);\r
+               commalist_started = 1;\r
+           }\r
+       }\r
+       /* List server host key algorithms. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       for (i = 0; i < lenof(hostkey_algs); i++) {\r
+           ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name);\r
+           if (i < lenof(hostkey_algs) - 1)\r
+               ssh2_pkt_addstring_str(s->pktout, ",");\r
+       }\r
+       /* List client->server encryption algorithms. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       commalist_started = 0;\r
+       for (i = 0; i < s->n_preferred_ciphers; i++) {\r
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];\r
+           if (!c) continue;          /* warning flag */\r
+           for (j = 0; j < c->nciphers; j++) {\r
+               if (commalist_started)\r
+                   ssh2_pkt_addstring_str(s->pktout, ",");\r
+               ssh2_pkt_addstring_str(s->pktout, c->list[j]->name);\r
+               commalist_started = 1;\r
+           }\r
+       }\r
+       /* List server->client encryption algorithms. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       commalist_started = 0;\r
+       for (i = 0; i < s->n_preferred_ciphers; i++) {\r
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];\r
+           if (!c) continue; /* warning flag */\r
+           for (j = 0; j < c->nciphers; j++) {\r
+               if (commalist_started)\r
+                   ssh2_pkt_addstring_str(s->pktout, ",");\r
+               ssh2_pkt_addstring_str(s->pktout, c->list[j]->name);\r
+               commalist_started = 1;\r
+           }\r
+       }\r
+       /* List client->server MAC algorithms. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       for (i = 0; i < s->nmacs; i++) {\r
+           ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name);\r
+           if (i < s->nmacs - 1)\r
+               ssh2_pkt_addstring_str(s->pktout, ",");\r
+       }\r
+       /* List server->client MAC algorithms. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       for (i = 0; i < s->nmacs; i++) {\r
+           ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name);\r
+           if (i < s->nmacs - 1)\r
+               ssh2_pkt_addstring_str(s->pktout, ",");\r
+       }\r
+       /* List client->server compression algorithms,\r
+        * then server->client compression algorithms. (We use the\r
+        * same set twice.) */\r
+       for (j = 0; j < 2; j++) {\r
+           ssh2_pkt_addstring_start(s->pktout);\r
+           assert(lenof(compressions) > 1);\r
+           /* Prefer non-delayed versions */\r
+           ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);\r
+           /* We don't even list delayed versions of algorithms until\r
+            * they're allowed to be used, to avoid a race. See the end of\r
+            * this function. */\r
+           if (s->userauth_succeeded && s->preferred_comp->delayed_name) {\r
+               ssh2_pkt_addstring_str(s->pktout, ",");\r
+               ssh2_pkt_addstring_str(s->pktout,\r
+                                      s->preferred_comp->delayed_name);\r
+           }\r
+           for (i = 0; i < lenof(compressions); i++) {\r
+               const struct ssh_compress *c = compressions[i];\r
+               if (c != s->preferred_comp) {\r
+                   ssh2_pkt_addstring_str(s->pktout, ",");\r
+                   ssh2_pkt_addstring_str(s->pktout, c->name);\r
+                   if (s->userauth_succeeded && c->delayed_name) {\r
+                       ssh2_pkt_addstring_str(s->pktout, ",");\r
+                       ssh2_pkt_addstring_str(s->pktout, c->delayed_name);\r
+                   }\r
+               }\r
+           }\r
+       }\r
+       /* List client->server languages. Empty list. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       /* List server->client languages. Empty list. */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       /* First KEX packet does _not_ follow, because we're not that brave. */\r
+       ssh2_pkt_addbool(s->pktout, FALSE);\r
+       /* Reserved. */\r
+       ssh2_pkt_adduint32(s->pktout, 0);\r
+    }\r
+\r
+    s->our_kexinitlen = s->pktout->length - 5;\r
+    s->our_kexinit = snewn(s->our_kexinitlen, unsigned char);\r
+    memcpy(s->our_kexinit, s->pktout->data + 5, s->our_kexinitlen); \r
+\r
+    ssh2_pkt_send_noqueue(ssh, s->pktout);\r
+\r
+    if (!pktin)\r
+       crWaitUntil(pktin);\r
+\r
+    /*\r
+     * Now examine the other side's KEXINIT to see what we're up\r
+     * to.\r
+     */\r
+    {\r
+       char *str, *preferred;\r
+       int i, j, len;\r
+\r
+       if (pktin->type != SSH2_MSG_KEXINIT) {\r
+           bombout(("expected key exchange packet from server"));\r
+           crStop(0);\r
+       }\r
+       ssh->kex = NULL;\r
+       ssh->hostkey = NULL;\r
+       s->cscipher_tobe = NULL;\r
+       s->sccipher_tobe = NULL;\r
+       s->csmac_tobe = NULL;\r
+       s->scmac_tobe = NULL;\r
+       s->cscomp_tobe = NULL;\r
+       s->sccomp_tobe = NULL;\r
+       s->warn_kex = s->warn_cscipher = s->warn_sccipher = FALSE;\r
+\r
+       pktin->savedpos += 16;          /* skip garbage cookie */\r
+       ssh_pkt_getstring(pktin, &str, &len);    /* key exchange algorithms */\r
+\r
+       preferred = NULL;\r
+       for (i = 0; i < s->n_preferred_kex; i++) {\r
+           const struct ssh_kexes *k = s->preferred_kex[i];\r
+           if (!k) {\r
+               s->warn_kex = TRUE;\r
+           } else {\r
+               for (j = 0; j < k->nkexes; j++) {\r
+                   if (!preferred) preferred = k->list[j]->name;\r
+                   if (in_commasep_string(k->list[j]->name, str, len)) {\r
+                       ssh->kex = k->list[j];\r
+                       break;\r
+                   }\r
+               }\r
+           }\r
+           if (ssh->kex)\r
+               break;\r
+       }\r
+       if (!ssh->kex) {\r
+           bombout(("Couldn't agree a key exchange algorithm (available: %s)",\r
+                    str ? str : "(null)"));\r
+           crStop(0);\r
+       }\r
+       /*\r
+        * Note that the server's guess is considered wrong if it doesn't match\r
+        * the first algorithm in our list, even if it's still the algorithm\r
+        * we end up using.\r
+        */\r
+       s->guessok = first_in_commasep_string(preferred, str, len);\r
+       ssh_pkt_getstring(pktin, &str, &len);    /* host key algorithms */\r
+       for (i = 0; i < lenof(hostkey_algs); i++) {\r
+           if (in_commasep_string(hostkey_algs[i]->name, str, len)) {\r
+               ssh->hostkey = hostkey_algs[i];\r
+               break;\r
+           }\r
+       }\r
+       s->guessok = s->guessok &&\r
+           first_in_commasep_string(hostkey_algs[0]->name, str, len);\r
+       ssh_pkt_getstring(pktin, &str, &len);    /* client->server cipher */\r
+       for (i = 0; i < s->n_preferred_ciphers; i++) {\r
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];\r
+           if (!c) {\r
+               s->warn_cscipher = TRUE;\r
+           } else {\r
+               for (j = 0; j < c->nciphers; j++) {\r
+                   if (in_commasep_string(c->list[j]->name, str, len)) {\r
+                       s->cscipher_tobe = c->list[j];\r
+                       break;\r
+                   }\r
+               }\r
+           }\r
+           if (s->cscipher_tobe)\r
+               break;\r
+       }\r
+       if (!s->cscipher_tobe) {\r
+           bombout(("Couldn't agree a client-to-server cipher (available: %s)",\r
+                    str ? str : "(null)"));\r
+           crStop(0);\r
+       }\r
+\r
+       ssh_pkt_getstring(pktin, &str, &len);    /* server->client cipher */\r
+       for (i = 0; i < s->n_preferred_ciphers; i++) {\r
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];\r
+           if (!c) {\r
+               s->warn_sccipher = TRUE;\r
+           } else {\r
+               for (j = 0; j < c->nciphers; j++) {\r
+                   if (in_commasep_string(c->list[j]->name, str, len)) {\r
+                       s->sccipher_tobe = c->list[j];\r
+                       break;\r
+                   }\r
+               }\r
+           }\r
+           if (s->sccipher_tobe)\r
+               break;\r
+       }\r
+       if (!s->sccipher_tobe) {\r
+           bombout(("Couldn't agree a server-to-client cipher (available: %s)",\r
+                    str ? str : "(null)"));\r
+           crStop(0);\r
+       }\r
+\r
+       ssh_pkt_getstring(pktin, &str, &len);    /* client->server mac */\r
+       for (i = 0; i < s->nmacs; i++) {\r
+           if (in_commasep_string(s->maclist[i]->name, str, len)) {\r
+               s->csmac_tobe = s->maclist[i];\r
+               break;\r
+           }\r
+       }\r
+       ssh_pkt_getstring(pktin, &str, &len);    /* server->client mac */\r
+       for (i = 0; i < s->nmacs; i++) {\r
+           if (in_commasep_string(s->maclist[i]->name, str, len)) {\r
+               s->scmac_tobe = s->maclist[i];\r
+               break;\r
+           }\r
+       }\r
+       ssh_pkt_getstring(pktin, &str, &len);  /* client->server compression */\r
+       for (i = 0; i < lenof(compressions) + 1; i++) {\r
+           const struct ssh_compress *c =\r
+               i == 0 ? s->preferred_comp : compressions[i - 1];\r
+           if (in_commasep_string(c->name, str, len)) {\r
+               s->cscomp_tobe = c;\r
+               break;\r
+           } else if (in_commasep_string(c->delayed_name, str, len)) {\r
+               if (s->userauth_succeeded) {\r
+                   s->cscomp_tobe = c;\r
+                   break;\r
+               } else {\r
+                   s->pending_compression = TRUE;  /* try this later */\r
+               }\r
+           }\r
+       }\r
+       ssh_pkt_getstring(pktin, &str, &len);  /* server->client compression */\r
+       for (i = 0; i < lenof(compressions) + 1; i++) {\r
+           const struct ssh_compress *c =\r
+               i == 0 ? s->preferred_comp : compressions[i - 1];\r
+           if (in_commasep_string(c->name, str, len)) {\r
+               s->sccomp_tobe = c;\r
+               break;\r
+           } else if (in_commasep_string(c->delayed_name, str, len)) {\r
+               if (s->userauth_succeeded) {\r
+                   s->sccomp_tobe = c;\r
+                   break;\r
+               } else {\r
+                   s->pending_compression = TRUE;  /* try this later */\r
+               }\r
+           }\r
+       }\r
+       if (s->pending_compression) {\r
+           logevent("Server supports delayed compression; "\r
+                    "will try this later");\r
+       }\r
+       ssh_pkt_getstring(pktin, &str, &len);  /* client->server language */\r
+       ssh_pkt_getstring(pktin, &str, &len);  /* server->client language */\r
+       s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;\r
+\r
+       if (s->warn_kex) {\r
+           ssh_set_frozen(ssh, 1);\r
+           s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",\r
+                              ssh->kex->name,\r
+                              ssh_dialog_callback, ssh);\r
+           if (s->dlgret < 0) {\r
+               do {\r
+                   crReturn(0);\r
+                   if (pktin) {\r
+                       bombout(("Unexpected data from server while"\r
+                                " waiting for user response"));\r
+                       crStop(0);\r
+                   }\r
+               } while (pktin || inlen > 0);\r
+               s->dlgret = ssh->user_response;\r
+           }\r
+           ssh_set_frozen(ssh, 0);\r
+           if (s->dlgret == 0) {\r
+               ssh_disconnect(ssh, "User aborted at kex warning", NULL,\r
+                              0, TRUE);\r
+               crStop(0);\r
+           }\r
+       }\r
+\r
+       if (s->warn_cscipher) {\r
+           ssh_set_frozen(ssh, 1);\r
+           s->dlgret = askalg(ssh->frontend,\r
+                              "client-to-server cipher",\r
+                              s->cscipher_tobe->name,\r
+                              ssh_dialog_callback, ssh);\r
+           if (s->dlgret < 0) {\r
+               do {\r
+                   crReturn(0);\r
+                   if (pktin) {\r
+                       bombout(("Unexpected data from server while"\r
+                                " waiting for user response"));\r
+                       crStop(0);\r
+                   }\r
+               } while (pktin || inlen > 0);\r
+               s->dlgret = ssh->user_response;\r
+           }\r
+           ssh_set_frozen(ssh, 0);\r
+           if (s->dlgret == 0) {\r
+               ssh_disconnect(ssh, "User aborted at cipher warning", NULL,\r
+                              0, TRUE);\r
+               crStop(0);\r
+           }\r
+       }\r
+\r
+       if (s->warn_sccipher) {\r
+           ssh_set_frozen(ssh, 1);\r
+           s->dlgret = askalg(ssh->frontend,\r
+                              "server-to-client cipher",\r
+                              s->sccipher_tobe->name,\r
+                              ssh_dialog_callback, ssh);\r
+           if (s->dlgret < 0) {\r
+               do {\r
+                   crReturn(0);\r
+                   if (pktin) {\r
+                       bombout(("Unexpected data from server while"\r
+                                " waiting for user response"));\r
+                       crStop(0);\r
+                   }\r
+               } while (pktin || inlen > 0);\r
+               s->dlgret = ssh->user_response;\r
+           }\r
+           ssh_set_frozen(ssh, 0);\r
+           if (s->dlgret == 0) {\r
+               ssh_disconnect(ssh, "User aborted at cipher warning", NULL,\r
+                              0, TRUE);\r
+               crStop(0);\r
+           }\r
+       }\r
+\r
+       ssh->exhash = ssh->kex->hash->init();\r
+       hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c));\r
+       hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s));\r
+       hash_string(ssh->kex->hash, ssh->exhash,\r
+           s->our_kexinit, s->our_kexinitlen);\r
+       sfree(s->our_kexinit);\r
+       if (pktin->length > 5)\r
+           hash_string(ssh->kex->hash, ssh->exhash,\r
+               pktin->data + 5, pktin->length - 5);\r
+\r
+       if (s->ignorepkt) /* first_kex_packet_follows */\r
+           crWaitUntil(pktin);                /* Ignore packet */\r
+    }\r
+\r
+    if (ssh->kex->main_type == KEXTYPE_DH) {\r
+        /*\r
+         * Work out the number of bits of key we will need from the\r
+         * key exchange. We start with the maximum key length of\r
+         * either cipher...\r
+         */\r
+        {\r
+            int csbits, scbits;\r
+\r
+            csbits = s->cscipher_tobe->keylen;\r
+            scbits = s->sccipher_tobe->keylen;\r
+            s->nbits = (csbits > scbits ? csbits : scbits);\r
+        }\r
+        /* The keys only have hlen-bit entropy, since they're based on\r
+         * a hash. So cap the key size at hlen bits. */\r
+        if (s->nbits > ssh->kex->hash->hlen * 8)\r
+            s->nbits = ssh->kex->hash->hlen * 8;\r
+\r
+        /*\r
+         * If we're doing Diffie-Hellman group exchange, start by\r
+         * requesting a group.\r
+         */\r
+        if (!ssh->kex->pdata) {\r
+            logevent("Doing Diffie-Hellman group exchange");\r
+            ssh->pkt_kctx = SSH2_PKTCTX_DHGEX;\r
+            /*\r
+             * Work out how big a DH group we will need to allow that\r
+             * much data.\r
+             */\r
+            s->pbits = 512 << ((s->nbits - 1) / 64);\r
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);\r
+            ssh2_pkt_adduint32(s->pktout, s->pbits);\r
+            ssh2_pkt_send_noqueue(ssh, s->pktout);\r
+\r
+            crWaitUntil(pktin);\r
+            if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {\r
+                bombout(("expected key exchange group packet from server"));\r
+                crStop(0);\r
+            }\r
+            s->p = ssh2_pkt_getmp(pktin);\r
+            s->g = ssh2_pkt_getmp(pktin);\r
+            if (!s->p || !s->g) {\r
+                bombout(("unable to read mp-ints from incoming group packet"));\r
+                crStop(0);\r
+            }\r
+            ssh->kex_ctx = dh_setup_gex(s->p, s->g);\r
+            s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;\r
+            s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;\r
+        } else {\r
+            ssh->pkt_kctx = SSH2_PKTCTX_DHGROUP;\r
+            ssh->kex_ctx = dh_setup_group(ssh->kex);\r
+            s->kex_init_value = SSH2_MSG_KEXDH_INIT;\r
+            s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;\r
+            logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",\r
+                      ssh->kex->groupname);\r
+        }\r
+\r
+        logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",\r
+                  ssh->kex->hash->text_name);\r
+        /*\r
+         * Now generate and send e for Diffie-Hellman.\r
+         */\r
+        set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */\r
+        s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);\r
+        s->pktout = ssh2_pkt_init(s->kex_init_value);\r
+        ssh2_pkt_addmp(s->pktout, s->e);\r
+        ssh2_pkt_send_noqueue(ssh, s->pktout);\r
+\r
+        set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */\r
+        crWaitUntil(pktin);\r
+        if (pktin->type != s->kex_reply_value) {\r
+            bombout(("expected key exchange reply packet from server"));\r
+            crStop(0);\r
+        }\r
+        set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */\r
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);\r
+        s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);\r
+        s->f = ssh2_pkt_getmp(pktin);\r
+        if (!s->f) {\r
+            bombout(("unable to parse key exchange reply packet"));\r
+            crStop(0);\r
+        }\r
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);\r
+\r
+        s->K = dh_find_K(ssh->kex_ctx, s->f);\r
+\r
+        /* We assume everything from now on will be quick, and it might\r
+         * involve user interaction. */\r
+        set_busy_status(ssh->frontend, BUSY_NOT);\r
+\r
+        hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);\r
+        if (!ssh->kex->pdata) {\r
+            hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);\r
+            hash_mpint(ssh->kex->hash, ssh->exhash, s->p);\r
+            hash_mpint(ssh->kex->hash, ssh->exhash, s->g);\r
+        }\r
+        hash_mpint(ssh->kex->hash, ssh->exhash, s->e);\r
+        hash_mpint(ssh->kex->hash, ssh->exhash, s->f);\r
+\r
+        dh_cleanup(ssh->kex_ctx);\r
+        freebn(s->f);\r
+        if (!ssh->kex->pdata) {\r
+            freebn(s->g);\r
+            freebn(s->p);\r
+        }\r
+    } else {\r
+       logeventf(ssh, "Doing RSA key exchange with hash %s",\r
+                 ssh->kex->hash->text_name);\r
+       ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX;\r
+        /*\r
+         * RSA key exchange. First expect a KEXRSA_PUBKEY packet\r
+         * from the server.\r
+         */\r
+        crWaitUntil(pktin);\r
+        if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {\r
+            bombout(("expected RSA public key packet from server"));\r
+            crStop(0);\r
+        }\r
+\r
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);\r
+        hash_string(ssh->kex->hash, ssh->exhash,\r
+                   s->hostkeydata, s->hostkeylen);\r
+       s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);\r
+\r
+        {\r
+            char *keydata;\r
+            ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);\r
+            s->rsakeydata = snewn(s->rsakeylen, char);\r
+            memcpy(s->rsakeydata, keydata, s->rsakeylen);\r
+        }\r
+\r
+        s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);\r
+        if (!s->rsakey) {\r
+            sfree(s->rsakeydata);\r
+            bombout(("unable to parse RSA public key from server"));\r
+            crStop(0);\r
+        }\r
+\r
+        hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);\r
+\r
+        /*\r
+         * Next, set up a shared secret K, of precisely KLEN -\r
+         * 2*HLEN - 49 bits, where KLEN is the bit length of the\r
+         * RSA key modulus and HLEN is the bit length of the hash\r
+         * we're using.\r
+         */\r
+        {\r
+            int klen = ssh_rsakex_klen(s->rsakey);\r
+            int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);\r
+            int i, byte = 0;\r
+            unsigned char *kstr1, *kstr2, *outstr;\r
+            int kstr1len, kstr2len, outstrlen;\r
+\r
+            s->K = bn_power_2(nbits - 1);\r
+\r
+            for (i = 0; i < nbits; i++) {\r
+                if ((i & 7) == 0) {\r
+                    byte = random_byte();\r
+                }\r
+                bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);\r
+            }\r
+\r
+            /*\r
+             * Encode this as an mpint.\r
+             */\r
+            kstr1 = ssh2_mpint_fmt(s->K, &kstr1len);\r
+            kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char);\r
+            PUT_32BIT(kstr2, kstr1len);\r
+            memcpy(kstr2 + 4, kstr1, kstr1len);\r
+\r
+            /*\r
+             * Encrypt it with the given RSA key.\r
+             */\r
+            outstrlen = (klen + 7) / 8;\r
+            outstr = snewn(outstrlen, unsigned char);\r
+            ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len,\r
+                              outstr, outstrlen, s->rsakey);\r
+\r
+            /*\r
+             * And send it off in a return packet.\r
+             */\r
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);\r
+            ssh2_pkt_addstring_start(s->pktout);\r
+            ssh2_pkt_addstring_data(s->pktout, (char *)outstr, outstrlen);\r
+            ssh2_pkt_send_noqueue(ssh, s->pktout);\r
+\r
+           hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen);\r
+\r
+            sfree(kstr2);\r
+            sfree(kstr1);\r
+            sfree(outstr);\r
+        }\r
+\r
+        ssh_rsakex_freekey(s->rsakey);\r
+\r
+        crWaitUntil(pktin);\r
+        if (pktin->type != SSH2_MSG_KEXRSA_DONE) {\r
+            sfree(s->rsakeydata);\r
+            bombout(("expected signature packet from server"));\r
+            crStop(0);\r
+        }\r
+\r
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);\r
+\r
+        sfree(s->rsakeydata);\r
+    }\r
+\r
+    hash_mpint(ssh->kex->hash, ssh->exhash, s->K);\r
+    assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));\r
+    ssh->kex->hash->final(ssh->exhash, s->exchange_hash);\r
+\r
+    ssh->kex_ctx = NULL;\r
+\r
+#if 0\r
+    debug(("Exchange hash is:\n"));\r
+    dmemdump(s->exchange_hash, ssh->kex->hash->hlen);\r
+#endif\r
+\r
+    if (!s->hkey ||\r
+       !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,\r
+                                (char *)s->exchange_hash,\r
+                                ssh->kex->hash->hlen)) {\r
+       bombout(("Server's host key did not match the signature supplied"));\r
+       crStop(0);\r
+    }\r
+\r
+    /*\r
+     * Authenticate remote host: verify host key. (We've already\r
+     * checked the signature of the exchange hash.)\r
+     */\r
+    s->keystr = ssh->hostkey->fmtkey(s->hkey);\r
+    s->fingerprint = ssh->hostkey->fingerprint(s->hkey);\r
+    ssh_set_frozen(ssh, 1);\r
+    s->dlgret = verify_ssh_host_key(ssh->frontend,\r
+                                    ssh->savedhost, ssh->savedport,\r
+                                    ssh->hostkey->keytype, s->keystr,\r
+                                   s->fingerprint,\r
+                                    ssh_dialog_callback, ssh);\r
+    if (s->dlgret < 0) {\r
+        do {\r
+            crReturn(0);\r
+            if (pktin) {\r
+                bombout(("Unexpected data from server while waiting"\r
+                         " for user host key response"));\r
+                    crStop(0);\r
+            }\r
+        } while (pktin || inlen > 0);\r
+        s->dlgret = ssh->user_response;\r
+    }\r
+    ssh_set_frozen(ssh, 0);\r
+    if (s->dlgret == 0) {\r
+       ssh_disconnect(ssh, "User aborted at host key verification", NULL,\r
+                      0, TRUE);\r
+        crStop(0);\r
+    }\r
+    if (!s->got_session_id) {     /* don't bother logging this in rekeys */\r
+       logevent("Host key fingerprint is:");\r
+       logevent(s->fingerprint);\r
+    }\r
+    sfree(s->fingerprint);\r
+    sfree(s->keystr);\r
+    ssh->hostkey->freekey(s->hkey);\r
+\r
+    /*\r
+     * The exchange hash from the very first key exchange is also\r
+     * the session id, used in session key construction and\r
+     * authentication.\r
+     */\r
+    if (!s->got_session_id) {\r
+       assert(sizeof(s->exchange_hash) <= sizeof(ssh->v2_session_id));\r
+       memcpy(ssh->v2_session_id, s->exchange_hash,\r
+              sizeof(s->exchange_hash));\r
+       ssh->v2_session_id_len = ssh->kex->hash->hlen;\r
+       assert(ssh->v2_session_id_len <= sizeof(ssh->v2_session_id));\r
+       s->got_session_id = TRUE;\r
+    }\r
+\r
+    /*\r
+     * Send SSH2_MSG_NEWKEYS.\r
+     */\r
+    s->pktout = ssh2_pkt_init(SSH2_MSG_NEWKEYS);\r
+    ssh2_pkt_send_noqueue(ssh, s->pktout);\r
+    ssh->outgoing_data_size = 0;       /* start counting from here */\r
+\r
+    /*\r
+     * We've sent client NEWKEYS, so create and initialise\r
+     * client-to-server session keys.\r
+     */\r
+    if (ssh->cs_cipher_ctx)\r
+       ssh->cscipher->free_context(ssh->cs_cipher_ctx);\r
+    ssh->cscipher = s->cscipher_tobe;\r
+    ssh->cs_cipher_ctx = ssh->cscipher->make_context();\r
+\r
+    if (ssh->cs_mac_ctx)\r
+       ssh->csmac->free_context(ssh->cs_mac_ctx);\r
+    ssh->csmac = s->csmac_tobe;\r
+    ssh->cs_mac_ctx = ssh->csmac->make_context();\r
+\r
+    if (ssh->cs_comp_ctx)\r
+       ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);\r
+    ssh->cscomp = s->cscomp_tobe;\r
+    ssh->cs_comp_ctx = ssh->cscomp->compress_init();\r
+\r
+    /*\r
+     * Set IVs on client-to-server keys. Here we use the exchange\r
+     * hash from the _first_ key exchange.\r
+     */\r
+    {\r
+       unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];\r
+       assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace);\r
+       assert((ssh->cscipher->keylen+7) / 8 <=\r
+              ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace);\r
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace);\r
+       assert(ssh->cscipher->blksize <=\r
+              ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace);\r
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace);\r
+       assert(ssh->csmac->len <=\r
+              ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);\r
+       memset(keyspace, 0, sizeof(keyspace));\r
+    }\r
+\r
+    logeventf(ssh, "Initialised %.200s client->server encryption",\r
+             ssh->cscipher->text_name);\r
+    logeventf(ssh, "Initialised %.200s client->server MAC algorithm",\r
+             ssh->csmac->text_name);\r
+    if (ssh->cscomp->text_name)\r
+       logeventf(ssh, "Initialised %s compression",\r
+                 ssh->cscomp->text_name);\r
+\r
+    /*\r
+     * Now our end of the key exchange is complete, we can send all\r
+     * our queued higher-layer packets.\r
+     */\r
+    ssh->queueing = FALSE;\r
+    ssh2_pkt_queuesend(ssh);\r
+\r
+    /*\r
+     * Expect SSH2_MSG_NEWKEYS from server.\r
+     */\r
+    crWaitUntil(pktin);\r
+    if (pktin->type != SSH2_MSG_NEWKEYS) {\r
+       bombout(("expected new-keys packet from server"));\r
+       crStop(0);\r
+    }\r
+    ssh->incoming_data_size = 0;       /* start counting from here */\r
+\r
+    /*\r
+     * We've seen server NEWKEYS, so create and initialise\r
+     * server-to-client session keys.\r
+     */\r
+    if (ssh->sc_cipher_ctx)\r
+       ssh->sccipher->free_context(ssh->sc_cipher_ctx);\r
+    ssh->sccipher = s->sccipher_tobe;\r
+    ssh->sc_cipher_ctx = ssh->sccipher->make_context();\r
+\r
+    if (ssh->sc_mac_ctx)\r
+       ssh->scmac->free_context(ssh->sc_mac_ctx);\r
+    ssh->scmac = s->scmac_tobe;\r
+    ssh->sc_mac_ctx = ssh->scmac->make_context();\r
+\r
+    if (ssh->sc_comp_ctx)\r
+       ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx);\r
+    ssh->sccomp = s->sccomp_tobe;\r
+    ssh->sc_comp_ctx = ssh->sccomp->decompress_init();\r
+\r
+    /*\r
+     * Set IVs on server-to-client keys. Here we use the exchange\r
+     * hash from the _first_ key exchange.\r
+     */\r
+    {\r
+       unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];\r
+       assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace);\r
+       assert((ssh->sccipher->keylen+7) / 8 <=\r
+              ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace);\r
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace);\r
+       assert(ssh->sccipher->blksize <=\r
+              ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace);\r
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace);\r
+       assert(ssh->scmac->len <=\r
+              ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);\r
+       ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);\r
+       memset(keyspace, 0, sizeof(keyspace));\r
+    }\r
+    logeventf(ssh, "Initialised %.200s server->client encryption",\r
+             ssh->sccipher->text_name);\r
+    logeventf(ssh, "Initialised %.200s server->client MAC algorithm",\r
+             ssh->scmac->text_name);\r
+    if (ssh->sccomp->text_name)\r
+       logeventf(ssh, "Initialised %s decompression",\r
+                 ssh->sccomp->text_name);\r
+\r
+    /*\r
+     * Free shared secret.\r
+     */\r
+    freebn(s->K);\r
+\r
+    /*\r
+     * Key exchange is over. Loop straight back round if we have a\r
+     * deferred rekey reason.\r
+     */\r
+    if (ssh->deferred_rekey_reason) {\r
+       logevent(ssh->deferred_rekey_reason);\r
+       pktin = NULL;\r
+       ssh->deferred_rekey_reason = NULL;\r
+       goto begin_key_exchange;\r
+    }\r
+\r
+    /*\r
+     * Otherwise, schedule a timer for our next rekey.\r
+     */\r
+    ssh->kex_in_progress = FALSE;\r
+    ssh->last_rekey = GETTICKCOUNT();\r
+    if (ssh->cfg.ssh_rekey_time != 0)\r
+       ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,\r
+                                        ssh2_timer, ssh);\r
+\r
+    /*\r
+     * If this is the first key exchange phase, we must pass the\r
+     * SSH2_MSG_NEWKEYS packet to the next layer, not because it\r
+     * wants to see it but because it will need time to initialise\r
+     * itself before it sees an actual packet. In subsequent key\r
+     * exchange phases, we don't pass SSH2_MSG_NEWKEYS on, because\r
+     * it would only confuse the layer above.\r
+     */\r
+    if (s->activated_authconn) {\r
+       crReturn(0);\r
+    }\r
+    s->activated_authconn = TRUE;\r
+\r
+    /*\r
+     * Now we're encrypting. Begin returning 1 to the protocol main\r
+     * function so that other things can run on top of the\r
+     * transport. If we ever see a KEXINIT, we must go back to the\r
+     * start.\r
+     * \r
+     * We _also_ go back to the start if we see pktin==NULL and\r
+     * inlen negative, because this is a special signal meaning\r
+     * `initiate client-driven rekey', and `in' contains a message\r
+     * giving the reason for the rekey.\r
+     *\r
+     * inlen==-1 means always initiate a rekey;\r
+     * inlen==-2 means that userauth has completed successfully and\r
+     *   we should consider rekeying (for delayed compression).\r
+     */\r
+    while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) ||\r
+            (!pktin && inlen < 0))) {\r
+        wait_for_rekey:\r
+       crReturn(1);\r
+    }\r
+    if (pktin) {\r
+       logevent("Server initiated key re-exchange");\r
+    } else {\r
+       if (inlen == -2) {\r
+           /* \r
+            * authconn has seen a USERAUTH_SUCCEEDED. Time to enable\r
+            * delayed compression, if it's available.\r
+            *\r
+            * draft-miller-secsh-compression-delayed-00 says that you\r
+            * negotiate delayed compression in the first key exchange, and\r
+            * both sides start compressing when the server has sent\r
+            * USERAUTH_SUCCESS. This has a race condition -- the server\r
+            * can't know when the client has seen it, and thus which incoming\r
+            * packets it should treat as compressed.\r
+            *\r
+            * Instead, we do the initial key exchange without offering the\r
+            * delayed methods, but note if the server offers them; when we\r
+            * get here, if a delayed method was available that was higher\r
+            * on our list than what we got, we initiate a rekey in which we\r
+            * _do_ list the delayed methods (and hopefully get it as a\r
+            * result). Subsequent rekeys will do the same.\r
+            */\r
+           assert(!s->userauth_succeeded); /* should only happen once */\r
+           s->userauth_succeeded = TRUE;\r
+           if (!s->pending_compression)\r
+               /* Can't see any point rekeying. */\r
+               goto wait_for_rekey;       /* this is utterly horrid */\r
+           /* else fall through to rekey... */\r
+           s->pending_compression = FALSE;\r
+       }\r
+        /*\r
+        * Now we've decided to rekey.\r
+        *\r
+         * Special case: if the server bug is set that doesn't\r
+         * allow rekeying, we give a different log message and\r
+         * continue waiting. (If such a server _initiates_ a rekey,\r
+         * we process it anyway!)\r
+         */\r
+        if ((ssh->remote_bugs & BUG_SSH2_REKEY)) {\r
+            logeventf(ssh, "Server bug prevents key re-exchange (%s)",\r
+                      (char *)in);\r
+            /* Reset the counters, so that at least this message doesn't\r
+             * hit the event log _too_ often. */\r
+            ssh->outgoing_data_size = 0;\r
+            ssh->incoming_data_size = 0;\r
+            if (ssh->cfg.ssh_rekey_time != 0) {\r
+                ssh->next_rekey =\r
+                    schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,\r
+                                   ssh2_timer, ssh);\r
+            }\r
+            goto wait_for_rekey;       /* this is still utterly horrid */\r
+        } else {\r
+            logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in);\r
+        }\r
+    }\r
+    goto begin_key_exchange;\r
+\r
+    crFinish(1);\r
+}\r
+\r
+/*\r
+ * Add data to an SSH-2 channel output buffer.\r
+ */\r
+static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,\r
+                                 int len)\r
+{\r
+    bufchain_add(&c->v.v2.outbuffer, buf, len);\r
+}\r
+\r
+/*\r
+ * Attempt to send data on an SSH-2 channel.\r
+ */\r
+static int ssh2_try_send(struct ssh_channel *c)\r
+{\r
+    Ssh ssh = c->ssh;\r
+    struct Packet *pktout;\r
+\r
+    while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) {\r
+       int len;\r
+       void *data;\r
+       bufchain_prefix(&c->v.v2.outbuffer, &data, &len);\r
+       if ((unsigned)len > c->v.v2.remwindow)\r
+           len = c->v.v2.remwindow;\r
+       if ((unsigned)len > c->v.v2.remmaxpkt)\r
+           len = c->v.v2.remmaxpkt;\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_addstring_start(pktout);\r
+       dont_log_data(ssh, pktout, PKTLOG_OMIT);\r
+       ssh2_pkt_addstring_data(pktout, data, len);\r
+       end_log_omission(ssh, pktout);\r
+       ssh2_pkt_send(ssh, pktout);\r
+       bufchain_consume(&c->v.v2.outbuffer, len);\r
+       c->v.v2.remwindow -= len;\r
+    }\r
+\r
+    /*\r
+     * After having sent as much data as we can, return the amount\r
+     * still buffered.\r
+     */\r
+    return bufchain_size(&c->v.v2.outbuffer);\r
+}\r
+\r
+static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)\r
+{\r
+    int bufsize;\r
+    if (c->closes)\r
+       return;                        /* don't send on closing channels */\r
+    bufsize = ssh2_try_send(c);\r
+    if (bufsize == 0) {\r
+       switch (c->type) {\r
+         case CHAN_MAINSESSION:\r
+           /* stdin need not receive an unthrottle\r
+            * notification since it will be polled */\r
+           break;\r
+         case CHAN_X11:\r
+           x11_unthrottle(c->u.x11.s);\r
+           break;\r
+         case CHAN_AGENT:\r
+           /* agent sockets are request/response and need no\r
+            * buffer management */\r
+           break;\r
+         case CHAN_SOCKDATA:\r
+           pfd_unthrottle(c->u.pfd.s);\r
+           break;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * If we've emptied the channel's output buffer and there's a\r
+     * pending close event, start the channel-closing procedure.\r
+     */\r
+    if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) {\r
+       struct Packet *pktout;\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_send(ssh, pktout);\r
+       c->closes = 1;\r
+       c->pending_close = FALSE;\r
+    }\r
+}\r
+\r
+/*\r
+ * Set up most of a new ssh_channel for SSH-2.\r
+ */\r
+static void ssh2_channel_init(struct ssh_channel *c)\r
+{\r
+    Ssh ssh = c->ssh;\r
+    c->localid = alloc_channel_id(ssh);\r
+    c->closes = 0;\r
+    c->pending_close = FALSE;\r
+    c->throttling_conn = FALSE;\r
+    c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =\r
+       ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;\r
+    c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL;\r
+    c->v.v2.throttle_state = UNTHROTTLED;\r
+    bufchain_init(&c->v.v2.outbuffer);\r
+}\r
+\r
+/*\r
+ * Potentially enlarge the window on an SSH-2 channel.\r
+ */\r
+static void ssh2_set_window(struct ssh_channel *c, int newwin)\r
+{\r
+    Ssh ssh = c->ssh;\r
+\r
+    /*\r
+     * Never send WINDOW_ADJUST for a channel that the remote side\r
+     * already thinks it's closed; there's no point, since it won't\r
+     * be sending any more data anyway.\r
+     */\r
+    if (c->closes != 0)\r
+       return;\r
+\r
+    /*\r
+     * If the remote end has a habit of ignoring maxpkt, limit the\r
+     * window so that it has no choice (assuming it doesn't ignore the\r
+     * window as well).\r
+     */\r
+    if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT)\r
+       newwin = OUR_V2_MAXPKT;\r
+       \r
+\r
+    /*\r
+     * Only send a WINDOW_ADJUST if there's significantly more window\r
+     * available than the other end thinks there is.  This saves us\r
+     * sending a WINDOW_ADJUST for every character in a shell session.\r
+     *\r
+     * "Significant" is arbitrarily defined as half the window size.\r
+     */\r
+    if (newwin / 2 >= c->v.v2.locwindow) {\r
+       struct Packet *pktout;\r
+       struct winadj *wa;\r
+\r
+       /*\r
+        * In order to keep track of how much window the client\r
+        * actually has available, we'd like it to acknowledge each\r
+        * WINDOW_ADJUST.  We can't do that directly, so we accompany\r
+        * it with a CHANNEL_REQUEST that has to be acknowledged.\r
+        *\r
+        * This is only necessary if we're opening the window wide.\r
+        * If we're not, then throughput is being constrained by\r
+        * something other than the maximum window size anyway.\r
+        *\r
+        * We also only send this if the main channel has finished its\r
+        * initial CHANNEL_REQUESTs and installed the default\r
+        * CHANNEL_FAILURE handler, so as not to risk giving it\r
+        * unexpected CHANNEL_FAILUREs.\r
+        */\r
+       if (newwin == c->v.v2.locmaxwin &&\r
+           ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) {\r
+           pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+           ssh2_pkt_adduint32(pktout, c->remoteid);\r
+           ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org");\r
+           ssh2_pkt_addbool(pktout, TRUE);\r
+           ssh2_pkt_send(ssh, pktout);\r
+\r
+           /*\r
+            * CHANNEL_FAILURE doesn't come with any indication of\r
+            * what message caused it, so we have to keep track of the\r
+            * outstanding CHANNEL_REQUESTs ourselves.\r
+            */\r
+           wa = snew(struct winadj);\r
+           wa->size = newwin - c->v.v2.locwindow;\r
+           wa->next = NULL;\r
+           if (!c->v.v2.winadj_head)\r
+               c->v.v2.winadj_head = wa;\r
+           else\r
+               c->v.v2.winadj_tail->next = wa;\r
+           c->v.v2.winadj_tail = wa;\r
+           if (c->v.v2.throttle_state != UNTHROTTLED)\r
+               c->v.v2.throttle_state = UNTHROTTLING;\r
+       } else {\r
+           /* Pretend the WINDOW_ADJUST was acked immediately. */\r
+           c->v.v2.remlocwin = newwin;\r
+           c->v.v2.throttle_state = THROTTLED;\r
+       }\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow);\r
+       ssh2_pkt_send(ssh, pktout);\r
+       c->v.v2.locwindow = newwin;\r
+    }\r
+}\r
+\r
+/*\r
+ * Find the channel associated with a message.  If there's no channel,\r
+ * or it's not properly open, make a noise about it and return NULL.\r
+ */\r
+static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin)\r
+{\r
+    unsigned localid = ssh_pkt_getuint32(pktin);\r
+    struct ssh_channel *c;\r
+\r
+    c = find234(ssh->channels, &localid, ssh_channelfind);\r
+    if (!c ||\r
+       (c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&\r
+        pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) {\r
+       char *buf = dupprintf("Received %s for %s channel %u",\r
+                             ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,\r
+                                           pktin->type),\r
+                             c ? "half-open" : "nonexistent", localid);\r
+       ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);\r
+       sfree(buf);\r
+       return NULL;\r
+    }\r
+    return c;\r
+}\r
+\r
+static int ssh2_handle_winadj_response(struct ssh_channel *c)\r
+{\r
+    struct winadj *wa = c->v.v2.winadj_head;\r
+    if (!wa)\r
+       return FALSE;\r
+    c->v.v2.winadj_head = wa->next;\r
+    c->v.v2.remlocwin += wa->size;\r
+    sfree(wa);\r
+    /*\r
+     * winadj messages are only sent when the window is fully open, so\r
+     * if we get an ack of one, we know any pending unthrottle is\r
+     * complete.\r
+     */\r
+    if (c->v.v2.throttle_state == UNTHROTTLING)\r
+       c->v.v2.throttle_state = UNTHROTTLED;\r
+    return TRUE;\r
+}\r
+\r
+static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /*\r
+     * This should never get called.  All channel requests are either\r
+     * sent with want_reply false, are sent before this handler gets\r
+     * installed, or are "winadj@putty" requests, which servers should\r
+     * never respond to with success.\r
+     *\r
+     * However, at least one server ("boks_sshd") is known to return\r
+     * SUCCESS for channel requests it's never heard of, such as\r
+     * "winadj@putty". Raised with foxt.com as bug 090916-090424, but\r
+     * for the sake of a quiet life, we handle it just the same as the\r
+     * expected FAILURE.\r
+     */\r
+    struct ssh_channel *c;\r
+\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    if (!ssh2_handle_winadj_response(c))\r
+       ssh_disconnect(ssh, NULL,\r
+                      "Received unsolicited SSH_MSG_CHANNEL_SUCCESS",\r
+                      SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);\r
+}\r
+\r
+static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /*\r
+     * The only time this should get called is for "winadj@putty"\r
+     * messages sent above.  All other channel requests are either\r
+     * sent with want_reply false or are sent before this handler gets\r
+     * installed.\r
+     */\r
+    struct ssh_channel *c;\r
+\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    if (!ssh2_handle_winadj_response(c))\r
+       ssh_disconnect(ssh, NULL,\r
+                      "Received unsolicited SSH_MSG_CHANNEL_FAILURE",\r
+                      SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);\r
+}\r
+\r
+static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)\r
+{\r
+    struct ssh_channel *c;\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    if (!c->closes) {\r
+       c->v.v2.remwindow += ssh_pkt_getuint32(pktin);\r
+       ssh2_try_send_and_unthrottle(ssh, c);\r
+    }\r
+}\r
+\r
+static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)\r
+{\r
+    char *data;\r
+    int length;\r
+    struct ssh_channel *c;\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&\r
+       ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR)\r
+       return;                        /* extended but not stderr */\r
+    ssh_pkt_getstring(pktin, &data, &length);\r
+    if (data) {\r
+       int bufsize = 0;\r
+       c->v.v2.locwindow -= length;\r
+       c->v.v2.remlocwin -= length;\r
+       switch (c->type) {\r
+         case CHAN_MAINSESSION:\r
+           bufsize =\r
+               from_backend(ssh->frontend, pktin->type ==\r
+                            SSH2_MSG_CHANNEL_EXTENDED_DATA,\r
+                            data, length);\r
+           break;\r
+         case CHAN_X11:\r
+           bufsize = x11_send(c->u.x11.s, data, length);\r
+           break;\r
+         case CHAN_SOCKDATA:\r
+           bufsize = pfd_send(c->u.pfd.s, data, length);\r
+           break;\r
+         case CHAN_AGENT:\r
+           while (length > 0) {\r
+               if (c->u.a.lensofar < 4) {\r
+                   unsigned int l = min(4 - c->u.a.lensofar,\r
+                                        (unsigned)length);\r
+                   memcpy(c->u.a.msglen + c->u.a.lensofar,\r
+                          data, l);\r
+                   data += l;\r
+                   length -= l;\r
+                   c->u.a.lensofar += l;\r
+               }\r
+               if (c->u.a.lensofar == 4) {\r
+                   c->u.a.totallen =\r
+                       4 + GET_32BIT(c->u.a.msglen);\r
+                   c->u.a.message = snewn(c->u.a.totallen,\r
+                                          unsigned char);\r
+                   memcpy(c->u.a.message, c->u.a.msglen, 4);\r
+               }\r
+               if (c->u.a.lensofar >= 4 && length > 0) {\r
+                   unsigned int l =\r
+                       min(c->u.a.totallen - c->u.a.lensofar,\r
+                           (unsigned)length);\r
+                   memcpy(c->u.a.message + c->u.a.lensofar,\r
+                          data, l);\r
+                   data += l;\r
+                   length -= l;\r
+                   c->u.a.lensofar += l;\r
+               }\r
+               if (c->u.a.lensofar == c->u.a.totallen) {\r
+                   void *reply;\r
+                   int replylen;\r
+                   if (agent_query(c->u.a.message,\r
+                                   c->u.a.totallen,\r
+                                   &reply, &replylen,\r
+                                   ssh_agentf_callback, c))\r
+                       ssh_agentf_callback(c, reply, replylen);\r
+                   sfree(c->u.a.message);\r
+                   c->u.a.lensofar = 0;\r
+               }\r
+           }\r
+           bufsize = 0;\r
+           break;\r
+       }\r
+       /*\r
+        * If it looks like the remote end hit the end of its window,\r
+        * and we didn't want it to do that, think about using a\r
+        * larger window.\r
+        */\r
+       if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED &&\r
+           c->v.v2.locmaxwin < 0x40000000)\r
+           c->v.v2.locmaxwin += OUR_V2_WINSIZE;\r
+       /*\r
+        * If we are not buffering too much data,\r
+        * enlarge the window again at the remote side.\r
+        * If we are buffering too much, we may still\r
+        * need to adjust the window if the server's\r
+        * sent excess data.\r
+        */\r
+       ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ?\r
+                       c->v.v2.locmaxwin - bufsize : 0);\r
+       /*\r
+        * If we're either buffering way too much data, or if we're\r
+        * buffering anything at all and we're in "simple" mode,\r
+        * throttle the whole channel.\r
+        */\r
+       if ((bufsize > c->v.v2.locmaxwin ||\r
+            (ssh->cfg.ssh_simple && bufsize > 0)) &&\r
+           !c->throttling_conn) {\r
+           c->throttling_conn = 1;\r
+           ssh_throttle_conn(ssh, +1);\r
+       }\r
+    }\r
+}\r
+\r
+static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)\r
+{\r
+    struct ssh_channel *c;\r
+\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+\r
+    if (c->type == CHAN_X11) {\r
+       /*\r
+        * Remote EOF on an X11 channel means we should\r
+        * wrap up and close the channel ourselves.\r
+        */\r
+       x11_close(c->u.x11.s);\r
+       c->u.x11.s = NULL;\r
+       sshfwd_close(c);\r
+    } else if (c->type == CHAN_AGENT) {\r
+       sshfwd_close(c);\r
+    } else if (c->type == CHAN_SOCKDATA) {\r
+       pfd_close(c->u.pfd.s);\r
+       c->u.pfd.s = NULL;\r
+       sshfwd_close(c);\r
+    }\r
+}\r
+\r
+static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)\r
+{\r
+    struct ssh_channel *c;\r
+    struct Packet *pktout;\r
+\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    /* Do pre-close processing on the channel. */\r
+    switch (c->type) {\r
+      case CHAN_MAINSESSION:\r
+       ssh->mainchan = NULL;\r
+       update_specials_menu(ssh->frontend);\r
+       break;\r
+      case CHAN_X11:\r
+       if (c->u.x11.s != NULL)\r
+           x11_close(c->u.x11.s);\r
+       sshfwd_close(c);\r
+       break;\r
+      case CHAN_AGENT:\r
+       sshfwd_close(c);\r
+       break;\r
+      case CHAN_SOCKDATA:\r
+       if (c->u.pfd.s != NULL)\r
+           pfd_close(c->u.pfd.s);\r
+       sshfwd_close(c);\r
+       break;\r
+    }\r
+    if (c->closes == 0) {\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_send(ssh, pktout);\r
+    }\r
+    del234(ssh->channels, c);\r
+    bufchain_clear(&c->v.v2.outbuffer);\r
+    sfree(c);\r
+\r
+    /*\r
+     * See if that was the last channel left open.\r
+     * (This is only our termination condition if we're\r
+     * not running in -N mode.)\r
+     */\r
+    if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {\r
+       /*\r
+        * We used to send SSH_MSG_DISCONNECT here,\r
+        * because I'd believed that _every_ conforming\r
+        * SSH-2 connection had to end with a disconnect\r
+        * being sent by at least one side; apparently\r
+        * I was wrong and it's perfectly OK to\r
+        * unceremoniously slam the connection shut\r
+        * when you're done, and indeed OpenSSH feels\r
+        * this is more polite than sending a\r
+        * DISCONNECT. So now we don't.\r
+        */\r
+       ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);\r
+    }\r
+}\r
+\r
+static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)\r
+{\r
+    struct ssh_channel *c;\r
+    struct Packet *pktout;\r
+\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    if (c->type != CHAN_SOCKDATA_DORMANT)\r
+       return;                        /* dunno why they're confirming this */\r
+    c->remoteid = ssh_pkt_getuint32(pktin);\r
+    c->halfopen = FALSE;\r
+    c->type = CHAN_SOCKDATA;\r
+    c->v.v2.remwindow = ssh_pkt_getuint32(pktin);\r
+    c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);\r
+    if (c->u.pfd.s)\r
+       pfd_confirm(c->u.pfd.s);\r
+    if (c->closes) {\r
+       /*\r
+        * We have a pending close on this channel,\r
+        * which we decided on before the server acked\r
+        * the channel open. So now we know the\r
+        * remoteid, we can close it again.\r
+        */\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_send(ssh, pktout);\r
+    }\r
+}\r
+\r
+static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)\r
+{\r
+    static const char *const reasons[] = {\r
+       "<unknown reason code>",\r
+           "Administratively prohibited",\r
+           "Connect failed",\r
+           "Unknown channel type",\r
+           "Resource shortage",\r
+    };\r
+    unsigned reason_code;\r
+    char *reason_string;\r
+    int reason_length;\r
+    struct ssh_channel *c;\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    if (c->type != CHAN_SOCKDATA_DORMANT)\r
+       return;                        /* dunno why they're failing this */\r
+\r
+    reason_code = ssh_pkt_getuint32(pktin);\r
+    if (reason_code >= lenof(reasons))\r
+       reason_code = 0; /* ensure reasons[reason_code] in range */\r
+    ssh_pkt_getstring(pktin, &reason_string, &reason_length);\r
+    logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",\r
+             reasons[reason_code], reason_length, reason_string);\r
+\r
+    pfd_close(c->u.pfd.s);\r
+\r
+    del234(ssh->channels, c);\r
+    sfree(c);\r
+}\r
+\r
+static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)\r
+{\r
+    char *type;\r
+    int typelen, want_reply;\r
+    int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */\r
+    struct ssh_channel *c;\r
+    struct Packet *pktout;\r
+\r
+    c = ssh2_channel_msg(ssh, pktin);\r
+    if (!c)\r
+       return;\r
+    ssh_pkt_getstring(pktin, &type, &typelen);\r
+    want_reply = ssh2_pkt_getbool(pktin);\r
+\r
+    /*\r
+     * Having got the channel number, we now look at\r
+     * the request type string to see if it's something\r
+     * we recognise.\r
+     */\r
+    if (c == ssh->mainchan) {\r
+       /*\r
+        * We recognise "exit-status" and "exit-signal" on\r
+        * the primary channel.\r
+        */\r
+       if (typelen == 11 &&\r
+           !memcmp(type, "exit-status", 11)) {\r
+\r
+           ssh->exitcode = ssh_pkt_getuint32(pktin);\r
+           logeventf(ssh, "Server sent command exit status %d",\r
+                     ssh->exitcode);\r
+           reply = SSH2_MSG_CHANNEL_SUCCESS;\r
+\r
+       } else if (typelen == 11 &&\r
+                  !memcmp(type, "exit-signal", 11)) {\r
+\r
+           int is_plausible = TRUE, is_int = FALSE;\r
+           char *fmt_sig = "", *fmt_msg = "";\r
+           char *msg;\r
+           int msglen = 0, core = FALSE;\r
+           /* ICK: older versions of OpenSSH (e.g. 3.4p1)\r
+            * provide an `int' for the signal, despite its\r
+            * having been a `string' in the drafts of RFC 4254 since at\r
+            * least 2001. (Fixed in session.c 1.147.) Try to\r
+            * infer which we can safely parse it as. */\r
+           {\r
+               unsigned char *p = pktin->body +\r
+                   pktin->savedpos;\r
+               long len = pktin->length - pktin->savedpos;\r
+               unsigned long num = GET_32BIT(p); /* what is it? */\r
+               /* If it's 0, it hardly matters; assume string */\r
+               if (num == 0) {\r
+                   is_int = FALSE;\r
+               } else {\r
+                   int maybe_int = FALSE, maybe_str = FALSE;\r
+#define CHECK_HYPOTHESIS(offset, result) \\r
+    do { \\r
+       long q = offset; \\r
+       if (q >= 0 && q+4 <= len) { \\r
+           q = q + 4 + GET_32BIT(p+q); \\r
+           if (q >= 0 && q+4 <= len && \\r
+                   ((q = q + 4 + GET_32BIT(p+q))!= 0) && q == len) \\r
+               result = TRUE; \\r
+       } \\r
+    } while(0)\r
+                   CHECK_HYPOTHESIS(4+1, maybe_int);\r
+                   CHECK_HYPOTHESIS(4+num+1, maybe_str);\r
+#undef CHECK_HYPOTHESIS\r
+                   if (maybe_int && !maybe_str)\r
+                       is_int = TRUE;\r
+                   else if (!maybe_int && maybe_str)\r
+                       is_int = FALSE;\r
+                   else\r
+                       /* Crikey. Either or neither. Panic. */\r
+                       is_plausible = FALSE;\r
+               }\r
+           }\r
+           ssh->exitcode = 128;       /* means `unknown signal' */\r
+           if (is_plausible) {\r
+               if (is_int) {\r
+                   /* Old non-standard OpenSSH. */\r
+                   int signum = ssh_pkt_getuint32(pktin);\r
+                   fmt_sig = dupprintf(" %d", signum);\r
+                   ssh->exitcode = 128 + signum;\r
+               } else {\r
+                   /* As per RFC 4254. */\r
+                   char *sig;\r
+                   int siglen;\r
+                   ssh_pkt_getstring(pktin, &sig, &siglen);\r
+                   /* Signal name isn't supposed to be blank, but\r
+                    * let's cope gracefully if it is. */\r
+                   if (siglen) {\r
+                       fmt_sig = dupprintf(" \"%.*s\"",\r
+                                           siglen, sig);\r
+                   }\r
+\r
+                   /*\r
+                    * Really hideous method of translating the\r
+                    * signal description back into a locally\r
+                    * meaningful number.\r
+                    */\r
+\r
+                   if (0)\r
+                       ;\r
+#define TRANSLATE_SIGNAL(s) \\r
+    else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \\r
+        ssh->exitcode = 128 + SIG ## s\r
+#ifdef SIGABRT\r
+                   TRANSLATE_SIGNAL(ABRT);\r
+#endif\r
+#ifdef SIGALRM\r
+                   TRANSLATE_SIGNAL(ALRM);\r
+#endif\r
+#ifdef SIGFPE\r
+                   TRANSLATE_SIGNAL(FPE);\r
+#endif\r
+#ifdef SIGHUP\r
+                   TRANSLATE_SIGNAL(HUP);\r
+#endif\r
+#ifdef SIGILL\r
+                   TRANSLATE_SIGNAL(ILL);\r
+#endif\r
+#ifdef SIGINT\r
+                   TRANSLATE_SIGNAL(INT);\r
+#endif\r
+#ifdef SIGKILL\r
+                   TRANSLATE_SIGNAL(KILL);\r
+#endif\r
+#ifdef SIGPIPE\r
+                   TRANSLATE_SIGNAL(PIPE);\r
+#endif\r
+#ifdef SIGQUIT\r
+                   TRANSLATE_SIGNAL(QUIT);\r
+#endif\r
+#ifdef SIGSEGV\r
+                   TRANSLATE_SIGNAL(SEGV);\r
+#endif\r
+#ifdef SIGTERM\r
+                   TRANSLATE_SIGNAL(TERM);\r
+#endif\r
+#ifdef SIGUSR1\r
+                   TRANSLATE_SIGNAL(USR1);\r
+#endif\r
+#ifdef SIGUSR2\r
+                   TRANSLATE_SIGNAL(USR2);\r
+#endif\r
+#undef TRANSLATE_SIGNAL\r
+                   else\r
+                       ssh->exitcode = 128;\r
+               }\r
+               core = ssh2_pkt_getbool(pktin);\r
+               ssh_pkt_getstring(pktin, &msg, &msglen);\r
+               if (msglen) {\r
+                   fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg);\r
+               }\r
+               /* ignore lang tag */\r
+           } /* else don't attempt to parse */\r
+           logeventf(ssh, "Server exited on signal%s%s%s",\r
+                     fmt_sig, core ? " (core dumped)" : "",\r
+                     fmt_msg);\r
+           if (*fmt_sig) sfree(fmt_sig);\r
+           if (*fmt_msg) sfree(fmt_msg);\r
+           reply = SSH2_MSG_CHANNEL_SUCCESS;\r
+\r
+       }\r
+    } else {\r
+       /*\r
+        * This is a channel request we don't know\r
+        * about, so we now either ignore the request\r
+        * or respond with CHANNEL_FAILURE, depending\r
+        * on want_reply.\r
+        */\r
+       reply = SSH2_MSG_CHANNEL_FAILURE;\r
+    }\r
+    if (want_reply) {\r
+       pktout = ssh2_pkt_init(reply);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_send(ssh, pktout);\r
+    }\r
+}\r
+\r
+static void ssh2_msg_global_request(Ssh ssh, struct Packet *pktin)\r
+{\r
+    char *type;\r
+    int typelen, want_reply;\r
+    struct Packet *pktout;\r
+\r
+    ssh_pkt_getstring(pktin, &type, &typelen);\r
+    want_reply = ssh2_pkt_getbool(pktin);\r
+\r
+    /*\r
+     * We currently don't support any global requests\r
+     * at all, so we either ignore the request or\r
+     * respond with REQUEST_FAILURE, depending on\r
+     * want_reply.\r
+     */\r
+    if (want_reply) {\r
+       pktout = ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE);\r
+       ssh2_pkt_send(ssh, pktout);\r
+    }\r
+}\r
+\r
+static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)\r
+{\r
+    char *type;\r
+    int typelen;\r
+    char *peeraddr;\r
+    int peeraddrlen;\r
+    int peerport;\r
+    char *error = NULL;\r
+    struct ssh_channel *c;\r
+    unsigned remid, winsize, pktsize;\r
+    struct Packet *pktout;\r
+\r
+    ssh_pkt_getstring(pktin, &type, &typelen);\r
+    c = snew(struct ssh_channel);\r
+    c->ssh = ssh;\r
+\r
+    remid = ssh_pkt_getuint32(pktin);\r
+    winsize = ssh_pkt_getuint32(pktin);\r
+    pktsize = ssh_pkt_getuint32(pktin);\r
+\r
+    if (typelen == 3 && !memcmp(type, "x11", 3)) {\r
+       char *addrstr;\r
+       const char *x11err;\r
+\r
+       ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);\r
+       addrstr = snewn(peeraddrlen+1, char);\r
+       memcpy(addrstr, peeraddr, peeraddrlen);\r
+       addrstr[peeraddrlen] = '\0';\r
+       peerport = ssh_pkt_getuint32(pktin);\r
+\r
+       logeventf(ssh, "Received X11 connect request from %s:%d",\r
+                 addrstr, peerport);\r
+\r
+       if (!ssh->X11_fwd_enabled)\r
+           error = "X11 forwarding is not enabled";\r
+       else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c,\r
+                                   addrstr, peerport, &ssh->cfg)) != NULL) {\r
+           logeventf(ssh, "Local X11 connection failed: %s", x11err);\r
+           error = "Unable to open an X11 connection";\r
+       } else {\r
+           logevent("Opening X11 forward connection succeeded");\r
+           c->type = CHAN_X11;\r
+       }\r
+\r
+       sfree(addrstr);\r
+    } else if (typelen == 15 &&\r
+              !memcmp(type, "forwarded-tcpip", 15)) {\r
+       struct ssh_rportfwd pf, *realpf;\r
+       char *dummy;\r
+       int dummylen;\r
+       ssh_pkt_getstring(pktin, &dummy, &dummylen);/* skip address */\r
+       pf.sport = ssh_pkt_getuint32(pktin);\r
+       ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);\r
+       peerport = ssh_pkt_getuint32(pktin);\r
+       realpf = find234(ssh->rportfwds, &pf, NULL);\r
+       logeventf(ssh, "Received remote port %d open request "\r
+                 "from %s:%d", pf.sport, peeraddr, peerport);\r
+       if (realpf == NULL) {\r
+           error = "Remote port is not recognised";\r
+       } else {\r
+           const char *e = pfd_newconnect(&c->u.pfd.s,\r
+                                          realpf->dhost,\r
+                                          realpf->dport, c,\r
+                                          &ssh->cfg,\r
+                                          realpf->pfrec->addressfamily);\r
+           logeventf(ssh, "Attempting to forward remote port to "\r
+                     "%s:%d", realpf->dhost, realpf->dport);\r
+           if (e != NULL) {\r
+               logeventf(ssh, "Port open failed: %s", e);\r
+               error = "Port open failed";\r
+           } else {\r
+               logevent("Forwarded port opened successfully");\r
+               c->type = CHAN_SOCKDATA;\r
+           }\r
+       }\r
+    } else if (typelen == 22 &&\r
+              !memcmp(type, "auth-agent@openssh.com", 22)) {\r
+       if (!ssh->agentfwd_enabled)\r
+           error = "Agent forwarding is not enabled";\r
+       else {\r
+           c->type = CHAN_AGENT;       /* identify channel type */\r
+           c->u.a.lensofar = 0;\r
+       }\r
+    } else {\r
+       error = "Unsupported channel type requested";\r
+    }\r
+\r
+    c->remoteid = remid;\r
+    c->halfopen = FALSE;\r
+    if (error) {\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_adduint32(pktout, SSH2_OPEN_CONNECT_FAILED);\r
+       ssh2_pkt_addstring(pktout, error);\r
+       ssh2_pkt_addstring(pktout, "en");       /* language tag */\r
+       ssh2_pkt_send(ssh, pktout);\r
+       logeventf(ssh, "Rejected channel open: %s", error);\r
+       sfree(c);\r
+    } else {\r
+       ssh2_channel_init(c);\r
+       c->v.v2.remwindow = winsize;\r
+       c->v.v2.remmaxpkt = pktsize;\r
+       add234(ssh->channels, c);\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);\r
+       ssh2_pkt_adduint32(pktout, c->remoteid);\r
+       ssh2_pkt_adduint32(pktout, c->localid);\r
+       ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);\r
+       ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */\r
+       ssh2_pkt_send(ssh, pktout);\r
+    }\r
+}\r
+\r
+/*\r
+ * Buffer banner messages for later display at some convenient point,\r
+ * if we're going to display them.\r
+ */\r
+static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* Arbitrary limit to prevent unbounded inflation of buffer */\r
+    if (ssh->cfg.ssh_show_banner &&\r
+       bufchain_size(&ssh->banner) <= 131072) {\r
+       char *banner = NULL;\r
+       int size = 0;\r
+       ssh_pkt_getstring(pktin, &banner, &size);\r
+       if (banner)\r
+           bufchain_add(&ssh->banner, banner, size);\r
+    }\r
+}\r
+\r
+/* Helper function to deal with sending tty modes for "pty-req" */\r
+static void ssh2_send_ttymode(void *data, char *mode, char *val)\r
+{\r
+    struct Packet *pktout = (struct Packet *)data;\r
+    int i = 0;\r
+    unsigned int arg = 0;\r
+    while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++;\r
+    if (i == lenof(ssh_ttymodes)) return;\r
+    switch (ssh_ttymodes[i].type) {\r
+      case TTY_OP_CHAR:\r
+       arg = ssh_tty_parse_specchar(val);\r
+       break;\r
+      case TTY_OP_BOOL:\r
+       arg = ssh_tty_parse_boolean(val);\r
+       break;\r
+    }\r
+    ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);\r
+    ssh2_pkt_adduint32(pktout, arg);\r
+}\r
+\r
+/*\r
+ * Handle the SSH-2 userauth and connection layers.\r
+ */\r
+static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,\r
+                            struct Packet *pktin)\r
+{\r
+    struct do_ssh2_authconn_state {\r
+       enum {\r
+           AUTH_TYPE_NONE,\r
+               AUTH_TYPE_PUBLICKEY,\r
+               AUTH_TYPE_PUBLICKEY_OFFER_LOUD,\r
+               AUTH_TYPE_PUBLICKEY_OFFER_QUIET,\r
+               AUTH_TYPE_PASSWORD,\r
+               AUTH_TYPE_GSSAPI,\r
+               AUTH_TYPE_KEYBOARD_INTERACTIVE,\r
+               AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET\r
+       } type;\r
+       int done_service_req;\r
+       int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;\r
+       int tried_pubkey_config, done_agent;\r
+#ifndef NO_GSSAPI\r
+       int can_gssapi;\r
+       int tried_gssapi;\r
+#endif\r
+       int kbd_inter_refused;\r
+       int we_are_in, userauth_success;\r
+       prompts_t *cur_prompt;\r
+       int num_prompts;\r
+       char username[100];\r
+       char *password;\r
+       int got_username;\r
+       void *publickey_blob;\r
+       int publickey_bloblen;\r
+       int publickey_encrypted;\r
+       char *publickey_algorithm;\r
+       char *publickey_comment;\r
+       unsigned char agent_request[5], *agent_response, *agentp;\r
+       int agent_responselen;\r
+       unsigned char *pkblob_in_agent;\r
+       int keyi, nkeys;\r
+       char *pkblob, *alg, *commentp;\r
+       int pklen, alglen, commentlen;\r
+       int siglen, retlen, len;\r
+       char *q, *agentreq, *ret;\r
+       int try_send;\r
+       int num_env, env_left, env_ok;\r
+       struct Packet *pktout;\r
+#ifndef NO_GSSAPI\r
+       struct ssh_gss_library *gsslib;\r
+       Ssh_gss_ctx gss_ctx;\r
+       Ssh_gss_buf gss_buf;\r
+       Ssh_gss_buf gss_rcvtok, gss_sndtok;\r
+       Ssh_gss_name gss_srv_name;\r
+       Ssh_gss_stat gss_stat;\r
+#endif\r
+    };\r
+    crState(do_ssh2_authconn_state);\r
+\r
+    crBegin(ssh->do_ssh2_authconn_crstate);\r
+\r
+    s->done_service_req = FALSE;\r
+    s->we_are_in = s->userauth_success = FALSE;\r
+#ifndef NO_GSSAPI\r
+    s->tried_gssapi = FALSE;\r
+#endif\r
+\r
+    if (!ssh->cfg.ssh_no_userauth) {\r
+       /*\r
+        * Request userauth protocol, and await a response to it.\r
+        */\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);\r
+       ssh2_pkt_addstring(s->pktout, "ssh-userauth");\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+       crWaitUntilV(pktin);\r
+       if (pktin->type == SSH2_MSG_SERVICE_ACCEPT)\r
+           s->done_service_req = TRUE;\r
+    }\r
+    if (!s->done_service_req) {\r
+       /*\r
+        * Request connection protocol directly, without authentication.\r
+        */\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);\r
+       ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+       crWaitUntilV(pktin);\r
+       if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) {\r
+           s->we_are_in = TRUE; /* no auth required */\r
+       } else {\r
+           bombout(("Server refused service request"));\r
+           crStopV;\r
+       }\r
+    }\r
+\r
+    /* Arrange to be able to deal with any BANNERs that come in.\r
+     * (We do this now as packets may come in during the next bit.) */\r
+    bufchain_init(&ssh->banner);\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =\r
+       ssh2_msg_userauth_banner;\r
+\r
+    /*\r
+     * Misc one-time setup for authentication.\r
+     */\r
+    s->publickey_blob = NULL;\r
+    if (!s->we_are_in) {\r
+\r
+       /*\r
+        * Load the public half of any configured public key file\r
+        * for later use.\r
+        */\r
+       if (!filename_is_null(ssh->cfg.keyfile)) {\r
+           int keytype;\r
+           logeventf(ssh, "Reading private key file \"%.150s\"",\r
+                     filename_to_str(&ssh->cfg.keyfile));\r
+           keytype = key_type(&ssh->cfg.keyfile);\r
+           if (keytype == SSH_KEYTYPE_SSH2) {\r
+               const char *error;\r
+               s->publickey_blob =\r
+                   ssh2_userkey_loadpub(&ssh->cfg.keyfile,\r
+                                        &s->publickey_algorithm,\r
+                                        &s->publickey_bloblen, \r
+                                        &s->publickey_comment, &error);\r
+               if (s->publickey_blob) {\r
+                   s->publickey_encrypted =\r
+                       ssh2_userkey_encrypted(&ssh->cfg.keyfile, NULL);\r
+               } else {\r
+                   char *msgbuf;\r
+                   logeventf(ssh, "Unable to load private key (%s)", \r
+                             error);\r
+                   msgbuf = dupprintf("Unable to load private key file "\r
+                                      "\"%.150s\" (%s)\r\n",\r
+                                      filename_to_str(&ssh->cfg.keyfile),\r
+                                      error);\r
+                   c_write_str(ssh, msgbuf);\r
+                   sfree(msgbuf);\r
+               }\r
+           } else {\r
+               char *msgbuf;\r
+               logeventf(ssh, "Unable to use this key file (%s)",\r
+                         key_type_to_str(keytype));\r
+               msgbuf = dupprintf("Unable to use key file \"%.150s\""\r
+                                  " (%s)\r\n",\r
+                                  filename_to_str(&ssh->cfg.keyfile),\r
+                                  key_type_to_str(keytype));\r
+               c_write_str(ssh, msgbuf);\r
+               sfree(msgbuf);\r
+               s->publickey_blob = NULL;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Find out about any keys Pageant has (but if there's a\r
+        * public key configured, filter out all others).\r
+        */\r
+       s->nkeys = 0;\r
+       s->agent_response = NULL;\r
+       s->pkblob_in_agent = NULL;\r
+       if (ssh->cfg.tryagent && agent_exists()) {\r
+\r
+           void *r;\r
+\r
+           logevent("Pageant is running. Requesting keys.");\r
+\r
+           /* Request the keys held by the agent. */\r
+           PUT_32BIT(s->agent_request, 1);\r
+           s->agent_request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;\r
+           if (!agent_query(s->agent_request, 5, &r, &s->agent_responselen,\r
+                            ssh_agent_callback, ssh)) {\r
+               do {\r
+                   crReturnV;\r
+                   if (pktin) {\r
+                       bombout(("Unexpected data from server while"\r
+                                " waiting for agent response"));\r
+                       crStopV;\r
+                   }\r
+               } while (pktin || inlen > 0);\r
+               r = ssh->agent_response;\r
+               s->agent_responselen = ssh->agent_response_len;\r
+           }\r
+           s->agent_response = (unsigned char *) r;\r
+           if (s->agent_response && s->agent_responselen >= 5 &&\r
+               s->agent_response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {\r
+               int keyi;\r
+               unsigned char *p;\r
+               p = s->agent_response + 5;\r
+               s->nkeys = GET_32BIT(p);\r
+               p += 4;\r
+               logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);\r
+               if (s->publickey_blob) {\r
+                   /* See if configured key is in agent. */\r
+                   for (keyi = 0; keyi < s->nkeys; keyi++) {\r
+                       s->pklen = GET_32BIT(p);\r
+                       if (s->pklen == s->publickey_bloblen &&\r
+                           !memcmp(p+4, s->publickey_blob,\r
+                                   s->publickey_bloblen)) {\r
+                           logeventf(ssh, "Pageant key #%d matches "\r
+                                     "configured key file", keyi);\r
+                           s->keyi = keyi;\r
+                           s->pkblob_in_agent = p;\r
+                           break;\r
+                       }\r
+                       p += 4 + s->pklen;\r
+                       p += GET_32BIT(p) + 4; /* comment */\r
+                   }\r
+                   if (!s->pkblob_in_agent) {\r
+                       logevent("Configured key file not in Pageant");\r
+                       s->nkeys = 0;\r
+                   }\r
+               }\r
+           } else {\r
+                logevent("Failed to get reply from Pageant");\r
+           }\r
+       }\r
+\r
+    }\r
+\r
+    /*\r
+     * We repeat this whole loop, including the username prompt,\r
+     * until we manage a successful authentication. If the user\r
+     * types the wrong _password_, they can be sent back to the\r
+     * beginning to try another username, if this is configured on.\r
+     * (If they specify a username in the config, they are never\r
+     * asked, even if they do give a wrong password.)\r
+     * \r
+     * I think this best serves the needs of\r
+     * \r
+     *  - the people who have no configuration, no keys, and just\r
+     *    want to try repeated (username,password) pairs until they\r
+     *    type both correctly\r
+     * \r
+     *  - people who have keys and configuration but occasionally\r
+     *    need to fall back to passwords\r
+     * \r
+     *  - people with a key held in Pageant, who might not have\r
+     *    logged in to a particular machine before; so they want to\r
+     *    type a username, and then _either_ their key will be\r
+     *    accepted, _or_ they will type a password. If they mistype\r
+     *    the username they will want to be able to get back and\r
+     *    retype it!\r
+     */\r
+    s->username[0] = '\0';\r
+    s->got_username = FALSE;\r
+    while (!s->we_are_in) {\r
+       /*\r
+        * Get a username.\r
+        */\r
+       if (s->got_username && !ssh->cfg.change_username) {\r
+           /*\r
+            * We got a username last time round this loop, and\r
+            * with change_username turned off we don't try to get\r
+            * it again.\r
+            */\r
+       } else if (!get_remote_username(&ssh->cfg, s->username,\r
+                                       sizeof(s->username))) {\r
+           int ret; /* need not be kept over crReturn */\r
+           s->cur_prompt = new_prompts(ssh->frontend);\r
+           s->cur_prompt->to_server = TRUE;\r
+           s->cur_prompt->name = dupstr("SSH login name");\r
+           add_prompt(s->cur_prompt, dupstr("login as: "), TRUE,\r
+                      lenof(s->username)); \r
+           ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+           while (ret < 0) {\r
+               ssh->send_ok = 1;\r
+               crWaitUntilV(!pktin);\r
+               ret = get_userpass_input(s->cur_prompt, in, inlen);\r
+               ssh->send_ok = 0;\r
+           }\r
+           if (!ret) {\r
+               /*\r
+                * get_userpass_input() failed to get a username.\r
+                * Terminate.\r
+                */\r
+               free_prompts(s->cur_prompt);\r
+               ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);\r
+               crStopV;\r
+           }\r
+           memcpy(s->username, s->cur_prompt->prompts[0]->result,\r
+                  lenof(s->username));\r
+           free_prompts(s->cur_prompt);\r
+       } else {\r
+           char *stuff;\r
+           if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {\r
+               stuff = dupprintf("Using username \"%s\".\r\n", s->username);\r
+               c_write_str(ssh, stuff);\r
+               sfree(stuff);\r
+           }\r
+       }\r
+       s->got_username = TRUE;\r
+\r
+       /*\r
+        * Send an authentication request using method "none": (a)\r
+        * just in case it succeeds, and (b) so that we know what\r
+        * authentication methods we can usefully try next.\r
+        */\r
+       ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;\r
+\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+       ssh2_pkt_addstring(s->pktout, s->username);\r
+       ssh2_pkt_addstring(s->pktout, "ssh-connection");/* service requested */\r
+       ssh2_pkt_addstring(s->pktout, "none");    /* method */\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+       s->type = AUTH_TYPE_NONE;\r
+       s->gotit = FALSE;\r
+       s->we_are_in = FALSE;\r
+\r
+       s->tried_pubkey_config = FALSE;\r
+       s->kbd_inter_refused = FALSE;\r
+\r
+       /* Reset agent request state. */\r
+       s->done_agent = FALSE;\r
+       if (s->agent_response) {\r
+           if (s->pkblob_in_agent) {\r
+               s->agentp = s->pkblob_in_agent;\r
+           } else {\r
+               s->agentp = s->agent_response + 5 + 4;\r
+               s->keyi = 0;\r
+           }\r
+       }\r
+\r
+       while (1) {\r
+           char *methods = NULL;\r
+           int methlen = 0;\r
+\r
+           /*\r
+            * Wait for the result of the last authentication request.\r
+            */\r
+           if (!s->gotit)\r
+               crWaitUntilV(pktin);\r
+           /*\r
+            * Now is a convenient point to spew any banner material\r
+            * that we've accumulated. (This should ensure that when\r
+            * we exit the auth loop, we haven't any left to deal\r
+            * with.)\r
+            */\r
+           {\r
+               int size = bufchain_size(&ssh->banner);\r
+               /*\r
+                * Don't show the banner if we're operating in\r
+                * non-verbose non-interactive mode. (It's probably\r
+                * a script, which means nobody will read the\r
+                * banner _anyway_, and moreover the printing of\r
+                * the banner will screw up processing on the\r
+                * output of (say) plink.)\r
+                */\r
+               if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {\r
+                   char *banner = snewn(size, char);\r
+                   bufchain_fetch(&ssh->banner, banner, size);\r
+                   c_write_untrusted(ssh, banner, size);\r
+                   sfree(banner);\r
+               }\r
+               bufchain_clear(&ssh->banner);\r
+           }\r
+           if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {\r
+               logevent("Access granted");\r
+               s->we_are_in = s->userauth_success = TRUE;\r
+               break;\r
+           }\r
+\r
+           if (pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) {\r
+               bombout(("Strange packet received during authentication: "\r
+                        "type %d", pktin->type));\r
+               crStopV;\r
+           }\r
+\r
+           s->gotit = FALSE;\r
+\r
+           /*\r
+            * OK, we're now sitting on a USERAUTH_FAILURE message, so\r
+            * we can look at the string in it and know what we can\r
+            * helpfully try next.\r
+            */\r
+           if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {\r
+               ssh_pkt_getstring(pktin, &methods, &methlen);\r
+               if (!ssh2_pkt_getbool(pktin)) {\r
+                   /*\r
+                    * We have received an unequivocal Access\r
+                    * Denied. This can translate to a variety of\r
+                    * messages:\r
+                    * \r
+                    *  - if we'd just tried "none" authentication,\r
+                    *    it's not worth printing anything at all\r
+                    * \r
+                    *  - if we'd just tried a public key _offer_,\r
+                    *    the message should be "Server refused our\r
+                    *    key" (or no message at all if the key\r
+                    *    came from Pageant)\r
+                    * \r
+                    *  - if we'd just tried anything else, the\r
+                    *    message really should be "Access denied".\r
+                    * \r
+                    * Additionally, if we'd just tried password\r
+                    * authentication, we should break out of this\r
+                    * whole loop so as to go back to the username\r
+                    * prompt (iff we're configured to allow\r
+                    * username change attempts).\r
+                    */\r
+                   if (s->type == AUTH_TYPE_NONE) {\r
+                       /* do nothing */\r
+                   } else if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD ||\r
+                              s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {\r
+                       if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)\r
+                           c_write_str(ssh, "Server refused our key\r\n");\r
+                       logevent("Server refused public key");\r
+                   } else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {\r
+                       /* server declined keyboard-interactive; ignore */\r
+                   } else {\r
+                       c_write_str(ssh, "Access denied\r\n");\r
+                       logevent("Access denied");\r
+                       if (s->type == AUTH_TYPE_PASSWORD &&\r
+                           ssh->cfg.change_username) {\r
+                           /* XXX perhaps we should allow\r
+                            * keyboard-interactive to do this too? */\r
+                           s->we_are_in = FALSE;\r
+                           break;\r
+                       }\r
+                   }\r
+               } else {\r
+                   c_write_str(ssh, "Further authentication required\r\n");\r
+                   logevent("Further authentication required");\r
+               }\r
+\r
+               s->can_pubkey =\r
+                   in_commasep_string("publickey", methods, methlen);\r
+               s->can_passwd =\r
+                   in_commasep_string("password", methods, methlen);\r
+               s->can_keyb_inter = ssh->cfg.try_ki_auth &&\r
+                   in_commasep_string("keyboard-interactive", methods, methlen);\r
+#ifndef NO_GSSAPI\r
+               if (!ssh->gsslibs)\r
+                   ssh->gsslibs = ssh_gss_setup(&ssh->cfg);\r
+               s->can_gssapi = ssh->cfg.try_gssapi_auth &&\r
+                   in_commasep_string("gssapi-with-mic", methods, methlen) &&\r
+                   ssh->gsslibs->nlibraries > 0;\r
+#endif\r
+           }\r
+\r
+           ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;\r
+\r
+           if (s->can_pubkey && !s->done_agent && s->nkeys) {\r
+\r
+               /*\r
+                * Attempt public-key authentication using a key from Pageant.\r
+                */\r
+\r
+               ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;\r
+\r
+               logeventf(ssh, "Trying Pageant key #%d", s->keyi);\r
+\r
+               /* Unpack key from agent response */\r
+               s->pklen = GET_32BIT(s->agentp);\r
+               s->agentp += 4;\r
+               s->pkblob = (char *)s->agentp;\r
+               s->agentp += s->pklen;\r
+               s->alglen = GET_32BIT(s->pkblob);\r
+               s->alg = s->pkblob + 4;\r
+               s->commentlen = GET_32BIT(s->agentp);\r
+               s->agentp += 4;\r
+               s->commentp = (char *)s->agentp;\r
+               s->agentp += s->commentlen;\r
+               /* s->agentp now points at next key, if any */\r
+\r
+               /* See if server will accept it */\r
+               s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+               ssh2_pkt_addstring(s->pktout, s->username);\r
+               ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+                                                   /* service requested */\r
+               ssh2_pkt_addstring(s->pktout, "publickey");\r
+                                                   /* method */\r
+               ssh2_pkt_addbool(s->pktout, FALSE); /* no signature included */\r
+               ssh2_pkt_addstring_start(s->pktout);\r
+               ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);\r
+               ssh2_pkt_addstring_start(s->pktout);\r
+               ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);\r
+               ssh2_pkt_send(ssh, s->pktout);\r
+               s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;\r
+\r
+               crWaitUntilV(pktin);\r
+               if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {\r
+\r
+                   /* Offer of key refused. */\r
+                   s->gotit = TRUE;\r
+\r
+               } else {\r
+                   \r
+                   void *vret;\r
+\r
+                   if (flags & FLAG_VERBOSE) {\r
+                       c_write_str(ssh, "Authenticating with "\r
+                                   "public key \"");\r
+                       c_write(ssh, s->commentp, s->commentlen);\r
+                       c_write_str(ssh, "\" from agent\r\n");\r
+                   }\r
+\r
+                   /*\r
+                    * Server is willing to accept the key.\r
+                    * Construct a SIGN_REQUEST.\r
+                    */\r
+                   s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+                   ssh2_pkt_addstring(s->pktout, s->username);\r
+                   ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+                                                       /* service requested */\r
+                   ssh2_pkt_addstring(s->pktout, "publickey");\r
+                                                       /* method */\r
+                   ssh2_pkt_addbool(s->pktout, TRUE);  /* signature included */\r
+                   ssh2_pkt_addstring_start(s->pktout);\r
+                   ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);\r
+                   ssh2_pkt_addstring_start(s->pktout);\r
+                   ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);\r
+\r
+                   /* Ask agent for signature. */\r
+                   s->siglen = s->pktout->length - 5 + 4 +\r
+                       ssh->v2_session_id_len;\r
+                   if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)\r
+                       s->siglen -= 4;\r
+                   s->len = 1;       /* message type */\r
+                   s->len += 4 + s->pklen;     /* key blob */\r
+                   s->len += 4 + s->siglen;    /* data to sign */\r
+                   s->len += 4;      /* flags */\r
+                   s->agentreq = snewn(4 + s->len, char);\r
+                   PUT_32BIT(s->agentreq, s->len);\r
+                   s->q = s->agentreq + 4;\r
+                   *s->q++ = SSH2_AGENTC_SIGN_REQUEST;\r
+                   PUT_32BIT(s->q, s->pklen);\r
+                   s->q += 4;\r
+                   memcpy(s->q, s->pkblob, s->pklen);\r
+                   s->q += s->pklen;\r
+                   PUT_32BIT(s->q, s->siglen);\r
+                   s->q += 4;\r
+                   /* Now the data to be signed... */\r
+                   if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {\r
+                       PUT_32BIT(s->q, ssh->v2_session_id_len);\r
+                       s->q += 4;\r
+                   }\r
+                   memcpy(s->q, ssh->v2_session_id,\r
+                          ssh->v2_session_id_len);\r
+                   s->q += ssh->v2_session_id_len;\r
+                   memcpy(s->q, s->pktout->data + 5,\r
+                          s->pktout->length - 5);\r
+                   s->q += s->pktout->length - 5;\r
+                   /* And finally the (zero) flags word. */\r
+                   PUT_32BIT(s->q, 0);\r
+                   if (!agent_query(s->agentreq, s->len + 4,\r
+                                    &vret, &s->retlen,\r
+                                    ssh_agent_callback, ssh)) {\r
+                       do {\r
+                           crReturnV;\r
+                           if (pktin) {\r
+                               bombout(("Unexpected data from server"\r
+                                        " while waiting for agent"\r
+                                        " response"));\r
+                               crStopV;\r
+                           }\r
+                       } while (pktin || inlen > 0);\r
+                       vret = ssh->agent_response;\r
+                       s->retlen = ssh->agent_response_len;\r
+                   }\r
+                   s->ret = vret;\r
+                   sfree(s->agentreq);\r
+                   if (s->ret) {\r
+                       if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {\r
+                           logevent("Sending Pageant's response");\r
+                           ssh2_add_sigblob(ssh, s->pktout,\r
+                                            s->pkblob, s->pklen,\r
+                                            s->ret + 9,\r
+                                            GET_32BIT(s->ret + 5));\r
+                           ssh2_pkt_send(ssh, s->pktout);\r
+                           s->type = AUTH_TYPE_PUBLICKEY;\r
+                       } else {\r
+                           /* FIXME: less drastic response */\r
+                           bombout(("Pageant failed to answer challenge"));\r
+                           crStopV;\r
+                       }\r
+                   }\r
+               }\r
+\r
+               /* Do we have any keys left to try? */\r
+               if (s->pkblob_in_agent) {\r
+                   s->done_agent = TRUE;\r
+                   s->tried_pubkey_config = TRUE;\r
+               } else {\r
+                   s->keyi++;\r
+                   if (s->keyi >= s->nkeys)\r
+                       s->done_agent = TRUE;\r
+               }\r
+\r
+           } else if (s->can_pubkey && s->publickey_blob &&\r
+                      !s->tried_pubkey_config) {\r
+\r
+               struct ssh2_userkey *key;   /* not live over crReturn */\r
+               char *passphrase;           /* not live over crReturn */\r
+\r
+               ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;\r
+\r
+               s->tried_pubkey_config = TRUE;\r
+\r
+               /*\r
+                * Try the public key supplied in the configuration.\r
+                *\r
+                * First, offer the public blob to see if the server is\r
+                * willing to accept it.\r
+                */\r
+               s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+               ssh2_pkt_addstring(s->pktout, s->username);\r
+               ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+                                               /* service requested */\r
+               ssh2_pkt_addstring(s->pktout, "publickey");     /* method */\r
+               ssh2_pkt_addbool(s->pktout, FALSE);\r
+                                               /* no signature included */\r
+               ssh2_pkt_addstring(s->pktout, s->publickey_algorithm);\r
+               ssh2_pkt_addstring_start(s->pktout);\r
+               ssh2_pkt_addstring_data(s->pktout,\r
+                                       (char *)s->publickey_blob,\r
+                                       s->publickey_bloblen);\r
+               ssh2_pkt_send(ssh, s->pktout);\r
+               logevent("Offered public key");\r
+\r
+               crWaitUntilV(pktin);\r
+               if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {\r
+                   /* Key refused. Give up. */\r
+                   s->gotit = TRUE; /* reconsider message next loop */\r
+                   s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;\r
+                   continue; /* process this new message */\r
+               }\r
+               logevent("Offer of public key accepted");\r
+\r
+               /*\r
+                * Actually attempt a serious authentication using\r
+                * the key.\r
+                */\r
+               if (flags & FLAG_VERBOSE) {\r
+                   c_write_str(ssh, "Authenticating with public key \"");\r
+                   c_write_str(ssh, s->publickey_comment);\r
+                   c_write_str(ssh, "\"\r\n");\r
+               }\r
+               key = NULL;\r
+               while (!key) {\r
+                   const char *error;  /* not live over crReturn */\r
+                   if (s->publickey_encrypted) {\r
+                       /*\r
+                        * Get a passphrase from the user.\r
+                        */\r
+                       int ret; /* need not be kept over crReturn */\r
+                       s->cur_prompt = new_prompts(ssh->frontend);\r
+                       s->cur_prompt->to_server = FALSE;\r
+                       s->cur_prompt->name = dupstr("SSH key passphrase");\r
+                       add_prompt(s->cur_prompt,\r
+                                  dupprintf("Passphrase for key \"%.100s\": ",\r
+                                            s->publickey_comment),\r
+                                  FALSE, SSH_MAX_PASSWORD_LEN);\r
+                       ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+                       while (ret < 0) {\r
+                           ssh->send_ok = 1;\r
+                           crWaitUntilV(!pktin);\r
+                           ret = get_userpass_input(s->cur_prompt,\r
+                                                    in, inlen);\r
+                           ssh->send_ok = 0;\r
+                       }\r
+                       if (!ret) {\r
+                           /* Failed to get a passphrase. Terminate. */\r
+                           free_prompts(s->cur_prompt);\r
+                           ssh_disconnect(ssh, NULL,\r
+                                          "Unable to authenticate",\r
+                                          SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,\r
+                                          TRUE);\r
+                           crStopV;\r
+                       }\r
+                       passphrase =\r
+                           dupstr(s->cur_prompt->prompts[0]->result);\r
+                       free_prompts(s->cur_prompt);\r
+                   } else {\r
+                       passphrase = NULL; /* no passphrase needed */\r
+                   }\r
+\r
+                   /*\r
+                    * Try decrypting the key.\r
+                    */\r
+                   key = ssh2_load_userkey(&ssh->cfg.keyfile, passphrase,\r
+                                           &error);\r
+                   if (passphrase) {\r
+                       /* burn the evidence */\r
+                       memset(passphrase, 0, strlen(passphrase));\r
+                       sfree(passphrase);\r
+                   }\r
+                   if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {\r
+                       if (passphrase &&\r
+                           (key == SSH2_WRONG_PASSPHRASE)) {\r
+                           c_write_str(ssh, "Wrong passphrase\r\n");\r
+                           key = NULL;\r
+                           /* and loop again */\r
+                       } else {\r
+                           c_write_str(ssh, "Unable to load private key (");\r
+                           c_write_str(ssh, error);\r
+                           c_write_str(ssh, ")\r\n");\r
+                           key = NULL;\r
+                           break; /* try something else */\r
+                       }\r
+                   }\r
+               }\r
+\r
+               if (key) {\r
+                   unsigned char *pkblob, *sigblob, *sigdata;\r
+                   int pkblob_len, sigblob_len, sigdata_len;\r
+                   int p;\r
+\r
+                   /*\r
+                    * We have loaded the private key and the server\r
+                    * has announced that it's willing to accept it.\r
+                    * Hallelujah. Generate a signature and send it.\r
+                    */\r
+                   s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+                   ssh2_pkt_addstring(s->pktout, s->username);\r
+                   ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+                                                   /* service requested */\r
+                   ssh2_pkt_addstring(s->pktout, "publickey");\r
+                                                   /* method */\r
+                   ssh2_pkt_addbool(s->pktout, TRUE);\r
+                                                   /* signature follows */\r
+                   ssh2_pkt_addstring(s->pktout, key->alg->name);\r
+                   pkblob = key->alg->public_blob(key->data,\r
+                                                  &pkblob_len);\r
+                   ssh2_pkt_addstring_start(s->pktout);\r
+                   ssh2_pkt_addstring_data(s->pktout, (char *)pkblob,\r
+                                           pkblob_len);\r
+\r
+                   /*\r
+                    * The data to be signed is:\r
+                    *\r
+                    *   string  session-id\r
+                    *\r
+                    * followed by everything so far placed in the\r
+                    * outgoing packet.\r
+                    */\r
+                   sigdata_len = s->pktout->length - 5 + 4 +\r
+                       ssh->v2_session_id_len;\r
+                   if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)\r
+                       sigdata_len -= 4;\r
+                   sigdata = snewn(sigdata_len, unsigned char);\r
+                   p = 0;\r
+                   if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {\r
+                       PUT_32BIT(sigdata+p, ssh->v2_session_id_len);\r
+                       p += 4;\r
+                   }\r
+                   memcpy(sigdata+p, ssh->v2_session_id,\r
+                          ssh->v2_session_id_len);\r
+                   p += ssh->v2_session_id_len;\r
+                   memcpy(sigdata+p, s->pktout->data + 5,\r
+                          s->pktout->length - 5);\r
+                   p += s->pktout->length - 5;\r
+                   assert(p == sigdata_len);\r
+                   sigblob = key->alg->sign(key->data, (char *)sigdata,\r
+                                            sigdata_len, &sigblob_len);\r
+                   ssh2_add_sigblob(ssh, s->pktout, pkblob, pkblob_len,\r
+                                    sigblob, sigblob_len);\r
+                   sfree(pkblob);\r
+                   sfree(sigblob);\r
+                   sfree(sigdata);\r
+\r
+                   ssh2_pkt_send(ssh, s->pktout);\r
+                   s->type = AUTH_TYPE_PUBLICKEY;\r
+                   key->alg->freekey(key->data);\r
+               }\r
+\r
+#ifndef NO_GSSAPI\r
+           } else if (s->can_gssapi && !s->tried_gssapi) {\r
+\r
+               /* GSSAPI Authentication */\r
+\r
+               int micoffset, len;\r
+               char *data;\r
+               Ssh_gss_buf mic;\r
+               s->type = AUTH_TYPE_GSSAPI;\r
+               s->tried_gssapi = TRUE;\r
+               s->gotit = TRUE;\r
+               ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;\r
+\r
+               /*\r
+                * Pick the highest GSS library on the preference\r
+                * list.\r
+                */\r
+               {\r
+                   int i, j;\r
+                   s->gsslib = NULL;\r
+                   for (i = 0; i < ngsslibs; i++) {\r
+                       int want_id = ssh->cfg.ssh_gsslist[i];\r
+                       for (j = 0; j < ssh->gsslibs->nlibraries; j++)\r
+                           if (ssh->gsslibs->libraries[j].id == want_id) {\r
+                               s->gsslib = &ssh->gsslibs->libraries[j];\r
+                               goto got_gsslib;   /* double break */\r
+                           }\r
+                   }\r
+                   got_gsslib:\r
+                   /*\r
+                    * We always expect to have found something in\r
+                    * the above loop: we only came here if there\r
+                    * was at least one viable GSS library, and the\r
+                    * preference list should always mention\r
+                    * everything and only change the order.\r
+                    */\r
+                   assert(s->gsslib);\r
+               }\r
+\r
+               if (s->gsslib->gsslogmsg)\r
+                   logevent(s->gsslib->gsslogmsg);\r
+\r
+               /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */\r
+               s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+               ssh2_pkt_addstring(s->pktout, s->username);\r
+               ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+               ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");\r
+\r
+               /* add mechanism info */\r
+               s->gsslib->indicate_mech(s->gsslib, &s->gss_buf);\r
+\r
+               /* number of GSSAPI mechanisms */\r
+               ssh2_pkt_adduint32(s->pktout,1);\r
+\r
+               /* length of OID + 2 */\r
+               ssh2_pkt_adduint32(s->pktout, s->gss_buf.length + 2);\r
+               ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE);\r
+\r
+               /* length of OID */\r
+               ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.length);\r
+\r
+               ssh_pkt_adddata(s->pktout, s->gss_buf.value,\r
+                               s->gss_buf.length);\r
+               ssh2_pkt_send(ssh, s->pktout);\r
+               crWaitUntilV(pktin);\r
+               if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {\r
+                   logevent("GSSAPI authentication request refused");\r
+                   continue;\r
+               }\r
+\r
+               /* check returned packet ... */\r
+\r
+               ssh_pkt_getstring(pktin, &data, &len);\r
+               s->gss_rcvtok.value = data;\r
+               s->gss_rcvtok.length = len;\r
+               if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||\r
+                   ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||\r
+                   ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||\r
+                   memcmp((char *)s->gss_rcvtok.value + 2,\r
+                          s->gss_buf.value,s->gss_buf.length) ) {\r
+                   logevent("GSSAPI authentication - wrong response from server");\r
+                   continue;\r
+               }\r
+\r
+               /* now start running */\r
+               s->gss_stat = s->gsslib->import_name(s->gsslib,\r
+                                                    ssh->fullhostname,\r
+                                                    &s->gss_srv_name);\r
+               if (s->gss_stat != SSH_GSS_OK) {\r
+                   if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)\r
+                       logevent("GSSAPI import name failed - Bad service name");\r
+                   else\r
+                       logevent("GSSAPI import name failed");\r
+                   continue;\r
+               }\r
+\r
+               /* fetch TGT into GSS engine */\r
+               s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &s->gss_ctx);\r
+\r
+               if (s->gss_stat != SSH_GSS_OK) {\r
+                   logevent("GSSAPI authentication failed to get credentials");\r
+                   s->gsslib->release_name(s->gsslib, &s->gss_srv_name);\r
+                   continue;\r
+               }\r
+\r
+               /* initial tokens are empty */\r
+               SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);\r
+               SSH_GSS_CLEAR_BUF(&s->gss_sndtok);\r
+\r
+               /* now enter the loop */\r
+               do {\r
+                   s->gss_stat = s->gsslib->init_sec_context\r
+                       (s->gsslib,\r
+                        &s->gss_ctx,\r
+                        s->gss_srv_name,\r
+                        ssh->cfg.gssapifwd,\r
+                        &s->gss_rcvtok,\r
+                        &s->gss_sndtok);\r
+\r
+                   if (s->gss_stat!=SSH_GSS_S_COMPLETE &&\r
+                       s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {\r
+                       logevent("GSSAPI authentication initialisation failed");\r
+\r
+                       if (s->gsslib->display_status(s->gsslib, s->gss_ctx,\r
+                                                     &s->gss_buf) == SSH_GSS_OK) {\r
+                           logevent(s->gss_buf.value);\r
+                           sfree(s->gss_buf.value);\r
+                       }\r
+\r
+                       break;\r
+                   }\r
+                   logevent("GSSAPI authentication initialised");\r
+\r
+                   /* Client and server now exchange tokens until GSSAPI\r
+                    * no longer says CONTINUE_NEEDED */\r
+\r
+                   if (s->gss_sndtok.length != 0) {\r
+                       s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);\r
+                       ssh_pkt_addstring_start(s->pktout);\r
+                       ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length);\r
+                       ssh2_pkt_send(ssh, s->pktout);\r
+                       s->gsslib->free_tok(s->gsslib, &s->gss_sndtok);\r
+                   }\r
+\r
+                   if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {\r
+                       crWaitUntilV(pktin);\r
+                       if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) {\r
+                           logevent("GSSAPI authentication - bad server response");\r
+                           s->gss_stat = SSH_GSS_FAILURE;\r
+                           break;\r
+                       }\r
+                       ssh_pkt_getstring(pktin, &data, &len);\r
+                       s->gss_rcvtok.value = data;\r
+                       s->gss_rcvtok.length = len;\r
+                   }\r
+               } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);\r
+\r
+               if (s->gss_stat != SSH_GSS_OK) {\r
+                   s->gsslib->release_name(s->gsslib, &s->gss_srv_name);\r
+                   s->gsslib->release_cred(s->gsslib, &s->gss_ctx);\r
+                   continue;\r
+               }\r
+               logevent("GSSAPI authentication loop finished OK");\r
+\r
+               /* Now send the MIC */\r
+\r
+               s->pktout = ssh2_pkt_init(0);\r
+               micoffset = s->pktout->length;\r
+               ssh_pkt_addstring_start(s->pktout);\r
+               ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len);\r
+               ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST);\r
+               ssh_pkt_addstring(s->pktout, s->username);\r
+               ssh_pkt_addstring(s->pktout, "ssh-connection");\r
+               ssh_pkt_addstring(s->pktout, "gssapi-with-mic");\r
+\r
+               s->gss_buf.value = (char *)s->pktout->data + micoffset;\r
+               s->gss_buf.length = s->pktout->length - micoffset;\r
+\r
+               s->gsslib->get_mic(s->gsslib, s->gss_ctx, &s->gss_buf, &mic);\r
+               s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC);\r
+               ssh_pkt_addstring_start(s->pktout);\r
+               ssh_pkt_addstring_data(s->pktout, mic.value, mic.length);\r
+               ssh2_pkt_send(ssh, s->pktout);\r
+               s->gsslib->free_mic(s->gsslib, &mic);\r
+\r
+               s->gotit = FALSE;\r
+\r
+               s->gsslib->release_name(s->gsslib, &s->gss_srv_name);\r
+               s->gsslib->release_cred(s->gsslib, &s->gss_ctx);\r
+               continue;\r
+#endif\r
+           } else if (s->can_keyb_inter && !s->kbd_inter_refused) {\r
+\r
+               /*\r
+                * Keyboard-interactive authentication.\r
+                */\r
+\r
+               s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;\r
+\r
+               ssh->pkt_actx = SSH2_PKTCTX_KBDINTER;\r
+\r
+               s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+               ssh2_pkt_addstring(s->pktout, s->username);\r
+               ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+                                                       /* service requested */\r
+               ssh2_pkt_addstring(s->pktout, "keyboard-interactive");\r
+                                                       /* method */\r
+               ssh2_pkt_addstring(s->pktout, "");      /* lang */\r
+               ssh2_pkt_addstring(s->pktout, "");      /* submethods */\r
+               ssh2_pkt_send(ssh, s->pktout);\r
+\r
+               crWaitUntilV(pktin);\r
+               if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {\r
+                   /* Server is not willing to do keyboard-interactive\r
+                    * at all (or, bizarrely but legally, accepts the\r
+                    * user without actually issuing any prompts).\r
+                    * Give up on it entirely. */\r
+                   s->gotit = TRUE;\r
+                   if (pktin->type == SSH2_MSG_USERAUTH_FAILURE)\r
+                       logevent("Keyboard-interactive authentication refused");\r
+                   s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;\r
+                   s->kbd_inter_refused = TRUE; /* don't try it again */\r
+                   continue;\r
+               }\r
+\r
+               /*\r
+                * Loop while the server continues to send INFO_REQUESTs.\r
+                */\r
+               while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {\r
+\r
+                   char *name, *inst, *lang;\r
+                   int name_len, inst_len, lang_len;\r
+                   int i;\r
+\r
+                   /*\r
+                    * We've got a fresh USERAUTH_INFO_REQUEST.\r
+                    * Get the preamble and start building a prompt.\r
+                    */\r
+                   ssh_pkt_getstring(pktin, &name, &name_len);\r
+                   ssh_pkt_getstring(pktin, &inst, &inst_len);\r
+                   ssh_pkt_getstring(pktin, &lang, &lang_len);\r
+                   s->cur_prompt = new_prompts(ssh->frontend);\r
+                   s->cur_prompt->to_server = TRUE;\r
+\r
+                   /*\r
+                    * Get any prompt(s) from the packet.\r
+                    */\r
+                   s->num_prompts = ssh_pkt_getuint32(pktin);\r
+                   for (i = 0; i < s->num_prompts; i++) {\r
+                       char *prompt;\r
+                       int prompt_len;\r
+                       int echo;\r
+                       static char noprompt[] =\r
+                           "<server failed to send prompt>: ";\r
+\r
+                       ssh_pkt_getstring(pktin, &prompt, &prompt_len);\r
+                       echo = ssh2_pkt_getbool(pktin);\r
+                       if (!prompt_len) {\r
+                           prompt = noprompt;\r
+                           prompt_len = lenof(noprompt)-1;\r
+                       }\r
+                       add_prompt(s->cur_prompt,\r
+                                  dupprintf("%.*s", prompt_len, prompt),\r
+                                  echo, SSH_MAX_PASSWORD_LEN);\r
+                   }\r
+\r
+                   if (name_len) {\r
+                       /* FIXME: better prefix to distinguish from\r
+                        * local prompts? */\r
+                       s->cur_prompt->name =\r
+                           dupprintf("SSH server: %.*s", name_len, name);\r
+                       s->cur_prompt->name_reqd = TRUE;\r
+                   } else {\r
+                       s->cur_prompt->name =\r
+                           dupstr("SSH server authentication");\r
+                       s->cur_prompt->name_reqd = FALSE;\r
+                   }\r
+                   /* We add a prefix to try to make it clear that a prompt\r
+                    * has come from the server.\r
+                    * FIXME: ugly to print "Using..." in prompt _every_\r
+                    * time round. Can this be done more subtly? */\r
+                   /* Special case: for reasons best known to themselves,\r
+                    * some servers send k-i requests with no prompts and\r
+                    * nothing to display. Keep quiet in this case. */\r
+                   if (s->num_prompts || name_len || inst_len) {\r
+                       s->cur_prompt->instruction =\r
+                           dupprintf("Using keyboard-interactive authentication.%s%.*s",\r
+                                     inst_len ? "\n" : "", inst_len, inst);\r
+                       s->cur_prompt->instr_reqd = TRUE;\r
+                   } else {\r
+                       s->cur_prompt->instr_reqd = FALSE;\r
+                   }\r
+\r
+                   /*\r
+                     * Display any instructions, and get the user's\r
+                     * response(s).\r
+                    */\r
+                   {\r
+                       int ret; /* not live over crReturn */\r
+                       ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+                       while (ret < 0) {\r
+                           ssh->send_ok = 1;\r
+                           crWaitUntilV(!pktin);\r
+                           ret = get_userpass_input(s->cur_prompt, in, inlen);\r
+                           ssh->send_ok = 0;\r
+                       }\r
+                       if (!ret) {\r
+                           /*\r
+                            * Failed to get responses. Terminate.\r
+                            */\r
+                           free_prompts(s->cur_prompt);\r
+                           ssh_disconnect(ssh, NULL, "Unable to authenticate",\r
+                                          SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,\r
+                                          TRUE);\r
+                           crStopV;\r
+                       }\r
+                   }\r
+\r
+                   /*\r
+                    * Send the response(s) to the server.\r
+                    */\r
+                   s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);\r
+                   ssh2_pkt_adduint32(s->pktout, s->num_prompts);\r
+                   for (i=0; i < s->num_prompts; i++) {\r
+                       dont_log_password(ssh, s->pktout, PKTLOG_BLANK);\r
+                       ssh2_pkt_addstring(s->pktout,\r
+                                          s->cur_prompt->prompts[i]->result);\r
+                       end_log_omission(ssh, s->pktout);\r
+                   }\r
+                   ssh2_pkt_send_with_padding(ssh, s->pktout, 256);\r
+\r
+                   /*\r
+                    * Get the next packet in case it's another\r
+                    * INFO_REQUEST.\r
+                    */\r
+                   crWaitUntilV(pktin);\r
+\r
+               }\r
+\r
+               /*\r
+                * We should have SUCCESS or FAILURE now.\r
+                */\r
+               s->gotit = TRUE;\r
+\r
+           } else if (s->can_passwd) {\r
+\r
+               /*\r
+                * Plain old password authentication.\r
+                */\r
+               int ret; /* not live over crReturn */\r
+               int changereq_first_time; /* not live over crReturn */\r
+\r
+               ssh->pkt_actx = SSH2_PKTCTX_PASSWORD;\r
+\r
+               s->cur_prompt = new_prompts(ssh->frontend);\r
+               s->cur_prompt->to_server = TRUE;\r
+               s->cur_prompt->name = dupstr("SSH password");\r
+               add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",\r
+                                                   s->username,\r
+                                                   ssh->savedhost),\r
+                          FALSE, SSH_MAX_PASSWORD_LEN);\r
+\r
+               ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+               while (ret < 0) {\r
+                   ssh->send_ok = 1;\r
+                   crWaitUntilV(!pktin);\r
+                   ret = get_userpass_input(s->cur_prompt, in, inlen);\r
+                   ssh->send_ok = 0;\r
+               }\r
+               if (!ret) {\r
+                   /*\r
+                    * Failed to get responses. Terminate.\r
+                    */\r
+                   free_prompts(s->cur_prompt);\r
+                   ssh_disconnect(ssh, NULL, "Unable to authenticate",\r
+                                  SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,\r
+                                  TRUE);\r
+                   crStopV;\r
+               }\r
+               /*\r
+                * Squirrel away the password. (We may need it later if\r
+                * asked to change it.)\r
+                */\r
+               s->password = dupstr(s->cur_prompt->prompts[0]->result);\r
+               free_prompts(s->cur_prompt);\r
+\r
+               /*\r
+                * Send the password packet.\r
+                *\r
+                * We pad out the password packet to 256 bytes to make\r
+                * it harder for an attacker to find the length of the\r
+                * user's password.\r
+                *\r
+                * Anyone using a password longer than 256 bytes\r
+                * probably doesn't have much to worry about from\r
+                * people who find out how long their password is!\r
+                */\r
+               s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+               ssh2_pkt_addstring(s->pktout, s->username);\r
+               ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+                                                       /* service requested */\r
+               ssh2_pkt_addstring(s->pktout, "password");\r
+               ssh2_pkt_addbool(s->pktout, FALSE);\r
+               dont_log_password(ssh, s->pktout, PKTLOG_BLANK);\r
+               ssh2_pkt_addstring(s->pktout, s->password);\r
+               end_log_omission(ssh, s->pktout);\r
+               ssh2_pkt_send_with_padding(ssh, s->pktout, 256);\r
+               logevent("Sent password");\r
+               s->type = AUTH_TYPE_PASSWORD;\r
+\r
+               /*\r
+                * Wait for next packet, in case it's a password change\r
+                * request.\r
+                */\r
+               crWaitUntilV(pktin);\r
+               changereq_first_time = TRUE;\r
+\r
+               while (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {\r
+\r
+                   /* \r
+                    * We're being asked for a new password\r
+                    * (perhaps not for the first time).\r
+                    * Loop until the server accepts it.\r
+                    */\r
+\r
+                   int got_new = FALSE; /* not live over crReturn */\r
+                   char *prompt;   /* not live over crReturn */\r
+                   int prompt_len; /* not live over crReturn */\r
+                   \r
+                   {\r
+                       char *msg;\r
+                       if (changereq_first_time)\r
+                           msg = "Server requested password change";\r
+                       else\r
+                           msg = "Server rejected new password";\r
+                       logevent(msg);\r
+                       c_write_str(ssh, msg);\r
+                       c_write_str(ssh, "\r\n");\r
+                   }\r
+\r
+                   ssh_pkt_getstring(pktin, &prompt, &prompt_len);\r
+\r
+                   s->cur_prompt = new_prompts(ssh->frontend);\r
+                   s->cur_prompt->to_server = TRUE;\r
+                   s->cur_prompt->name = dupstr("New SSH password");\r
+                   s->cur_prompt->instruction =\r
+                       dupprintf("%.*s", prompt_len, prompt);\r
+                   s->cur_prompt->instr_reqd = TRUE;\r
+                   /*\r
+                    * There's no explicit requirement in the protocol\r
+                    * for the "old" passwords in the original and\r
+                    * password-change messages to be the same, and\r
+                    * apparently some Cisco kit supports password change\r
+                    * by the user entering a blank password originally\r
+                    * and the real password subsequently, so,\r
+                    * reluctantly, we prompt for the old password again.\r
+                    *\r
+                    * (On the other hand, some servers don't even bother\r
+                    * to check this field.)\r
+                    */\r
+                   add_prompt(s->cur_prompt,\r
+                              dupstr("Current password (blank for previously entered password): "),\r
+                              FALSE, SSH_MAX_PASSWORD_LEN);\r
+                   add_prompt(s->cur_prompt, dupstr("Enter new password: "),\r
+                              FALSE, SSH_MAX_PASSWORD_LEN);\r
+                   add_prompt(s->cur_prompt, dupstr("Confirm new password: "),\r
+                              FALSE, SSH_MAX_PASSWORD_LEN);\r
+\r
+                   /*\r
+                    * Loop until the user manages to enter the same\r
+                    * password twice.\r
+                    */\r
+                   while (!got_new) {\r
+\r
+                       ret = get_userpass_input(s->cur_prompt, NULL, 0);\r
+                       while (ret < 0) {\r
+                           ssh->send_ok = 1;\r
+                           crWaitUntilV(!pktin);\r
+                           ret = get_userpass_input(s->cur_prompt, in, inlen);\r
+                           ssh->send_ok = 0;\r
+                       }\r
+                       if (!ret) {\r
+                           /*\r
+                            * Failed to get responses. Terminate.\r
+                            */\r
+                           /* burn the evidence */\r
+                           free_prompts(s->cur_prompt);\r
+                           memset(s->password, 0, strlen(s->password));\r
+                           sfree(s->password);\r
+                           ssh_disconnect(ssh, NULL, "Unable to authenticate",\r
+                                          SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,\r
+                                          TRUE);\r
+                           crStopV;\r
+                       }\r
+\r
+                       /*\r
+                        * If the user specified a new original password\r
+                        * (IYSWIM), overwrite any previously specified\r
+                        * one.\r
+                        * (A side effect is that the user doesn't have to\r
+                        * re-enter it if they louse up the new password.)\r
+                        */\r
+                       if (s->cur_prompt->prompts[0]->result[0]) {\r
+                           memset(s->password, 0, strlen(s->password));\r
+                               /* burn the evidence */\r
+                           sfree(s->password);\r
+                           s->password =\r
+                               dupstr(s->cur_prompt->prompts[0]->result);\r
+                       }\r
+\r
+                       /*\r
+                        * Check the two new passwords match.\r
+                        */\r
+                       got_new = (strcmp(s->cur_prompt->prompts[1]->result,\r
+                                         s->cur_prompt->prompts[2]->result)\r
+                                  == 0);\r
+                       if (!got_new)\r
+                           /* They don't. Silly user. */\r
+                           c_write_str(ssh, "Passwords do not match\r\n");\r
+\r
+                   }\r
+\r
+                   /*\r
+                    * Send the new password (along with the old one).\r
+                    * (see above for padding rationale)\r
+                    */\r
+                   s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);\r
+                   ssh2_pkt_addstring(s->pktout, s->username);\r
+                   ssh2_pkt_addstring(s->pktout, "ssh-connection");\r
+                                                       /* service requested */\r
+                   ssh2_pkt_addstring(s->pktout, "password");\r
+                   ssh2_pkt_addbool(s->pktout, TRUE);\r
+                   dont_log_password(ssh, s->pktout, PKTLOG_BLANK);\r
+                   ssh2_pkt_addstring(s->pktout, s->password);\r
+                   ssh2_pkt_addstring(s->pktout,\r
+                                      s->cur_prompt->prompts[1]->result);\r
+                   free_prompts(s->cur_prompt);\r
+                   end_log_omission(ssh, s->pktout);\r
+                   ssh2_pkt_send_with_padding(ssh, s->pktout, 256);\r
+                   logevent("Sent new password");\r
+                   \r
+                   /*\r
+                    * Now see what the server has to say about it.\r
+                    * (If it's CHANGEREQ again, it's not happy with the\r
+                    * new password.)\r
+                    */\r
+                   crWaitUntilV(pktin);\r
+                   changereq_first_time = FALSE;\r
+\r
+               }\r
+\r
+               /*\r
+                * We need to reexamine the current pktin at the top\r
+                * of the loop. Either:\r
+                *  - we weren't asked to change password at all, in\r
+                *    which case it's a SUCCESS or FAILURE with the\r
+                *    usual meaning\r
+                *  - we sent a new password, and the server was\r
+                *    either OK with it (SUCCESS or FAILURE w/partial\r
+                *    success) or unhappy with the _old_ password\r
+                *    (FAILURE w/o partial success)\r
+                * In any of these cases, we go back to the top of\r
+                * the loop and start again.\r
+                */\r
+               s->gotit = TRUE;\r
+\r
+               /*\r
+                * We don't need the old password any more, in any\r
+                * case. Burn the evidence.\r
+                */\r
+               memset(s->password, 0, strlen(s->password));\r
+               sfree(s->password);\r
+\r
+           } else {\r
+               char *str = dupprintf("No supported authentication methods available"\r
+                                     " (server sent: %.*s)",\r
+                                     methlen, methods);\r
+\r
+               ssh_disconnect(ssh, str,\r
+                              "No supported authentication methods available",\r
+                              SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,\r
+                              FALSE);\r
+               sfree(str);\r
+\r
+               crStopV;\r
+\r
+           }\r
+\r
+       }\r
+    }\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL;\r
+\r
+    /* Clear up various bits and pieces from authentication. */\r
+    if (s->publickey_blob) {\r
+       sfree(s->publickey_blob);\r
+       sfree(s->publickey_comment);\r
+    }\r
+    if (s->agent_response)\r
+       sfree(s->agent_response);\r
+\r
+    if (s->userauth_success) {\r
+       /*\r
+        * We've just received USERAUTH_SUCCESS, and we haven't sent any\r
+        * packets since. Signal the transport layer to consider enacting\r
+        * delayed compression.\r
+        *\r
+        * (Relying on we_are_in is not sufficient, as\r
+        * draft-miller-secsh-compression-delayed is quite clear that it\r
+        * triggers on USERAUTH_SUCCESS specifically, and we_are_in can\r
+        * become set for other reasons.)\r
+        */\r
+       do_ssh2_transport(ssh, "enabling delayed compression", -2, NULL);\r
+    }\r
+\r
+    /*\r
+     * Now the connection protocol has started, one way or another.\r
+     */\r
+\r
+    ssh->channels = newtree234(ssh_channelcmp);\r
+\r
+    /*\r
+     * Set up handlers for some connection protocol messages, so we\r
+     * don't have to handle them repeatedly in this coroutine.\r
+     */\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] =\r
+       ssh2_msg_channel_window_adjust;\r
+    ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] =\r
+       ssh2_msg_global_request;\r
+\r
+    /*\r
+     * Create the main session channel.\r
+     */\r
+    if (ssh->cfg.ssh_no_shell) {\r
+       ssh->mainchan = NULL;\r
+    } else if (*ssh->cfg.ssh_nc_host) {\r
+       /*\r
+        * Just start a direct-tcpip channel and use it as the main\r
+        * channel.\r
+        */\r
+       ssh->mainchan = snew(struct ssh_channel);\r
+       ssh->mainchan->ssh = ssh;\r
+       ssh2_channel_init(ssh->mainchan);\r
+       logeventf(ssh,\r
+                 "Opening direct-tcpip channel to %s:%d in place of session",\r
+                 ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port);\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);\r
+       ssh2_pkt_addstring(s->pktout, "direct-tcpip");\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */\r
+       ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);      /* our max pkt size */\r
+       ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port);\r
+       /*\r
+        * There's nothing meaningful to put in the originator\r
+        * fields, but some servers insist on syntactically correct\r
+        * information.\r
+        */\r
+       ssh2_pkt_addstring(s->pktout, "0.0.0.0");\r
+       ssh2_pkt_adduint32(s->pktout, 0);\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+\r
+       crWaitUntilV(pktin);\r
+       if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {\r
+           bombout(("Server refused to open a direct-tcpip channel"));\r
+           crStopV;\r
+           /* FIXME: error data comes back in FAILURE packet */\r
+       }\r
+       if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {\r
+           bombout(("Server's channel confirmation cited wrong channel"));\r
+           crStopV;\r
+       }\r
+       ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);\r
+       ssh->mainchan->halfopen = FALSE;\r
+       ssh->mainchan->type = CHAN_MAINSESSION;\r
+       ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);\r
+       ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);\r
+       add234(ssh->channels, ssh->mainchan);\r
+       update_specials_menu(ssh->frontend);\r
+       logevent("Opened direct-tcpip channel");\r
+       ssh->ncmode = TRUE;\r
+    } else {\r
+       ssh->mainchan = snew(struct ssh_channel);\r
+       ssh->mainchan->ssh = ssh;\r
+       ssh2_channel_init(ssh->mainchan);\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);\r
+       ssh2_pkt_addstring(s->pktout, "session");\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */\r
+       ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);    /* our max pkt size */\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+       crWaitUntilV(pktin);\r
+       if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {\r
+           bombout(("Server refused to open a session"));\r
+           crStopV;\r
+           /* FIXME: error data comes back in FAILURE packet */\r
+       }\r
+       if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {\r
+           bombout(("Server's channel confirmation cited wrong channel"));\r
+           crStopV;\r
+       }\r
+       ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);\r
+       ssh->mainchan->halfopen = FALSE;\r
+       ssh->mainchan->type = CHAN_MAINSESSION;\r
+       ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);\r
+       ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);\r
+       add234(ssh->channels, ssh->mainchan);\r
+       update_specials_menu(ssh->frontend);\r
+       logevent("Opened channel for session");\r
+       ssh->ncmode = FALSE;\r
+    }\r
+\r
+    /*\r
+     * Now we have a channel, make dispatch table entries for\r
+     * general channel-based messages.\r
+     */\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] =\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] =\r
+       ssh2_msg_channel_data;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_channel_eof;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_channel_close;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] =\r
+       ssh2_msg_channel_open_confirmation;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] =\r
+       ssh2_msg_channel_open_failure;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] =\r
+       ssh2_msg_channel_request;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =\r
+       ssh2_msg_channel_open;\r
+\r
+    if (ssh->mainchan && ssh->cfg.ssh_simple) {\r
+       /*\r
+        * This message indicates to the server that we promise\r
+        * not to try to run any other channel in parallel with\r
+        * this one, so it's safe for it to advertise a very large\r
+        * window and leave the flow control to TCP.\r
+        */\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);\r
+       ssh2_pkt_addstring(s->pktout, "simple@putty.projects.tartarus.org");\r
+       ssh2_pkt_addbool(s->pktout, 0); /* no reply */\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+    }\r
+\r
+    /*\r
+     * Potentially enable X11 forwarding.\r
+     */\r
+    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward &&\r
+       (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,\r
+                                         ssh->cfg.x11_auth, &ssh->cfg))) {\r
+       logevent("Requesting X11 forwarding");\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);\r
+       ssh2_pkt_addstring(s->pktout, "x11-req");\r
+       ssh2_pkt_addbool(s->pktout, 1);        /* want reply */\r
+       ssh2_pkt_addbool(s->pktout, 0);        /* many connections */\r
+       ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname);\r
+       /*\r
+        * Note that while we blank the X authentication data here, we don't\r
+        * take any special action to blank the start of an X11 channel,\r
+        * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection\r
+        * without having session blanking enabled is likely to leak your\r
+        * cookie into the log.\r
+        */\r
+       dont_log_password(ssh, s->pktout, PKTLOG_BLANK);\r
+       ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthdatastring);\r
+       end_log_omission(ssh, s->pktout);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum);\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+\r
+       crWaitUntilV(pktin);\r
+\r
+       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {\r
+           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {\r
+               bombout(("Unexpected response to X11 forwarding request:"\r
+                        " packet type %d", pktin->type));\r
+               crStopV;\r
+           }\r
+           logevent("X11 forwarding refused");\r
+       } else {\r
+           logevent("X11 forwarding enabled");\r
+           ssh->X11_fwd_enabled = TRUE;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Enable port forwardings.\r
+     */\r
+    ssh_setup_portfwd(ssh, &ssh->cfg);\r
+\r
+    /*\r
+     * Potentially enable agent forwarding.\r
+     */\r
+    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) {\r
+       logevent("Requesting OpenSSH-style agent forwarding");\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);\r
+       ssh2_pkt_addstring(s->pktout, "auth-agent-req@openssh.com");\r
+       ssh2_pkt_addbool(s->pktout, 1);        /* want reply */\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+\r
+       crWaitUntilV(pktin);\r
+\r
+       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {\r
+           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {\r
+               bombout(("Unexpected response to agent forwarding request:"\r
+                        " packet type %d", pktin->type));\r
+               crStopV;\r
+           }\r
+           logevent("Agent forwarding refused");\r
+       } else {\r
+           logevent("Agent forwarding enabled");\r
+           ssh->agentfwd_enabled = TRUE;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Now allocate a pty for the session.\r
+     */\r
+    if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) {\r
+       /* Unpick the terminal-speed string. */\r
+       /* XXX perhaps we should allow no speeds to be sent. */\r
+        ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */\r
+       sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed);\r
+       /* Build the pty request. */\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */\r
+       ssh2_pkt_addstring(s->pktout, "pty-req");\r
+       ssh2_pkt_addbool(s->pktout, 1);        /* want reply */\r
+       ssh2_pkt_addstring(s->pktout, ssh->cfg.termtype);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->term_width);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->term_height);\r
+       ssh2_pkt_adduint32(s->pktout, 0);              /* pixel width */\r
+       ssh2_pkt_adduint32(s->pktout, 0);              /* pixel height */\r
+       ssh2_pkt_addstring_start(s->pktout);\r
+       parse_ttymodes(ssh, ssh->cfg.ttymodes,\r
+                      ssh2_send_ttymode, (void *)s->pktout);\r
+       ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->ispeed);\r
+       ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->ospeed);\r
+       ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+       ssh->state = SSH_STATE_INTERMED;\r
+\r
+       crWaitUntilV(pktin);\r
+\r
+       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {\r
+           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {\r
+               bombout(("Unexpected response to pty request:"\r
+                        " packet type %d", pktin->type));\r
+               crStopV;\r
+           }\r
+           c_write_str(ssh, "Server refused to allocate pty\r\n");\r
+           ssh->editing = ssh->echoing = 1;\r
+       } else {\r
+           logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",\r
+                     ssh->ospeed, ssh->ispeed);\r
+       }\r
+    } else {\r
+       ssh->editing = ssh->echoing = 1;\r
+    }\r
+\r
+    /*\r
+     * Send environment variables.\r
+     * \r
+     * Simplest thing here is to send all the requests at once, and\r
+     * then wait for a whole bunch of successes or failures.\r
+     */\r
+    if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) {\r
+       char *e = ssh->cfg.environmt;\r
+       char *var, *varend, *val;\r
+\r
+       s->num_env = 0;\r
+\r
+       while (*e) {\r
+           var = e;\r
+           while (*e && *e != '\t') e++;\r
+           varend = e;\r
+           if (*e == '\t') e++;\r
+           val = e;\r
+           while (*e) e++;\r
+           e++;\r
+\r
+           s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+           ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);\r
+           ssh2_pkt_addstring(s->pktout, "env");\r
+           ssh2_pkt_addbool(s->pktout, 1);            /* want reply */\r
+           ssh2_pkt_addstring_start(s->pktout);\r
+           ssh2_pkt_addstring_data(s->pktout, var, varend-var);\r
+           ssh2_pkt_addstring(s->pktout, val);\r
+           ssh2_pkt_send(ssh, s->pktout);\r
+\r
+           s->num_env++;\r
+       }\r
+\r
+       logeventf(ssh, "Sent %d environment variables", s->num_env);\r
+\r
+       s->env_ok = 0;\r
+       s->env_left = s->num_env;\r
+\r
+       while (s->env_left > 0) {\r
+           crWaitUntilV(pktin);\r
+\r
+           if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {\r
+               if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {\r
+                   bombout(("Unexpected response to environment request:"\r
+                            " packet type %d", pktin->type));\r
+                   crStopV;\r
+               }\r
+           } else {\r
+               s->env_ok++;\r
+           }\r
+\r
+           s->env_left--;\r
+       }\r
+\r
+       if (s->env_ok == s->num_env) {\r
+           logevent("All environment variables successfully set");\r
+       } else if (s->env_ok == 0) {\r
+           logevent("All environment variables refused");\r
+           c_write_str(ssh, "Server refused to set environment variables\r\n");\r
+       } else {\r
+           logeventf(ssh, "%d environment variables refused",\r
+                     s->num_env - s->env_ok);\r
+           c_write_str(ssh, "Server refused to set all environment variables\r\n");\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Start a shell or a remote command. We may have to attempt\r
+     * this twice if the config data has provided a second choice\r
+     * of command.\r
+     */\r
+    if (ssh->mainchan && !ssh->ncmode) while (1) {\r
+       int subsys;\r
+       char *cmd;\r
+\r
+       if (ssh->fallback_cmd) {\r
+           subsys = ssh->cfg.ssh_subsys2;\r
+           cmd = ssh->cfg.remote_cmd_ptr2;\r
+       } else {\r
+           subsys = ssh->cfg.ssh_subsys;\r
+           cmd = ssh->cfg.remote_cmd_ptr;\r
+           if (!cmd) cmd = ssh->cfg.remote_cmd;\r
+       }\r
+\r
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); /* recipient channel */\r
+       if (subsys) {\r
+           ssh2_pkt_addstring(s->pktout, "subsystem");\r
+           ssh2_pkt_addbool(s->pktout, 1);            /* want reply */\r
+           ssh2_pkt_addstring(s->pktout, cmd);\r
+       } else if (*cmd) {\r
+           ssh2_pkt_addstring(s->pktout, "exec");\r
+           ssh2_pkt_addbool(s->pktout, 1);            /* want reply */\r
+           ssh2_pkt_addstring(s->pktout, cmd);\r
+       } else {\r
+           ssh2_pkt_addstring(s->pktout, "shell");\r
+           ssh2_pkt_addbool(s->pktout, 1);            /* want reply */\r
+       }\r
+       ssh2_pkt_send(ssh, s->pktout);\r
+\r
+       crWaitUntilV(pktin);\r
+\r
+       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {\r
+           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {\r
+               bombout(("Unexpected response to shell/command request:"\r
+                        " packet type %d", pktin->type));\r
+               crStopV;\r
+           }\r
+           /*\r
+            * We failed to start the command. If this is the\r
+            * fallback command, we really are finished; if it's\r
+            * not, and if the fallback command exists, try falling\r
+            * back to it before complaining.\r
+            */\r
+           if (!ssh->fallback_cmd && ssh->cfg.remote_cmd_ptr2 != NULL) {\r
+               logevent("Primary command failed; attempting fallback");\r
+               ssh->fallback_cmd = TRUE;\r
+               continue;\r
+           }\r
+           bombout(("Server refused to start a shell/command"));\r
+           crStopV;\r
+       } else {\r
+           logevent("Started a shell/command");\r
+       }\r
+       break;\r
+    }\r
+\r
+    ssh->state = SSH_STATE_SESSION;\r
+    if (ssh->size_needed)\r
+       ssh_size(ssh, ssh->term_width, ssh->term_height);\r
+    if (ssh->eof_needed)\r
+       ssh_special(ssh, TS_EOF);\r
+\r
+    /*\r
+     * All the initial channel requests are done, so install the default\r
+     * failure handler.\r
+     */\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure;\r
+\r
+    /*\r
+     * Transfer data!\r
+     */\r
+    if (ssh->ldisc)\r
+       ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */\r
+    if (ssh->mainchan)\r
+       ssh->send_ok = 1;\r
+    while (1) {\r
+       crReturnV;\r
+       s->try_send = FALSE;\r
+       if (pktin) {\r
+\r
+           /*\r
+            * _All_ the connection-layer packets we expect to\r
+            * receive are now handled by the dispatch table.\r
+            * Anything that reaches here must be bogus.\r
+            */\r
+\r
+           bombout(("Strange packet received: type %d", pktin->type));\r
+           crStopV;\r
+       } else if (ssh->mainchan) {\r
+           /*\r
+            * We have spare data. Add it to the channel buffer.\r
+            */\r
+           ssh2_add_channel_data(ssh->mainchan, (char *)in, inlen);\r
+           s->try_send = TRUE;\r
+       }\r
+       if (s->try_send) {\r
+           int i;\r
+           struct ssh_channel *c;\r
+           /*\r
+            * Try to send data on all channels if we can.\r
+            */\r
+           for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)\r
+               ssh2_try_send_and_unthrottle(ssh, c);\r
+       }\r
+    }\r
+\r
+    crFinishV;\r
+}\r
+\r
+/*\r
+ * Handlers for SSH-2 messages that might arrive at any moment.\r
+ */\r
+static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* log reason code in disconnect message */\r
+    char *buf, *msg;\r
+    int reason, msglen;\r
+\r
+    reason = ssh_pkt_getuint32(pktin);\r
+    ssh_pkt_getstring(pktin, &msg, &msglen);\r
+\r
+    if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {\r
+       buf = dupprintf("Received disconnect message (%s)",\r
+                       ssh2_disconnect_reasons[reason]);\r
+    } else {\r
+       buf = dupprintf("Received disconnect message (unknown"\r
+                       " type %d)", reason);\r
+    }\r
+    logevent(buf);\r
+    sfree(buf);\r
+    buf = dupprintf("Disconnection message text: %.*s",\r
+                   msglen, msg);\r
+    logevent(buf);\r
+    bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"",\r
+            reason,\r
+            (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?\r
+            ssh2_disconnect_reasons[reason] : "unknown",\r
+            msglen, msg));\r
+    sfree(buf);\r
+}\r
+\r
+static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin)\r
+{\r
+    /* log the debug message */\r
+    char *msg;\r
+    int msglen;\r
+\r
+    /* XXX maybe we should actually take notice of the return value */\r
+    ssh2_pkt_getbool(pktin);\r
+    ssh_pkt_getstring(pktin, &msg, &msglen);\r
+\r
+    logeventf(ssh, "Remote debug message: %.*s", msglen, msg);\r
+}\r
+\r
+static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin)\r
+{\r
+    struct Packet *pktout;\r
+    pktout = ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);\r
+    ssh2_pkt_adduint32(pktout, pktin->sequence);\r
+    /*\r
+     * UNIMPLEMENTED messages MUST appear in the same order as the\r
+     * messages they respond to. Hence, never queue them.\r
+     */\r
+    ssh2_pkt_send_noqueue(ssh, pktout);\r
+}\r
+\r
+/*\r
+ * Handle the top-level SSH-2 protocol.\r
+ */\r
+static void ssh2_protocol_setup(Ssh ssh)\r
+{\r
+    int i;\r
+\r
+    /*\r
+     * Most messages cause SSH2_MSG_UNIMPLEMENTED.\r
+     */\r
+    for (i = 0; i < 256; i++)\r
+       ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented;\r
+\r
+    /*\r
+     * Any message we actually understand, we set to NULL so that\r
+     * the coroutines will get it.\r
+     */\r
+    ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_KEXINIT] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = NULL;\r
+    /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = NULL; duplicate case value */\r
+    /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = NULL; duplicate case value */\r
+    ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = NULL;\r
+    /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = NULL; duplicate case value */\r
+    /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = NULL; duplicate case value */\r
+    ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = NULL;\r
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = NULL;\r
+\r
+    /*\r
+     * These special message types we install handlers for.\r
+     */\r
+    ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect;\r
+    ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */\r
+    ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug;\r
+}\r
+\r
+static void ssh2_timer(void *ctx, long now)\r
+{\r
+    Ssh ssh = (Ssh)ctx;\r
+\r
+    if (ssh->state == SSH_STATE_CLOSED)\r
+       return;\r
+\r
+    if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 &&\r
+       now - ssh->next_rekey >= 0) {\r
+       do_ssh2_transport(ssh, "timeout", -1, NULL);\r
+    }\r
+}\r
+\r
+static void ssh2_protocol(Ssh ssh, void *vin, int inlen,\r
+                         struct Packet *pktin)\r
+{\r
+    unsigned char *in = (unsigned char *)vin;\r
+    if (ssh->state == SSH_STATE_CLOSED)\r
+       return;\r
+\r
+    if (pktin) {\r
+       ssh->incoming_data_size += pktin->encrypted_len;\r
+       if (!ssh->kex_in_progress &&\r
+           ssh->max_data_size != 0 &&\r
+           ssh->incoming_data_size > ssh->max_data_size)\r
+           do_ssh2_transport(ssh, "too much data received", -1, NULL);\r
+    }\r
+\r
+    if (pktin && ssh->packet_dispatch[pktin->type]) {\r
+       ssh->packet_dispatch[pktin->type](ssh, pktin);\r
+       return;\r
+    }\r
+\r
+    if (!ssh->protocol_initial_phase_done ||\r
+       (pktin && pktin->type >= 20 && pktin->type < 50)) {\r
+       if (do_ssh2_transport(ssh, in, inlen, pktin) &&\r
+           !ssh->protocol_initial_phase_done) {\r
+           ssh->protocol_initial_phase_done = TRUE;\r
+           /*\r
+            * Allow authconn to initialise itself.\r
+            */\r
+           do_ssh2_authconn(ssh, NULL, 0, NULL);\r
+       }\r
+    } else {\r
+       do_ssh2_authconn(ssh, in, inlen, pktin);\r
+    }\r
+}\r
+\r
+/*\r
+ * Called to set up the connection.\r
+ *\r
+ * Returns an error message, or NULL on success.\r
+ */\r
+static const char *ssh_init(void *frontend_handle, void **backend_handle,\r
+                           Config *cfg,\r
+                           char *host, int port, char **realhost, int nodelay,\r
+                           int keepalive)\r
+{\r
+    const char *p;\r
+    Ssh ssh;\r
+\r
+    ssh = snew(struct ssh_tag);\r
+    ssh->cfg = *cfg;                  /* STRUCTURE COPY */\r
+    ssh->version = 0;                 /* when not ready yet */\r
+    ssh->s = NULL;\r
+    ssh->cipher = NULL;\r
+    ssh->v1_cipher_ctx = NULL;\r
+    ssh->crcda_ctx = NULL;\r
+    ssh->cscipher = NULL;\r
+    ssh->cs_cipher_ctx = NULL;\r
+    ssh->sccipher = NULL;\r
+    ssh->sc_cipher_ctx = NULL;\r
+    ssh->csmac = NULL;\r
+    ssh->cs_mac_ctx = NULL;\r
+    ssh->scmac = NULL;\r
+    ssh->sc_mac_ctx = NULL;\r
+    ssh->cscomp = NULL;\r
+    ssh->cs_comp_ctx = NULL;\r
+    ssh->sccomp = NULL;\r
+    ssh->sc_comp_ctx = NULL;\r
+    ssh->kex = NULL;\r
+    ssh->kex_ctx = NULL;\r
+    ssh->hostkey = NULL;\r
+    ssh->exitcode = -1;\r
+    ssh->close_expected = FALSE;\r
+    ssh->clean_exit = FALSE;\r
+    ssh->state = SSH_STATE_PREPACKET;\r
+    ssh->size_needed = FALSE;\r
+    ssh->eof_needed = FALSE;\r
+    ssh->ldisc = NULL;\r
+    ssh->logctx = NULL;\r
+    ssh->deferred_send_data = NULL;\r
+    ssh->deferred_len = 0;\r
+    ssh->deferred_size = 0;\r
+    ssh->fallback_cmd = 0;\r
+    ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;\r
+    ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;\r
+    ssh->x11disp = NULL;\r
+    ssh->v1_compressing = FALSE;\r
+    ssh->v2_outgoing_sequence = 0;\r
+    ssh->ssh1_rdpkt_crstate = 0;\r
+    ssh->ssh2_rdpkt_crstate = 0;\r
+    ssh->do_ssh_init_crstate = 0;\r
+    ssh->ssh_gotdata_crstate = 0;\r
+    ssh->do_ssh1_connection_crstate = 0;\r
+    ssh->do_ssh1_login_crstate = 0;\r
+    ssh->do_ssh2_transport_crstate = 0;\r
+    ssh->do_ssh2_authconn_crstate = 0;\r
+    ssh->do_ssh_init_state = NULL;\r
+    ssh->do_ssh1_login_state = NULL;\r
+    ssh->do_ssh2_transport_state = NULL;\r
+    ssh->do_ssh2_authconn_state = NULL;\r
+    ssh->v_c = NULL;\r
+    ssh->v_s = NULL;\r
+    ssh->mainchan = NULL;\r
+    ssh->throttled_all = 0;\r
+    ssh->v1_stdout_throttling = 0;\r
+    ssh->queue = NULL;\r
+    ssh->queuelen = ssh->queuesize = 0;\r
+    ssh->queueing = FALSE;\r
+    ssh->qhead = ssh->qtail = NULL;\r
+    ssh->deferred_rekey_reason = NULL;\r
+    bufchain_init(&ssh->queued_incoming_data);\r
+    ssh->frozen = FALSE;\r
+\r
+    *backend_handle = ssh;\r
+\r
+#ifdef MSCRYPTOAPI\r
+    if (crypto_startup() == 0)\r
+       return "Microsoft high encryption pack not installed!";\r
+#endif\r
+\r
+    ssh->frontend = frontend_handle;\r
+    ssh->term_width = ssh->cfg.width;\r
+    ssh->term_height = ssh->cfg.height;\r
+\r
+    ssh->channels = NULL;\r
+    ssh->rportfwds = NULL;\r
+    ssh->portfwds = NULL;\r
+\r
+    ssh->send_ok = 0;\r
+    ssh->editing = 0;\r
+    ssh->echoing = 0;\r
+    ssh->conn_throttle_count = 0;\r
+    ssh->overall_bufsize = 0;\r
+    ssh->fallback_cmd = 0;\r
+\r
+    ssh->protocol = NULL;\r
+\r
+    ssh->protocol_initial_phase_done = FALSE;\r
+\r
+    ssh->pinger = NULL;\r
+\r
+    ssh->incoming_data_size = ssh->outgoing_data_size =\r
+       ssh->deferred_data_size = 0L;\r
+    ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);\r
+    ssh->kex_in_progress = FALSE;\r
+\r
+#ifndef NO_GSSAPI\r
+    ssh->gsslibs = NULL;\r
+#endif\r
+\r
+    p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);\r
+    if (p != NULL)\r
+       return p;\r
+\r
+    random_ref();\r
+\r
+    return NULL;\r
+}\r
+\r
+static void ssh_free(void *handle)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    struct ssh_channel *c;\r
+    struct ssh_rportfwd *pf;\r
+\r
+    if (ssh->v1_cipher_ctx)\r
+       ssh->cipher->free_context(ssh->v1_cipher_ctx);\r
+    if (ssh->cs_cipher_ctx)\r
+       ssh->cscipher->free_context(ssh->cs_cipher_ctx);\r
+    if (ssh->sc_cipher_ctx)\r
+       ssh->sccipher->free_context(ssh->sc_cipher_ctx);\r
+    if (ssh->cs_mac_ctx)\r
+       ssh->csmac->free_context(ssh->cs_mac_ctx);\r
+    if (ssh->sc_mac_ctx)\r
+       ssh->scmac->free_context(ssh->sc_mac_ctx);\r
+    if (ssh->cs_comp_ctx) {\r
+       if (ssh->cscomp)\r
+           ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);\r
+       else\r
+           zlib_compress_cleanup(ssh->cs_comp_ctx);\r
+    }\r
+    if (ssh->sc_comp_ctx) {\r
+       if (ssh->sccomp)\r
+           ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx);\r
+       else\r
+           zlib_decompress_cleanup(ssh->sc_comp_ctx);\r
+    }\r
+    if (ssh->kex_ctx)\r
+       dh_cleanup(ssh->kex_ctx);\r
+    sfree(ssh->savedhost);\r
+\r
+    while (ssh->queuelen-- > 0)\r
+       ssh_free_packet(ssh->queue[ssh->queuelen]);\r
+    sfree(ssh->queue);\r
+\r
+    while (ssh->qhead) {\r
+       struct queued_handler *qh = ssh->qhead;\r
+       ssh->qhead = qh->next;\r
+       sfree(ssh->qhead);\r
+    }\r
+    ssh->qhead = ssh->qtail = NULL;\r
+\r
+    if (ssh->channels) {\r
+       while ((c = delpos234(ssh->channels, 0)) != NULL) {\r
+           switch (c->type) {\r
+             case CHAN_X11:\r
+               if (c->u.x11.s != NULL)\r
+                   x11_close(c->u.x11.s);\r
+               break;\r
+             case CHAN_SOCKDATA:\r
+             case CHAN_SOCKDATA_DORMANT:\r
+               if (c->u.pfd.s != NULL)\r
+                   pfd_close(c->u.pfd.s);\r
+               break;\r
+           }\r
+           sfree(c);\r
+       }\r
+       freetree234(ssh->channels);\r
+       ssh->channels = NULL;\r
+    }\r
+\r
+    if (ssh->rportfwds) {\r
+       while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)\r
+           free_rportfwd(pf);\r
+       freetree234(ssh->rportfwds);\r
+       ssh->rportfwds = NULL;\r
+    }\r
+    sfree(ssh->deferred_send_data);\r
+    if (ssh->x11disp)\r
+       x11_free_display(ssh->x11disp);\r
+    sfree(ssh->do_ssh_init_state);\r
+    sfree(ssh->do_ssh1_login_state);\r
+    sfree(ssh->do_ssh2_transport_state);\r
+    sfree(ssh->do_ssh2_authconn_state);\r
+    sfree(ssh->v_c);\r
+    sfree(ssh->v_s);\r
+    sfree(ssh->fullhostname);\r
+    if (ssh->crcda_ctx) {\r
+       crcda_free_context(ssh->crcda_ctx);\r
+       ssh->crcda_ctx = NULL;\r
+    }\r
+    if (ssh->s)\r
+       ssh_do_close(ssh, TRUE);\r
+    expire_timer_context(ssh);\r
+    if (ssh->pinger)\r
+       pinger_free(ssh->pinger);\r
+    bufchain_clear(&ssh->queued_incoming_data);\r
+#ifndef NO_GSSAPI\r
+    if (ssh->gsslibs)\r
+       ssh_gss_cleanup(ssh->gsslibs);\r
+#endif\r
+    sfree(ssh);\r
+\r
+    random_unref();\r
+}\r
+\r
+/*\r
+ * Reconfigure the SSH backend.\r
+ */\r
+static void ssh_reconfig(void *handle, Config *cfg)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    char *rekeying = NULL, rekey_mandatory = FALSE;\r
+    unsigned long old_max_data_size;\r
+\r
+    pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);\r
+    if (ssh->portfwds)\r
+       ssh_setup_portfwd(ssh, cfg);\r
+\r
+    if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time &&\r
+       cfg->ssh_rekey_time != 0) {\r
+       long new_next = ssh->last_rekey + cfg->ssh_rekey_time*60*TICKSPERSEC;\r
+       long now = GETTICKCOUNT();\r
+\r
+       if (new_next - now < 0) {\r
+           rekeying = "timeout shortened";\r
+       } else {\r
+           ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh);\r
+       }\r
+    }\r
+\r
+    old_max_data_size = ssh->max_data_size;\r
+    ssh->max_data_size = parse_blocksize(cfg->ssh_rekey_data);\r
+    if (old_max_data_size != ssh->max_data_size &&\r
+       ssh->max_data_size != 0) {\r
+       if (ssh->outgoing_data_size > ssh->max_data_size ||\r
+           ssh->incoming_data_size > ssh->max_data_size)\r
+           rekeying = "data limit lowered";\r
+    }\r
+\r
+    if (ssh->cfg.compression != cfg->compression) {\r
+       rekeying = "compression setting changed";\r
+       rekey_mandatory = TRUE;\r
+    }\r
+\r
+    if (ssh->cfg.ssh2_des_cbc != cfg->ssh2_des_cbc ||\r
+       memcmp(ssh->cfg.ssh_cipherlist, cfg->ssh_cipherlist,\r
+              sizeof(ssh->cfg.ssh_cipherlist))) {\r
+       rekeying = "cipher settings changed";\r
+       rekey_mandatory = TRUE;\r
+    }\r
+\r
+    ssh->cfg = *cfg;                  /* STRUCTURE COPY */\r
+\r
+    if (rekeying) {\r
+       if (!ssh->kex_in_progress) {\r
+           do_ssh2_transport(ssh, rekeying, -1, NULL);\r
+       } else if (rekey_mandatory) {\r
+           ssh->deferred_rekey_reason = rekeying;\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Called to send data down the SSH connection.\r
+ */\r
+static int ssh_send(void *handle, char *buf, int len)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+\r
+    if (ssh == NULL || ssh->s == NULL || ssh->protocol == NULL)\r
+       return 0;\r
+\r
+    ssh->protocol(ssh, (unsigned char *)buf, len, 0);\r
+\r
+    return ssh_sendbuffer(ssh);\r
+}\r
+\r
+/*\r
+ * Called to query the current amount of buffered stdin data.\r
+ */\r
+static int ssh_sendbuffer(void *handle)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    int override_value;\r
+\r
+    if (ssh == NULL || ssh->s == NULL || ssh->protocol == NULL)\r
+       return 0;\r
+\r
+    /*\r
+     * If the SSH socket itself has backed up, add the total backup\r
+     * size on that to any individual buffer on the stdin channel.\r
+     */\r
+    override_value = 0;\r
+    if (ssh->throttled_all)\r
+       override_value = ssh->overall_bufsize;\r
+\r
+    if (ssh->version == 1) {\r
+       return override_value;\r
+    } else if (ssh->version == 2) {\r
+       if (!ssh->mainchan || ssh->mainchan->closes > 0)\r
+           return override_value;\r
+       else\r
+           return (override_value +\r
+                   bufchain_size(&ssh->mainchan->v.v2.outbuffer));\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window from SSH's POV.\r
+ */\r
+static void ssh_size(void *handle, int width, int height)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    struct Packet *pktout;\r
+\r
+    ssh->term_width = width;\r
+    ssh->term_height = height;\r
+\r
+    switch (ssh->state) {\r
+      case SSH_STATE_BEFORE_SIZE:\r
+      case SSH_STATE_PREPACKET:\r
+      case SSH_STATE_CLOSED:\r
+       break;                         /* do nothing */\r
+      case SSH_STATE_INTERMED:\r
+       ssh->size_needed = TRUE;       /* buffer for later */\r
+       break;\r
+      case SSH_STATE_SESSION:\r
+       if (!ssh->cfg.nopty) {\r
+           if (ssh->version == 1) {\r
+               send_packet(ssh, SSH1_CMSG_WINDOW_SIZE,\r
+                           PKT_INT, ssh->term_height,\r
+                           PKT_INT, ssh->term_width,\r
+                           PKT_INT, 0, PKT_INT, 0, PKT_END);\r
+           } else if (ssh->mainchan) {\r
+               pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+               ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);\r
+               ssh2_pkt_addstring(pktout, "window-change");\r
+               ssh2_pkt_addbool(pktout, 0);\r
+               ssh2_pkt_adduint32(pktout, ssh->term_width);\r
+               ssh2_pkt_adduint32(pktout, ssh->term_height);\r
+               ssh2_pkt_adduint32(pktout, 0);\r
+               ssh2_pkt_adduint32(pktout, 0);\r
+               ssh2_pkt_send(ssh, pktout);\r
+           }\r
+       }\r
+       break;\r
+    }\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *ssh_get_specials(void *handle)\r
+{\r
+    static const struct telnet_special ssh1_ignore_special[] = {\r
+       {"IGNORE message", TS_NOP}\r
+    };\r
+    static const struct telnet_special ssh2_ignore_special[] = {\r
+       {"IGNORE message", TS_NOP},\r
+    };\r
+    static const struct telnet_special ssh2_rekey_special[] = {\r
+       {"Repeat key exchange", TS_REKEY},\r
+    };\r
+    static const struct telnet_special ssh2_session_specials[] = {\r
+       {NULL, TS_SEP},\r
+       {"Break", TS_BRK},\r
+       /* These are the signal names defined by RFC 4254.\r
+        * They include all the ISO C signals, but are a subset of the POSIX\r
+        * required signals. */\r
+       {"SIGINT (Interrupt)", TS_SIGINT},\r
+       {"SIGTERM (Terminate)", TS_SIGTERM},\r
+       {"SIGKILL (Kill)", TS_SIGKILL},\r
+       {"SIGQUIT (Quit)", TS_SIGQUIT},\r
+       {"SIGHUP (Hangup)", TS_SIGHUP},\r
+       {"More signals", TS_SUBMENU},\r
+         {"SIGABRT", TS_SIGABRT}, {"SIGALRM", TS_SIGALRM},\r
+         {"SIGFPE",  TS_SIGFPE},  {"SIGILL",  TS_SIGILL},\r
+         {"SIGPIPE", TS_SIGPIPE}, {"SIGSEGV", TS_SIGSEGV},\r
+         {"SIGUSR1", TS_SIGUSR1}, {"SIGUSR2", TS_SIGUSR2},\r
+       {NULL, TS_EXITMENU}\r
+    };\r
+    static const struct telnet_special specials_end[] = {\r
+       {NULL, TS_EXITMENU}\r
+    };\r
+    /* XXX review this length for any changes: */\r
+    static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) +\r
+                                             lenof(ssh2_rekey_special) +\r
+                                             lenof(ssh2_session_specials) +\r
+                                             lenof(specials_end)];\r
+    Ssh ssh = (Ssh) handle;\r
+    int i = 0;\r
+#define ADD_SPECIALS(name) \\r
+    do { \\r
+       assert((i + lenof(name)) <= lenof(ssh_specials)); \\r
+       memcpy(&ssh_specials[i], name, sizeof name); \\r
+       i += lenof(name); \\r
+    } while(0)\r
+\r
+    if (ssh->version == 1) {\r
+       /* Don't bother offering IGNORE if we've decided the remote\r
+        * won't cope with it, since we wouldn't bother sending it if\r
+        * asked anyway. */\r
+       if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))\r
+           ADD_SPECIALS(ssh1_ignore_special);\r
+    } else if (ssh->version == 2) {\r
+       if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE))\r
+           ADD_SPECIALS(ssh2_ignore_special);\r
+       if (!(ssh->remote_bugs & BUG_SSH2_REKEY))\r
+           ADD_SPECIALS(ssh2_rekey_special);\r
+       if (ssh->mainchan)\r
+           ADD_SPECIALS(ssh2_session_specials);\r
+    } /* else we're not ready yet */\r
+\r
+    if (i) {\r
+       ADD_SPECIALS(specials_end);\r
+       return ssh_specials;\r
+    } else {\r
+       return NULL;\r
+    }\r
+#undef ADD_SPECIALS\r
+}\r
+\r
+/*\r
+ * Send special codes. TS_EOF is useful for `plink', so you\r
+ * can send an EOF and collect resulting output (e.g. `plink\r
+ * hostname sort').\r
+ */\r
+static void ssh_special(void *handle, Telnet_Special code)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    struct Packet *pktout;\r
+\r
+    if (code == TS_EOF) {\r
+       if (ssh->state != SSH_STATE_SESSION) {\r
+           /*\r
+            * Buffer the EOF in case we are pre-SESSION, so we can\r
+            * send it as soon as we reach SESSION.\r
+            */\r
+           if (code == TS_EOF)\r
+               ssh->eof_needed = TRUE;\r
+           return;\r
+       }\r
+       if (ssh->version == 1) {\r
+           send_packet(ssh, SSH1_CMSG_EOF, PKT_END);\r
+       } else if (ssh->mainchan) {\r
+           struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);\r
+           ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);\r
+           ssh2_pkt_send(ssh, pktout);\r
+            ssh->send_ok = 0;          /* now stop trying to read from stdin */\r
+       }\r
+       logevent("Sent EOF message");\r
+    } else if (code == TS_PING || code == TS_NOP) {\r
+       if (ssh->state == SSH_STATE_CLOSED\r
+           || ssh->state == SSH_STATE_PREPACKET) return;\r
+       if (ssh->version == 1) {\r
+           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))\r
+               send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);\r
+       } else {\r
+           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {\r
+               pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);\r
+               ssh2_pkt_addstring_start(pktout);\r
+               ssh2_pkt_send_noqueue(ssh, pktout);\r
+           }\r
+       }\r
+    } else if (code == TS_REKEY) {\r
+       if (!ssh->kex_in_progress && ssh->version == 2) {\r
+           do_ssh2_transport(ssh, "at user request", -1, NULL);\r
+       }\r
+    } else if (code == TS_BRK) {\r
+       if (ssh->state == SSH_STATE_CLOSED\r
+           || ssh->state == SSH_STATE_PREPACKET) return;\r
+       if (ssh->version == 1) {\r
+           logevent("Unable to send BREAK signal in SSH-1");\r
+       } else if (ssh->mainchan) {\r
+           pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+           ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);\r
+           ssh2_pkt_addstring(pktout, "break");\r
+           ssh2_pkt_addbool(pktout, 0);\r
+           ssh2_pkt_adduint32(pktout, 0);   /* default break length */\r
+           ssh2_pkt_send(ssh, pktout);\r
+       }\r
+    } else {\r
+       /* Is is a POSIX signal? */\r
+       char *signame = NULL;\r
+       if (code == TS_SIGABRT) signame = "ABRT";\r
+       if (code == TS_SIGALRM) signame = "ALRM";\r
+       if (code == TS_SIGFPE)  signame = "FPE";\r
+       if (code == TS_SIGHUP)  signame = "HUP";\r
+       if (code == TS_SIGILL)  signame = "ILL";\r
+       if (code == TS_SIGINT)  signame = "INT";\r
+       if (code == TS_SIGKILL) signame = "KILL";\r
+       if (code == TS_SIGPIPE) signame = "PIPE";\r
+       if (code == TS_SIGQUIT) signame = "QUIT";\r
+       if (code == TS_SIGSEGV) signame = "SEGV";\r
+       if (code == TS_SIGTERM) signame = "TERM";\r
+       if (code == TS_SIGUSR1) signame = "USR1";\r
+       if (code == TS_SIGUSR2) signame = "USR2";\r
+       /* The SSH-2 protocol does in principle support arbitrary named\r
+        * signals, including signame@domain, but we don't support those. */\r
+       if (signame) {\r
+           /* It's a signal. */\r
+           if (ssh->version == 2 && ssh->mainchan) {\r
+               pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);\r
+               ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);\r
+               ssh2_pkt_addstring(pktout, "signal");\r
+               ssh2_pkt_addbool(pktout, 0);\r
+               ssh2_pkt_addstring(pktout, signame);\r
+               ssh2_pkt_send(ssh, pktout);\r
+               logeventf(ssh, "Sent signal SIG%s", signame);\r
+           }\r
+       } else {\r
+           /* Never heard of it. Do nothing */\r
+       }\r
+    }\r
+}\r
+\r
+void *new_sock_channel(void *handle, Socket s)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    struct ssh_channel *c;\r
+    c = snew(struct ssh_channel);\r
+\r
+    c->ssh = ssh;\r
+    ssh2_channel_init(c);\r
+    c->halfopen = TRUE;\r
+    c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */\r
+    c->u.pfd.s = s;\r
+    add234(ssh->channels, c);\r
+    return c;\r
+}\r
+\r
+/*\r
+ * This is called when stdout/stderr (the entity to which\r
+ * from_backend sends data) manages to clear some backlog.\r
+ */\r
+static void ssh_unthrottle(void *handle, int bufsize)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    int buflimit;\r
+\r
+    if (ssh->version == 1) {\r
+       if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {\r
+           ssh->v1_stdout_throttling = 0;\r
+           ssh_throttle_conn(ssh, -1);\r
+       }\r
+    } else {\r
+       if (ssh->mainchan) {\r
+           ssh2_set_window(ssh->mainchan,\r
+                           bufsize < ssh->mainchan->v.v2.locmaxwin ?\r
+                           ssh->mainchan->v.v2.locmaxwin - bufsize : 0);\r
+           if (ssh->cfg.ssh_simple)\r
+               buflimit = 0;\r
+           else\r
+               buflimit = ssh->mainchan->v.v2.locmaxwin;\r
+           if (ssh->mainchan->throttling_conn && bufsize <= buflimit) {\r
+               ssh->mainchan->throttling_conn = 0;\r
+               ssh_throttle_conn(ssh, -1);\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+void ssh_send_port_open(void *channel, char *hostname, int port, char *org)\r
+{\r
+    struct ssh_channel *c = (struct ssh_channel *)channel;\r
+    Ssh ssh = c->ssh;\r
+    struct Packet *pktout;\r
+\r
+    logeventf(ssh, "Opening forwarded connection to %s:%d", hostname, port);\r
+\r
+    if (ssh->version == 1) {\r
+       send_packet(ssh, SSH1_MSG_PORT_OPEN,\r
+                   PKT_INT, c->localid,\r
+                   PKT_STR, hostname,\r
+                   PKT_INT, port,\r
+                   /* PKT_STR, <org:orgport>, */\r
+                   PKT_END);\r
+    } else {\r
+       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);\r
+       ssh2_pkt_addstring(pktout, "direct-tcpip");\r
+       ssh2_pkt_adduint32(pktout, c->localid);\r
+       ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */\r
+       ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */\r
+       ssh2_pkt_addstring(pktout, hostname);\r
+       ssh2_pkt_adduint32(pktout, port);\r
+       /*\r
+        * We make up values for the originator data; partly it's\r
+        * too much hassle to keep track, and partly I'm not\r
+        * convinced the server should be told details like that\r
+        * about my local network configuration.\r
+        * The "originator IP address" is syntactically a numeric\r
+        * IP address, and some servers (e.g., Tectia) get upset\r
+        * if it doesn't match this syntax.\r
+        */\r
+       ssh2_pkt_addstring(pktout, "0.0.0.0");\r
+       ssh2_pkt_adduint32(pktout, 0);\r
+       ssh2_pkt_send(ssh, pktout);\r
+    }\r
+}\r
+\r
+static int ssh_connected(void *handle)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    return ssh->s != NULL;\r
+}\r
+\r
+static int ssh_sendok(void *handle)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    return ssh->send_ok;\r
+}\r
+\r
+static int ssh_ldisc(void *handle, int option)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    if (option == LD_ECHO)\r
+       return ssh->echoing;\r
+    if (option == LD_EDIT)\r
+       return ssh->editing;\r
+    return FALSE;\r
+}\r
+\r
+static void ssh_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    ssh->ldisc = ldisc;\r
+}\r
+\r
+static void ssh_provide_logctx(void *handle, void *logctx)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    ssh->logctx = logctx;\r
+}\r
+\r
+static int ssh_return_exitcode(void *handle)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    if (ssh->s != NULL)\r
+        return -1;\r
+    else\r
+        return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX);\r
+}\r
+\r
+/*\r
+ * cfg_info for SSH is the currently running version of the\r
+ * protocol. (1 for 1; 2 for 2; 0 for not-decided-yet.)\r
+ */\r
+static int ssh_cfg_info(void *handle)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    return ssh->version;\r
+}\r
+\r
+/*\r
+ * Gross hack: pscp will try to start SFTP but fall back to scp1 if\r
+ * that fails. This variable is the means by which scp.c can reach\r
+ * into the SSH code and find out which one it got.\r
+ */\r
+extern int ssh_fallback_cmd(void *handle)\r
+{\r
+    Ssh ssh = (Ssh) handle;\r
+    return ssh->fallback_cmd;\r
+}\r
+\r
+Backend ssh_backend = {\r
+    ssh_init,\r
+    ssh_free,\r
+    ssh_reconfig,\r
+    ssh_send,\r
+    ssh_sendbuffer,\r
+    ssh_size,\r
+    ssh_special,\r
+    ssh_get_specials,\r
+    ssh_connected,\r
+    ssh_return_exitcode,\r
+    ssh_sendok,\r
+    ssh_ldisc,\r
+    ssh_provide_ldisc,\r
+    ssh_provide_logctx,\r
+    ssh_unthrottle,\r
+    ssh_cfg_info,\r
+    "ssh",\r
+    PROT_SSH,\r
+    22\r
+};\r
diff --git a/putty/SSH.H b/putty/SSH.H
new file mode 100644 (file)
index 0000000..605b608
--- /dev/null
@@ -0,0 +1,600 @@
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+#include "puttymem.h"\r
+#include "tree234.h"\r
+#include "network.h"\r
+#include "int64.h"\r
+#include "misc.h"\r
+\r
+struct ssh_channel;\r
+\r
+extern void sshfwd_close(struct ssh_channel *c);\r
+extern int sshfwd_write(struct ssh_channel *c, char *, int);\r
+extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);\r
+\r
+/*\r
+ * Useful thing.\r
+ */\r
+#ifndef lenof\r
+#define lenof(x) ( (sizeof((x))) / (sizeof(*(x))))\r
+#endif\r
+\r
+#define SSH_CIPHER_IDEA                1\r
+#define SSH_CIPHER_DES         2\r
+#define SSH_CIPHER_3DES                3\r
+#define SSH_CIPHER_BLOWFISH    6\r
+\r
+#ifdef MSCRYPTOAPI\r
+#define APIEXTRA 8\r
+#else\r
+#define APIEXTRA 0\r
+#endif\r
+\r
+#ifndef BIGNUM_INTERNAL\r
+typedef void *Bignum;\r
+#endif\r
+\r
+struct RSAKey {\r
+    int bits;\r
+    int bytes;\r
+#ifdef MSCRYPTOAPI\r
+    unsigned long exponent;\r
+    unsigned char *modulus;\r
+#else\r
+    Bignum modulus;\r
+    Bignum exponent;\r
+    Bignum private_exponent;\r
+    Bignum p;\r
+    Bignum q;\r
+    Bignum iqmp;\r
+#endif\r
+    char *comment;\r
+};\r
+\r
+struct dss_key {\r
+    Bignum p, q, g, y, x;\r
+};\r
+\r
+int makekey(unsigned char *data, int len, struct RSAKey *result,\r
+           unsigned char **keystr, int order);\r
+int makeprivate(unsigned char *data, int len, struct RSAKey *result);\r
+int rsaencrypt(unsigned char *data, int length, struct RSAKey *key);\r
+Bignum rsadecrypt(Bignum input, struct RSAKey *key);\r
+void rsasign(unsigned char *data, int length, struct RSAKey *key);\r
+void rsasanitise(struct RSAKey *key);\r
+int rsastr_len(struct RSAKey *key);\r
+void rsastr_fmt(char *str, struct RSAKey *key);\r
+void rsa_fingerprint(char *str, int len, struct RSAKey *key);\r
+int rsa_verify(struct RSAKey *key);\r
+unsigned char *rsa_public_blob(struct RSAKey *key, int *len);\r
+int rsa_public_blob_len(void *data, int maxlen);\r
+void freersakey(struct RSAKey *key);\r
+\r
+#ifndef PUTTY_UINT32_DEFINED\r
+/* This makes assumptions about the int type. */\r
+typedef unsigned int uint32;\r
+#define PUTTY_UINT32_DEFINED\r
+#endif\r
+typedef uint32 word32;\r
+\r
+unsigned long crc32_compute(const void *s, size_t len);\r
+unsigned long crc32_update(unsigned long crc_input, const void *s, size_t len);\r
+\r
+/* SSH CRC compensation attack detector */\r
+void *crcda_make_context(void);\r
+void crcda_free_context(void *handle);\r
+int detect_attack(void *handle, unsigned char *buf, uint32 len,\r
+                 unsigned char *IV);\r
+\r
+/*\r
+ * SSH2 RSA key exchange functions\r
+ */\r
+struct ssh_hash;\r
+void *ssh_rsakex_newkey(char *data, int len);\r
+void ssh_rsakex_freekey(void *key);\r
+int ssh_rsakex_klen(void *key);\r
+void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,\r
+                        unsigned char *out, int outlen,\r
+                        void *key);\r
+\r
+typedef struct {\r
+    uint32 h[4];\r
+} MD5_Core_State;\r
+\r
+struct MD5Context {\r
+#ifdef MSCRYPTOAPI\r
+    unsigned long hHash;\r
+#else\r
+    MD5_Core_State core;\r
+    unsigned char block[64];\r
+    int blkused;\r
+    uint32 lenhi, lenlo;\r
+#endif\r
+};\r
+\r
+void MD5Init(struct MD5Context *context);\r
+void MD5Update(struct MD5Context *context, unsigned char const *buf,\r
+              unsigned len);\r
+void MD5Final(unsigned char digest[16], struct MD5Context *context);\r
+void MD5Simple(void const *p, unsigned len, unsigned char output[16]);\r
+\r
+void *hmacmd5_make_context(void);\r
+void hmacmd5_free_context(void *handle);\r
+void hmacmd5_key(void *handle, void const *key, int len);\r
+void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,\r
+                    unsigned char *hmac);\r
+\r
+typedef struct {\r
+    uint32 h[5];\r
+    unsigned char block[64];\r
+    int blkused;\r
+    uint32 lenhi, lenlo;\r
+} SHA_State;\r
+void SHA_Init(SHA_State * s);\r
+void SHA_Bytes(SHA_State * s, void *p, int len);\r
+void SHA_Final(SHA_State * s, unsigned char *output);\r
+void SHA_Simple(void *p, int len, unsigned char *output);\r
+\r
+void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,\r
+                     unsigned char *output);\r
+typedef struct {\r
+    uint32 h[8];\r
+    unsigned char block[64];\r
+    int blkused;\r
+    uint32 lenhi, lenlo;\r
+} SHA256_State;\r
+void SHA256_Init(SHA256_State * s);\r
+void SHA256_Bytes(SHA256_State * s, const void *p, int len);\r
+void SHA256_Final(SHA256_State * s, unsigned char *output);\r
+void SHA256_Simple(const void *p, int len, unsigned char *output);\r
+\r
+typedef struct {\r
+    uint64 h[8];\r
+    unsigned char block[128];\r
+    int blkused;\r
+    uint32 len[4];\r
+} SHA512_State;\r
+void SHA512_Init(SHA512_State * s);\r
+void SHA512_Bytes(SHA512_State * s, const void *p, int len);\r
+void SHA512_Final(SHA512_State * s, unsigned char *output);\r
+void SHA512_Simple(const void *p, int len, unsigned char *output);\r
+\r
+struct ssh_cipher {\r
+    void *(*make_context)(void);\r
+    void (*free_context)(void *);\r
+    void (*sesskey) (void *, unsigned char *key);      /* for SSH-1 */\r
+    void (*encrypt) (void *, unsigned char *blk, int len);\r
+    void (*decrypt) (void *, unsigned char *blk, int len);\r
+    int blksize;\r
+    char *text_name;\r
+};\r
+\r
+struct ssh2_cipher {\r
+    void *(*make_context)(void);\r
+    void (*free_context)(void *);\r
+    void (*setiv) (void *, unsigned char *key);        /* for SSH-2 */\r
+    void (*setkey) (void *, unsigned char *key);/* for SSH-2 */\r
+    void (*encrypt) (void *, unsigned char *blk, int len);\r
+    void (*decrypt) (void *, unsigned char *blk, int len);\r
+    char *name;\r
+    int blksize;\r
+    int keylen;\r
+    unsigned int flags;\r
+#define SSH_CIPHER_IS_CBC      1\r
+    char *text_name;\r
+};\r
+\r
+struct ssh2_ciphers {\r
+    int nciphers;\r
+    const struct ssh2_cipher *const *list;\r
+};\r
+\r
+struct ssh_mac {\r
+    void *(*make_context)(void);\r
+    void (*free_context)(void *);\r
+    void (*setkey) (void *, unsigned char *key);\r
+    /* whole-packet operations */\r
+    void (*generate) (void *, unsigned char *blk, int len, unsigned long seq);\r
+    int (*verify) (void *, unsigned char *blk, int len, unsigned long seq);\r
+    /* partial-packet operations */\r
+    void (*start) (void *);\r
+    void (*bytes) (void *, unsigned char const *, int);\r
+    void (*genresult) (void *, unsigned char *);\r
+    int (*verresult) (void *, unsigned char const *);\r
+    char *name;\r
+    int len;\r
+    char *text_name;\r
+};\r
+\r
+struct ssh_hash {\r
+    void *(*init)(void); /* also allocates context */\r
+    void (*bytes)(void *, void *, int);\r
+    void (*final)(void *, unsigned char *); /* also frees context */\r
+    int hlen; /* output length in bytes */\r
+    char *text_name;\r
+};   \r
+\r
+struct ssh_kex {\r
+    char *name, *groupname;\r
+    enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;\r
+    /* For DH */\r
+    const unsigned char *pdata, *gdata; /* NULL means group exchange */\r
+    int plen, glen;\r
+    const struct ssh_hash *hash;\r
+};\r
+\r
+struct ssh_kexes {\r
+    int nkexes;\r
+    const struct ssh_kex *const *list;\r
+};\r
+\r
+struct ssh_signkey {\r
+    void *(*newkey) (char *data, int len);\r
+    void (*freekey) (void *key);\r
+    char *(*fmtkey) (void *key);\r
+    unsigned char *(*public_blob) (void *key, int *len);\r
+    unsigned char *(*private_blob) (void *key, int *len);\r
+    void *(*createkey) (unsigned char *pub_blob, int pub_len,\r
+                       unsigned char *priv_blob, int priv_len);\r
+    void *(*openssh_createkey) (unsigned char **blob, int *len);\r
+    int (*openssh_fmtkey) (void *key, unsigned char *blob, int len);\r
+    int (*pubkey_bits) (void *blob, int len);\r
+    char *(*fingerprint) (void *key);\r
+    int (*verifysig) (void *key, char *sig, int siglen,\r
+                     char *data, int datalen);\r
+    unsigned char *(*sign) (void *key, char *data, int datalen,\r
+                           int *siglen);\r
+    char *name;\r
+    char *keytype;                    /* for host key cache */\r
+};\r
+\r
+struct ssh_compress {\r
+    char *name;\r
+    /* For zlib@openssh.com: if non-NULL, this name will be considered once\r
+     * userauth has completed successfully. */\r
+    char *delayed_name;\r
+    void *(*compress_init) (void);\r
+    void (*compress_cleanup) (void *);\r
+    int (*compress) (void *, unsigned char *block, int len,\r
+                    unsigned char **outblock, int *outlen);\r
+    void *(*decompress_init) (void);\r
+    void (*decompress_cleanup) (void *);\r
+    int (*decompress) (void *, unsigned char *block, int len,\r
+                      unsigned char **outblock, int *outlen);\r
+    int (*disable_compression) (void *);\r
+    char *text_name;\r
+};\r
+\r
+struct ssh2_userkey {\r
+    const struct ssh_signkey *alg;     /* the key algorithm */\r
+    void *data;                               /* the key data */\r
+    char *comment;                    /* the key comment */\r
+};\r
+\r
+/* The maximum length of any hash algorithm used in kex. (bytes) */\r
+#define SSH2_KEX_MAX_HASH_LEN (32) /* SHA-256 */\r
+\r
+extern const struct ssh_cipher ssh_3des;\r
+extern const struct ssh_cipher ssh_des;\r
+extern const struct ssh_cipher ssh_blowfish_ssh1;\r
+extern const struct ssh2_ciphers ssh2_3des;\r
+extern const struct ssh2_ciphers ssh2_des;\r
+extern const struct ssh2_ciphers ssh2_aes;\r
+extern const struct ssh2_ciphers ssh2_blowfish;\r
+extern const struct ssh2_ciphers ssh2_arcfour;\r
+extern const struct ssh_hash ssh_sha1;\r
+extern const struct ssh_hash ssh_sha256;\r
+extern const struct ssh_kexes ssh_diffiehellman_group1;\r
+extern const struct ssh_kexes ssh_diffiehellman_group14;\r
+extern const struct ssh_kexes ssh_diffiehellman_gex;\r
+extern const struct ssh_kexes ssh_rsa_kex;\r
+extern const struct ssh_signkey ssh_dss;\r
+extern const struct ssh_signkey ssh_rsa;\r
+extern const struct ssh_mac ssh_hmac_md5;\r
+extern const struct ssh_mac ssh_hmac_sha1;\r
+extern const struct ssh_mac ssh_hmac_sha1_buggy;\r
+extern const struct ssh_mac ssh_hmac_sha1_96;\r
+extern const struct ssh_mac ssh_hmac_sha1_96_buggy;\r
+\r
+void *aes_make_context(void);\r
+void aes_free_context(void *handle);\r
+void aes128_key(void *handle, unsigned char *key);\r
+void aes192_key(void *handle, unsigned char *key);\r
+void aes256_key(void *handle, unsigned char *key);\r
+void aes_iv(void *handle, unsigned char *iv);\r
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len);\r
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len);\r
+\r
+/*\r
+ * PuTTY version number formatted as an SSH version string. \r
+ */\r
+extern char sshver[];\r
+\r
+/*\r
+ * Gross hack: pscp will try to start SFTP but fall back to scp1 if\r
+ * that fails. This variable is the means by which scp.c can reach\r
+ * into the SSH code and find out which one it got.\r
+ */\r
+extern int ssh_fallback_cmd(void *handle);\r
+\r
+#ifndef MSCRYPTOAPI\r
+void SHATransform(word32 * digest, word32 * data);\r
+#endif\r
+\r
+int random_byte(void);\r
+void random_add_noise(void *noise, int length);\r
+void random_add_heavynoise(void *noise, int length);\r
+\r
+void logevent(void *, const char *);\r
+\r
+/* Allocate and register a new channel for port forwarding */\r
+void *new_sock_channel(void *handle, Socket s);\r
+void ssh_send_port_open(void *channel, char *hostname, int port, char *org);\r
+\r
+/* Exports from portfwd.c */\r
+extern const char *pfd_newconnect(Socket * s, char *hostname, int port,\r
+                                 void *c, const Config *cfg,\r
+                                 int addressfamily);\r
+/* desthost == NULL indicates dynamic (SOCKS) port forwarding */\r
+extern const char *pfd_addforward(char *desthost, int destport, char *srcaddr,\r
+                                 int port, void *backhandle,\r
+                                 const Config *cfg, void **sockdata,\r
+                                 int address_family);\r
+extern void pfd_close(Socket s);\r
+extern void pfd_terminate(void *sockdata);\r
+extern int pfd_send(Socket s, char *data, int len);\r
+extern void pfd_confirm(Socket s);\r
+extern void pfd_unthrottle(Socket s);\r
+extern void pfd_override_throttle(Socket s, int enable);\r
+\r
+/* Exports from x11fwd.c */\r
+enum {\r
+    X11_TRANS_IPV4 = 0, X11_TRANS_IPV6 = 6, X11_TRANS_UNIX = 256\r
+};\r
+struct X11Display {\r
+    /* Broken-down components of the display name itself */\r
+    int unixdomain;\r
+    char *hostname;\r
+    int displaynum;\r
+    int screennum;\r
+    /* OSX sometimes replaces all the above with a full Unix-socket pathname */\r
+    char *unixsocketpath;\r
+\r
+    /* PuTTY networking SockAddr to connect to the display, and associated\r
+     * gubbins */\r
+    SockAddr addr;\r
+    int port;\r
+    char *realhost;\r
+\r
+    /* Auth details we invented for the virtual display on the SSH server. */\r
+    int remoteauthproto;\r
+    unsigned char *remoteauthdata;\r
+    int remoteauthdatalen;\r
+    char *remoteauthprotoname;\r
+    char *remoteauthdatastring;\r
+\r
+    /* Our local auth details for talking to the real X display. */\r
+    int localauthproto;\r
+    unsigned char *localauthdata;\r
+    int localauthdatalen;\r
+\r
+    /*\r
+     * Used inside x11fwd.c to remember recently seen\r
+     * XDM-AUTHORIZATION-1 strings, to avoid replay attacks.\r
+     */\r
+    tree234 *xdmseen;\r
+};\r
+/*\r
+ * x11_setup_display() parses the display variable and fills in an\r
+ * X11Display structure. Some remote auth details are invented;\r
+ * the supplied authtype parameter configures the preferred\r
+ * authorisation protocol to use at the remote end. The local auth\r
+ * details are looked up by calling platform_get_x11_auth.\r
+ */\r
+extern struct X11Display *x11_setup_display(char *display, int authtype,\r
+                                           const Config *);\r
+void x11_free_display(struct X11Display *disp);\r
+extern const char *x11_init(Socket *, struct X11Display *, void *,\r
+                           const char *, int, const Config *);\r
+extern void x11_close(Socket);\r
+extern int x11_send(Socket, char *, int);\r
+extern void x11_unthrottle(Socket s);\r
+extern void x11_override_throttle(Socket s, int enable);\r
+char *x11_display(const char *display);\r
+/* Platform-dependent X11 functions */\r
+extern void platform_get_x11_auth(struct X11Display *display,\r
+                                 const Config *);\r
+    /* examine a mostly-filled-in X11Display and fill in localauth* */\r
+extern const int platform_uses_x11_unix_by_default;\r
+    /* choose default X transport in the absence of a specified one */\r
+SockAddr platform_get_x11_unix_address(const char *path, int displaynum);\r
+    /* make up a SockAddr naming the address for displaynum */\r
+char *platform_get_x_display(void);\r
+    /* allocated local X display string, if any */\r
+/* Callbacks in x11.c usable _by_ platform X11 functions */\r
+/*\r
+ * This function does the job of platform_get_x11_auth, provided\r
+ * it is told where to find a normally formatted .Xauthority file:\r
+ * it opens that file, parses it to find an auth record which\r
+ * matches the display details in "display", and fills in the\r
+ * localauth fields.\r
+ *\r
+ * It is expected that most implementations of\r
+ * platform_get_x11_auth() will work by finding their system's\r
+ * .Xauthority file, adjusting the display details if necessary\r
+ * for local oddities like Unix-domain socket transport, and\r
+ * calling this function to do the rest of the work.\r
+ */\r
+void x11_get_auth_from_authfile(struct X11Display *display,\r
+                               const char *authfilename);\r
+\r
+Bignum copybn(Bignum b);\r
+Bignum bn_power_2(int n);\r
+void bn_restore_invariant(Bignum b);\r
+Bignum bignum_from_long(unsigned long n);\r
+void freebn(Bignum b);\r
+Bignum modpow(Bignum base, Bignum exp, Bignum mod);\r
+Bignum modmul(Bignum a, Bignum b, Bignum mod);\r
+void decbn(Bignum n);\r
+extern Bignum Zero, One;\r
+Bignum bignum_from_bytes(const unsigned char *data, int nbytes);\r
+int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result);\r
+int bignum_bitcount(Bignum bn);\r
+int ssh1_bignum_length(Bignum bn);\r
+int ssh2_bignum_length(Bignum bn);\r
+int bignum_byte(Bignum bn, int i);\r
+int bignum_bit(Bignum bn, int i);\r
+void bignum_set_bit(Bignum bn, int i, int value);\r
+int ssh1_write_bignum(void *data, Bignum bn);\r
+Bignum biggcd(Bignum a, Bignum b);\r
+unsigned short bignum_mod_short(Bignum number, unsigned short modulus);\r
+Bignum bignum_add_long(Bignum number, unsigned long addend);\r
+Bignum bigadd(Bignum a, Bignum b);\r
+Bignum bigsub(Bignum a, Bignum b);\r
+Bignum bigmul(Bignum a, Bignum b);\r
+Bignum bigmuladd(Bignum a, Bignum b, Bignum addend);\r
+Bignum bigdiv(Bignum a, Bignum b);\r
+Bignum bigmod(Bignum a, Bignum b);\r
+Bignum modinv(Bignum number, Bignum modulus);\r
+Bignum bignum_bitmask(Bignum number);\r
+Bignum bignum_rshift(Bignum number, int shift);\r
+int bignum_cmp(Bignum a, Bignum b);\r
+char *bignum_decimal(Bignum x);\r
+\r
+#ifdef DEBUG\r
+void diagbn(char *prefix, Bignum md);\r
+#endif\r
+\r
+void *dh_setup_group(const struct ssh_kex *kex);\r
+void *dh_setup_gex(Bignum pval, Bignum gval);\r
+void dh_cleanup(void *);\r
+Bignum dh_create_e(void *, int nbits);\r
+Bignum dh_find_K(void *, Bignum f);\r
+\r
+int loadrsakey(const Filename *filename, struct RSAKey *key,\r
+              char *passphrase, const char **errorstr);\r
+int rsakey_encrypted(const Filename *filename, char **comment);\r
+int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,\r
+                  char **commentptr, const char **errorstr);\r
+\r
+int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase);\r
+\r
+extern int base64_decode_atom(char *atom, unsigned char *out);\r
+extern int base64_lines(int datalen);\r
+extern void base64_encode_atom(unsigned char *data, int n, char *out);\r
+extern void base64_encode(FILE *fp, unsigned char *data, int datalen, int cpl);\r
+\r
+/* ssh2_load_userkey can return this as an error */\r
+extern struct ssh2_userkey ssh2_wrong_passphrase;\r
+#define SSH2_WRONG_PASSPHRASE (&ssh2_wrong_passphrase)\r
+\r
+int ssh2_userkey_encrypted(const Filename *filename, char **comment);\r
+struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,\r
+                                      char *passphrase, const char **errorstr);\r
+unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,\r
+                                   int *pub_blob_len, char **commentptr,\r
+                                   const char **errorstr);\r
+int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,\r
+                     char *passphrase);\r
+const struct ssh_signkey *find_pubkey_alg(const char *name);\r
+\r
+enum {\r
+    SSH_KEYTYPE_UNOPENABLE,\r
+    SSH_KEYTYPE_UNKNOWN,\r
+    SSH_KEYTYPE_SSH1, SSH_KEYTYPE_SSH2,\r
+    SSH_KEYTYPE_OPENSSH, SSH_KEYTYPE_SSHCOM\r
+};\r
+int key_type(const Filename *filename);\r
+char *key_type_to_str(int type);\r
+\r
+int import_possible(int type);\r
+int import_target_type(int type);\r
+int import_encrypted(const Filename *filename, int type, char **comment);\r
+int import_ssh1(const Filename *filename, int type,\r
+               struct RSAKey *key, char *passphrase, const char **errmsg_p);\r
+struct ssh2_userkey *import_ssh2(const Filename *filename, int type,\r
+                                char *passphrase, const char **errmsg_p);\r
+int export_ssh1(const Filename *filename, int type,\r
+               struct RSAKey *key, char *passphrase);\r
+int export_ssh2(const Filename *filename, int type,\r
+                struct ssh2_userkey *key, char *passphrase);\r
+\r
+void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len);\r
+void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len);\r
+void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,\r
+                             unsigned char *blk, int len);\r
+void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,\r
+                             unsigned char *blk, int len);\r
+void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk,\r
+                          int len);\r
+void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk,\r
+                          int len);\r
+\r
+void des_encrypt_xdmauth(unsigned char *key, unsigned char *blk, int len);\r
+void des_decrypt_xdmauth(unsigned char *key, unsigned char *blk, int len);\r
+\r
+/*\r
+ * For progress updates in the key generation utility.\r
+ */\r
+#define PROGFN_INITIALISE 1\r
+#define PROGFN_LIN_PHASE 2\r
+#define PROGFN_EXP_PHASE 3\r
+#define PROGFN_PHASE_EXTENT 4\r
+#define PROGFN_READY 5\r
+#define PROGFN_PROGRESS 6\r
+typedef void (*progfn_t) (void *param, int action, int phase, int progress);\r
+\r
+int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,\r
+                void *pfnparam);\r
+int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,\r
+                void *pfnparam);\r
+Bignum primegen(int bits, int modulus, int residue, Bignum factor,\r
+               int phase, progfn_t pfn, void *pfnparam);\r
+\r
+\r
+/*\r
+ * zlib compression.\r
+ */\r
+void *zlib_compress_init(void);\r
+void zlib_compress_cleanup(void *);\r
+void *zlib_decompress_init(void);\r
+void zlib_decompress_cleanup(void *);\r
+int zlib_compress_block(void *, unsigned char *block, int len,\r
+                       unsigned char **outblock, int *outlen);\r
+int zlib_decompress_block(void *, unsigned char *block, int len,\r
+                         unsigned char **outblock, int *outlen);\r
+\r
+/*\r
+ * SSH-1 agent messages.\r
+ */\r
+#define SSH1_AGENTC_REQUEST_RSA_IDENTITIES    1\r
+#define SSH1_AGENT_RSA_IDENTITIES_ANSWER      2\r
+#define SSH1_AGENTC_RSA_CHALLENGE             3\r
+#define SSH1_AGENT_RSA_RESPONSE               4\r
+#define SSH1_AGENTC_ADD_RSA_IDENTITY          7\r
+#define SSH1_AGENTC_REMOVE_RSA_IDENTITY       8\r
+#define SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9        /* openssh private? */\r
+\r
+/*\r
+ * Messages common to SSH-1 and OpenSSH's SSH-2.\r
+ */\r
+#define SSH_AGENT_FAILURE                    5\r
+#define SSH_AGENT_SUCCESS                    6\r
+\r
+/*\r
+ * OpenSSH's SSH-2 agent messages.\r
+ */\r
+#define SSH2_AGENTC_REQUEST_IDENTITIES          11\r
+#define SSH2_AGENT_IDENTITIES_ANSWER            12\r
+#define SSH2_AGENTC_SIGN_REQUEST                13\r
+#define SSH2_AGENT_SIGN_RESPONSE                14\r
+#define SSH2_AGENTC_ADD_IDENTITY                17\r
+#define SSH2_AGENTC_REMOVE_IDENTITY             18\r
+#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES       19\r
+\r
+/*\r
+ * Need this to warn about support for the original SSH-2 keyfile\r
+ * format.\r
+ */\r
+void old_keyfile_warning(void);\r
diff --git a/putty/SSHAES.C b/putty/SSHAES.C
new file mode 100644 (file)
index 0000000..7684cd9
--- /dev/null
@@ -0,0 +1,1234 @@
+/*\r
+ * aes.c - implementation of AES / Rijndael\r
+ * \r
+ * AES is a flexible algorithm as regards endianness: it has no\r
+ * inherent preference as to which way round you should form words\r
+ * from the input byte stream. It talks endlessly of four-byte\r
+ * _vectors_, but never of 32-bit _words_ - there's no 32-bit\r
+ * addition at all, which would force an endianness by means of\r
+ * which way the carries went. So it would be possible to write a\r
+ * working AES that read words big-endian, and another working one\r
+ * that read them little-endian, just by computing a different set\r
+ * of tables - with no speed drop.\r
+ * \r
+ * It's therefore tempting to do just that, and remove the overhead\r
+ * of GET_32BIT_MSB_FIRST() et al, allowing every system to use its\r
+ * own endianness-native code; but I decided not to, partly for\r
+ * ease of testing, and mostly because I like the flexibility that\r
+ * allows you to encrypt a non-word-aligned block of memory (which\r
+ * many systems would stop being able to do if I went the\r
+ * endianness-dependent route).\r
+ * \r
+ * This implementation reads and stores words big-endian, but\r
+ * that's a minor implementation detail. By flipping the endianness\r
+ * of everything in the E0..E3, D0..D3 tables, and substituting\r
+ * GET_32BIT_LSB_FIRST for GET_32BIT_MSB_FIRST, I could create an\r
+ * implementation that worked internally little-endian and gave the\r
+ * same answers at the same speed.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "ssh.h"\r
+\r
+#define MAX_NR 14                     /* max no of rounds */\r
+#define MAX_NK 8                      /* max no of words in input key */\r
+#define MAX_NB 8                      /* max no of words in cipher blk */\r
+\r
+#define mulby2(x) ( ((x&0x7F) << 1) ^ (x & 0x80 ? 0x1B : 0) )\r
+\r
+typedef struct AESContext AESContext;\r
+\r
+struct AESContext {\r
+    word32 keysched[(MAX_NR + 1) * MAX_NB];\r
+    word32 invkeysched[(MAX_NR + 1) * MAX_NB];\r
+    void (*encrypt) (AESContext * ctx, word32 * block);\r
+    void (*decrypt) (AESContext * ctx, word32 * block);\r
+    word32 iv[MAX_NB];\r
+    int Nb, Nr;\r
+};\r
+\r
+static const unsigned char Sbox[256] = {\r
+    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,\r
+    0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,\r
+    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,\r
+    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,\r
+    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,\r
+    0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,\r
+    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,\r
+    0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,\r
+    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,\r
+    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,\r
+    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,\r
+    0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,\r
+    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,\r
+    0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,\r
+    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,\r
+    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,\r
+    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,\r
+    0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,\r
+    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,\r
+    0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,\r
+    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,\r
+    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,\r
+    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,\r
+    0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,\r
+    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,\r
+    0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,\r
+    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,\r
+    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,\r
+    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,\r
+    0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,\r
+    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,\r
+    0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16\r
+};\r
+\r
+static const unsigned char Sboxinv[256] = {\r
+    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,\r
+    0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,\r
+    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,\r
+    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,\r
+    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,\r
+    0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,\r
+    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,\r
+    0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,\r
+    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,\r
+    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,\r
+    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,\r
+    0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,\r
+    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,\r
+    0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,\r
+    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,\r
+    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,\r
+    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,\r
+    0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,\r
+    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,\r
+    0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,\r
+    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,\r
+    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,\r
+    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,\r
+    0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,\r
+    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,\r
+    0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,\r
+    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,\r
+    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,\r
+    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,\r
+    0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,\r
+    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,\r
+    0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d\r
+};\r
+\r
+static const word32 E0[256] = {\r
+    0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d,\r
+    0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,\r
+    0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,\r
+    0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,\r
+    0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87,\r
+    0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,\r
+    0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea,\r
+    0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,\r
+    0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,\r
+    0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,\r
+    0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108,\r
+    0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,\r
+    0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e,\r
+    0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,\r
+    0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,\r
+    0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,\r
+    0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e,\r
+    0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,\r
+    0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce,\r
+    0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,\r
+    0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,\r
+    0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,\r
+    0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b,\r
+    0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,\r
+    0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16,\r
+    0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,\r
+    0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,\r
+    0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,\r
+    0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a,\r
+    0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,\r
+    0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163,\r
+    0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,\r
+    0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,\r
+    0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,\r
+    0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47,\r
+    0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,\r
+    0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f,\r
+    0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,\r
+    0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,\r
+    0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,\r
+    0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e,\r
+    0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,\r
+    0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6,\r
+    0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,\r
+    0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,\r
+    0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,\r
+    0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25,\r
+    0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,\r
+    0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72,\r
+    0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,\r
+    0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,\r
+    0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,\r
+    0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa,\r
+    0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,\r
+    0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0,\r
+    0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,\r
+    0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,\r
+    0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,\r
+    0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920,\r
+    0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,\r
+    0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17,\r
+    0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,\r
+    0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,\r
+    0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,\r
+};\r
+static const word32 E1[256] = {\r
+    0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b,\r
+    0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5,\r
+    0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b,\r
+    0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676,\r
+    0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d,\r
+    0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,\r
+    0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf,\r
+    0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0,\r
+    0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,\r
+    0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc,\r
+    0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1,\r
+    0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,\r
+    0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3,\r
+    0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a,\r
+    0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,\r
+    0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575,\r
+    0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a,\r
+    0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,\r
+    0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3,\r
+    0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484,\r
+    0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded,\r
+    0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b,\r
+    0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939,\r
+    0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,\r
+    0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb,\r
+    0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585,\r
+    0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f,\r
+    0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8,\r
+    0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f,\r
+    0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,\r
+    0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121,\r
+    0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2,\r
+    0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,\r
+    0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717,\r
+    0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d,\r
+    0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,\r
+    0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc,\r
+    0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888,\r
+    0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,\r
+    0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb,\r
+    0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a,\r
+    0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,\r
+    0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262,\r
+    0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979,\r
+    0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,\r
+    0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9,\r
+    0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea,\r
+    0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,\r
+    0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e,\r
+    0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6,\r
+    0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,\r
+    0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a,\r
+    0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666,\r
+    0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,\r
+    0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9,\r
+    0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e,\r
+    0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,\r
+    0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494,\r
+    0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9,\r
+    0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,\r
+    0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d,\r
+    0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868,\r
+    0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,\r
+    0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616,\r
+};\r
+static const word32 E2[256] = {\r
+    0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b,\r
+    0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5,\r
+    0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b,\r
+    0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76,\r
+    0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d,\r
+    0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,\r
+    0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af,\r
+    0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0,\r
+    0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,\r
+    0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc,\r
+    0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1,\r
+    0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,\r
+    0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3,\r
+    0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a,\r
+    0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,\r
+    0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75,\r
+    0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a,\r
+    0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,\r
+    0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3,\r
+    0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384,\r
+    0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed,\r
+    0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b,\r
+    0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239,\r
+    0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,\r
+    0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb,\r
+    0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185,\r
+    0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f,\r
+    0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8,\r
+    0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f,\r
+    0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,\r
+    0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221,\r
+    0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2,\r
+    0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec,\r
+    0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17,\r
+    0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d,\r
+    0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,\r
+    0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc,\r
+    0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88,\r
+    0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,\r
+    0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb,\r
+    0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a,\r
+    0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,\r
+    0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462,\r
+    0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279,\r
+    0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,\r
+    0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9,\r
+    0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea,\r
+    0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,\r
+    0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e,\r
+    0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6,\r
+    0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,\r
+    0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a,\r
+    0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66,\r
+    0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,\r
+    0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9,\r
+    0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e,\r
+    0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,\r
+    0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394,\r
+    0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9,\r
+    0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,\r
+    0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d,\r
+    0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068,\r
+    0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f,\r
+    0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16,\r
+};\r
+static const word32 E3[256] = {\r
+    0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6,\r
+    0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491,\r
+    0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56,\r
+    0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec,\r
+    0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa,\r
+    0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,\r
+    0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45,\r
+    0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b,\r
+    0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,\r
+    0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83,\r
+    0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9,\r
+    0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,\r
+    0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d,\r
+    0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f,\r
+    0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,\r
+    0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea,\r
+    0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34,\r
+    0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,\r
+    0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d,\r
+    0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713,\r
+    0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1,\r
+    0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6,\r
+    0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72,\r
+    0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,\r
+    0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed,\r
+    0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411,\r
+    0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe,\r
+    0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b,\r
+    0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05,\r
+    0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,\r
+    0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342,\r
+    0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf,\r
+    0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3,\r
+    0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e,\r
+    0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a,\r
+    0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,\r
+    0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3,\r
+    0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b,\r
+    0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,\r
+    0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad,\r
+    0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14,\r
+    0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,\r
+    0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4,\r
+    0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2,\r
+    0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,\r
+    0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049,\r
+    0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf,\r
+    0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,\r
+    0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c,\r
+    0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197,\r
+    0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,\r
+    0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f,\r
+    0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc,\r
+    0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,\r
+    0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069,\r
+    0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927,\r
+    0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,\r
+    0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733,\r
+    0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9,\r
+    0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,\r
+    0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a,\r
+    0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0,\r
+    0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e,\r
+    0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c,\r
+};\r
+static const word32 D0[256] = {\r
+    0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96,\r
+    0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,\r
+    0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,\r
+    0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,\r
+    0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1,\r
+    0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,\r
+    0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da,\r
+    0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,\r
+    0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,\r
+    0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,\r
+    0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45,\r
+    0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,\r
+    0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7,\r
+    0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,\r
+    0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,\r
+    0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,\r
+    0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1,\r
+    0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,\r
+    0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75,\r
+    0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,\r
+    0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,\r
+    0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,\r
+    0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77,\r
+    0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,\r
+    0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000,\r
+    0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,\r
+    0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,\r
+    0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,\r
+    0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e,\r
+    0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,\r
+    0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d,\r
+    0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,\r
+    0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,\r
+    0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,\r
+    0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163,\r
+    0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,\r
+    0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d,\r
+    0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,\r
+    0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,\r
+    0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,\r
+    0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36,\r
+    0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,\r
+    0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662,\r
+    0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,\r
+    0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,\r
+    0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,\r
+    0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8,\r
+    0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,\r
+    0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6,\r
+    0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,\r
+    0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,\r
+    0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,\r
+    0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df,\r
+    0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,\r
+    0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e,\r
+    0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,\r
+    0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,\r
+    0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,\r
+    0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf,\r
+    0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,\r
+    0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f,\r
+    0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,\r
+    0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,\r
+    0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742,\r
+};\r
+static const word32 D1[256] = {\r
+    0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e,\r
+    0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303,\r
+    0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c,\r
+    0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3,\r
+    0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0,\r
+    0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,\r
+    0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259,\r
+    0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8,\r
+    0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971,\r
+    0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a,\r
+    0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f,\r
+    0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,\r
+    0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8,\r
+    0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab,\r
+    0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708,\r
+    0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682,\r
+    0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2,\r
+    0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,\r
+    0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb,\r
+    0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10,\r
+    0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd,\r
+    0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015,\r
+    0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e,\r
+    0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,\r
+    0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000,\r
+    0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72,\r
+    0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39,\r
+    0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e,\r
+    0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91,\r
+    0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,\r
+    0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17,\r
+    0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9,\r
+    0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60,\r
+    0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e,\r
+    0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1,\r
+    0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,\r
+    0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1,\r
+    0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3,\r
+    0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964,\r
+    0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390,\r
+    0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b,\r
+    0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,\r
+    0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46,\r
+    0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af,\r
+    0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512,\r
+    0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb,\r
+    0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a,\r
+    0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,\r
+    0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c,\r
+    0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266,\r
+    0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8,\r
+    0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6,\r
+    0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604,\r
+    0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,\r
+    0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41,\r
+    0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647,\r
+    0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c,\r
+    0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1,\r
+    0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737,\r
+    0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,\r
+    0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340,\r
+    0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95,\r
+    0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1,\r
+    0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857,\r
+};\r
+static const word32 D2[256] = {\r
+    0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27,\r
+    0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3,\r
+    0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502,\r
+    0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562,\r
+    0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe,\r
+    0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,\r
+    0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552,\r
+    0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9,\r
+    0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9,\r
+    0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce,\r
+    0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253,\r
+    0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,\r
+    0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b,\r
+    0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655,\r
+    0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337,\r
+    0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16,\r
+    0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69,\r
+    0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,\r
+    0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6,\r
+    0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e,\r
+    0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6,\r
+    0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050,\r
+    0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9,\r
+    0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,\r
+    0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000,\r
+    0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a,\r
+    0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d,\r
+    0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436,\r
+    0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b,\r
+    0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,\r
+    0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b,\r
+    0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e,\r
+    0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f,\r
+    0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb,\r
+    0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4,\r
+    0xdccad731, 0x85104263, 0x22401397, 0x112084c6,\r
+    0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729,\r
+    0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1,\r
+    0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9,\r
+    0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233,\r
+    0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4,\r
+    0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,\r
+    0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e,\r
+    0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3,\r
+    0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25,\r
+    0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b,\r
+    0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f,\r
+    0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,\r
+    0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0,\r
+    0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2,\r
+    0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7,\r
+    0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791,\r
+    0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496,\r
+    0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,\r
+    0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b,\r
+    0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6,\r
+    0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13,\r
+    0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47,\r
+    0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7,\r
+    0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,\r
+    0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3,\r
+    0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d,\r
+    0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456,\r
+    0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8,\r
+};\r
+static const word32 D3[256] = {\r
+    0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a,\r
+    0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b,\r
+    0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5,\r
+    0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5,\r
+    0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d,\r
+    0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,\r
+    0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95,\r
+    0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e,\r
+    0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27,\r
+    0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d,\r
+    0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562,\r
+    0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,\r
+    0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752,\r
+    0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66,\r
+    0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3,\r
+    0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced,\r
+    0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e,\r
+    0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,\r
+    0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4,\r
+    0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd,\r
+    0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d,\r
+    0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60,\r
+    0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767,\r
+    0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,\r
+    0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000,\r
+    0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c,\r
+    0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736,\r
+    0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24,\r
+    0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b,\r
+    0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,\r
+    0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12,\r
+    0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814,\r
+    0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3,\r
+    0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b,\r
+    0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8,\r
+    0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,\r
+    0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7,\r
+    0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077,\r
+    0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247,\r
+    0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22,\r
+    0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698,\r
+    0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,\r
+    0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254,\r
+    0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582,\r
+    0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf,\r
+    0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb,\r
+    0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883,\r
+    0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,\r
+    0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629,\r
+    0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035,\r
+    0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533,\r
+    0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17,\r
+    0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4,\r
+    0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,\r
+    0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb,\r
+    0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d,\r
+    0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb,\r
+    0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a,\r
+    0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73,\r
+    0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,\r
+    0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2,\r
+    0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff,\r
+    0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064,\r
+    0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0,\r
+};\r
+\r
+/*\r
+ * Common macros in both the encryption and decryption routines.\r
+ */\r
+#define ADD_ROUND_KEY_4 (block[0]^=*keysched++, block[1]^=*keysched++, \\r
+                        block[2]^=*keysched++, block[3]^=*keysched++)\r
+#define ADD_ROUND_KEY_6 (block[0]^=*keysched++, block[1]^=*keysched++, \\r
+                        block[2]^=*keysched++, block[3]^=*keysched++, \\r
+                        block[4]^=*keysched++, block[5]^=*keysched++)\r
+#define ADD_ROUND_KEY_8 (block[0]^=*keysched++, block[1]^=*keysched++, \\r
+                        block[2]^=*keysched++, block[3]^=*keysched++, \\r
+                        block[4]^=*keysched++, block[5]^=*keysched++, \\r
+                        block[6]^=*keysched++, block[7]^=*keysched++)\r
+#define MOVEWORD(i) ( block[i] = newstate[i] )\r
+\r
+/*\r
+ * Macros for the encryption routine. There are three encryption\r
+ * cores, for Nb=4,6,8.\r
+ */\r
+#define MAKEWORD(i) ( newstate[i] = (E0[(block[i] >> 24) & 0xFF] ^ \\r
+                                    E1[(block[(i+C1)%Nb] >> 16) & 0xFF] ^ \\r
+                                    E2[(block[(i+C2)%Nb] >> 8) & 0xFF] ^ \\r
+                                    E3[block[(i+C3)%Nb] & 0xFF]) )\r
+#define LASTWORD(i) ( newstate[i] = (Sbox[(block[i] >> 24) & 0xFF] << 24) | \\r
+                           (Sbox[(block[(i+C1)%Nb] >> 16) & 0xFF] << 16) | \\r
+                           (Sbox[(block[(i+C2)%Nb] >>  8) & 0xFF] <<  8) | \\r
+                           (Sbox[(block[(i+C3)%Nb]      ) & 0xFF]      ) )\r
+\r
+/*\r
+ * Core encrypt routines, expecting word32 inputs read big-endian\r
+ * from the byte-oriented input stream.\r
+ */\r
+static void aes_encrypt_nb_4(AESContext * ctx, word32 * block)\r
+{\r
+    int i;\r
+    static const int C1 = 1, C2 = 2, C3 = 3, Nb = 4;\r
+    word32 *keysched = ctx->keysched;\r
+    word32 newstate[4];\r
+    for (i = 0; i < ctx->Nr - 1; i++) {\r
+       ADD_ROUND_KEY_4;\r
+       MAKEWORD(0);\r
+       MAKEWORD(1);\r
+       MAKEWORD(2);\r
+       MAKEWORD(3);\r
+       MOVEWORD(0);\r
+       MOVEWORD(1);\r
+       MOVEWORD(2);\r
+       MOVEWORD(3);\r
+    }\r
+    ADD_ROUND_KEY_4;\r
+    LASTWORD(0);\r
+    LASTWORD(1);\r
+    LASTWORD(2);\r
+    LASTWORD(3);\r
+    MOVEWORD(0);\r
+    MOVEWORD(1);\r
+    MOVEWORD(2);\r
+    MOVEWORD(3);\r
+    ADD_ROUND_KEY_4;\r
+}\r
+static void aes_encrypt_nb_6(AESContext * ctx, word32 * block)\r
+{\r
+    int i;\r
+    static const int C1 = 1, C2 = 2, C3 = 3, Nb = 6;\r
+    word32 *keysched = ctx->keysched;\r
+    word32 newstate[6];\r
+    for (i = 0; i < ctx->Nr - 1; i++) {\r
+       ADD_ROUND_KEY_6;\r
+       MAKEWORD(0);\r
+       MAKEWORD(1);\r
+       MAKEWORD(2);\r
+       MAKEWORD(3);\r
+       MAKEWORD(4);\r
+       MAKEWORD(5);\r
+       MOVEWORD(0);\r
+       MOVEWORD(1);\r
+       MOVEWORD(2);\r
+       MOVEWORD(3);\r
+       MOVEWORD(4);\r
+       MOVEWORD(5);\r
+    }\r
+    ADD_ROUND_KEY_6;\r
+    LASTWORD(0);\r
+    LASTWORD(1);\r
+    LASTWORD(2);\r
+    LASTWORD(3);\r
+    LASTWORD(4);\r
+    LASTWORD(5);\r
+    MOVEWORD(0);\r
+    MOVEWORD(1);\r
+    MOVEWORD(2);\r
+    MOVEWORD(3);\r
+    MOVEWORD(4);\r
+    MOVEWORD(5);\r
+    ADD_ROUND_KEY_6;\r
+}\r
+static void aes_encrypt_nb_8(AESContext * ctx, word32 * block)\r
+{\r
+    int i;\r
+    static const int C1 = 1, C2 = 3, C3 = 4, Nb = 8;\r
+    word32 *keysched = ctx->keysched;\r
+    word32 newstate[8];\r
+    for (i = 0; i < ctx->Nr - 1; i++) {\r
+       ADD_ROUND_KEY_8;\r
+       MAKEWORD(0);\r
+       MAKEWORD(1);\r
+       MAKEWORD(2);\r
+       MAKEWORD(3);\r
+       MAKEWORD(4);\r
+       MAKEWORD(5);\r
+       MAKEWORD(6);\r
+       MAKEWORD(7);\r
+       MOVEWORD(0);\r
+       MOVEWORD(1);\r
+       MOVEWORD(2);\r
+       MOVEWORD(3);\r
+       MOVEWORD(4);\r
+       MOVEWORD(5);\r
+       MOVEWORD(6);\r
+       MOVEWORD(7);\r
+    }\r
+    ADD_ROUND_KEY_8;\r
+    LASTWORD(0);\r
+    LASTWORD(1);\r
+    LASTWORD(2);\r
+    LASTWORD(3);\r
+    LASTWORD(4);\r
+    LASTWORD(5);\r
+    LASTWORD(6);\r
+    LASTWORD(7);\r
+    MOVEWORD(0);\r
+    MOVEWORD(1);\r
+    MOVEWORD(2);\r
+    MOVEWORD(3);\r
+    MOVEWORD(4);\r
+    MOVEWORD(5);\r
+    MOVEWORD(6);\r
+    MOVEWORD(7);\r
+    ADD_ROUND_KEY_8;\r
+}\r
+\r
+#undef MAKEWORD\r
+#undef LASTWORD\r
+\r
+/*\r
+ * Macros for the decryption routine. There are three decryption\r
+ * cores, for Nb=4,6,8.\r
+ */\r
+#define MAKEWORD(i) ( newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^ \\r
+                                    D1[(block[(i+C1)%Nb] >> 16) & 0xFF] ^ \\r
+                                    D2[(block[(i+C2)%Nb] >> 8) & 0xFF] ^ \\r
+                                    D3[block[(i+C3)%Nb] & 0xFF]) )\r
+#define LASTWORD(i) (newstate[i] = (Sboxinv[(block[i] >> 24) & 0xFF] << 24) | \\r
+                          (Sboxinv[(block[(i+C1)%Nb] >> 16) & 0xFF] << 16) | \\r
+                          (Sboxinv[(block[(i+C2)%Nb] >>  8) & 0xFF] <<  8) | \\r
+                          (Sboxinv[(block[(i+C3)%Nb]      ) & 0xFF]      ) )\r
+\r
+/*\r
+ * Core decrypt routines, expecting word32 inputs read big-endian\r
+ * from the byte-oriented input stream.\r
+ */\r
+static void aes_decrypt_nb_4(AESContext * ctx, word32 * block)\r
+{\r
+    int i;\r
+    static const int C1 = 4 - 1, C2 = 4 - 2, C3 = 4 - 3, Nb = 4;\r
+    word32 *keysched = ctx->invkeysched;\r
+    word32 newstate[4];\r
+    for (i = 0; i < ctx->Nr - 1; i++) {\r
+       ADD_ROUND_KEY_4;\r
+       MAKEWORD(0);\r
+       MAKEWORD(1);\r
+       MAKEWORD(2);\r
+       MAKEWORD(3);\r
+       MOVEWORD(0);\r
+       MOVEWORD(1);\r
+       MOVEWORD(2);\r
+       MOVEWORD(3);\r
+    }\r
+    ADD_ROUND_KEY_4;\r
+    LASTWORD(0);\r
+    LASTWORD(1);\r
+    LASTWORD(2);\r
+    LASTWORD(3);\r
+    MOVEWORD(0);\r
+    MOVEWORD(1);\r
+    MOVEWORD(2);\r
+    MOVEWORD(3);\r
+    ADD_ROUND_KEY_4;\r
+}\r
+static void aes_decrypt_nb_6(AESContext * ctx, word32 * block)\r
+{\r
+    int i;\r
+    static const int C1 = 6 - 1, C2 = 6 - 2, C3 = 6 - 3, Nb = 6;\r
+    word32 *keysched = ctx->invkeysched;\r
+    word32 newstate[6];\r
+    for (i = 0; i < ctx->Nr - 1; i++) {\r
+       ADD_ROUND_KEY_6;\r
+       MAKEWORD(0);\r
+       MAKEWORD(1);\r
+       MAKEWORD(2);\r
+       MAKEWORD(3);\r
+       MAKEWORD(4);\r
+       MAKEWORD(5);\r
+       MOVEWORD(0);\r
+       MOVEWORD(1);\r
+       MOVEWORD(2);\r
+       MOVEWORD(3);\r
+       MOVEWORD(4);\r
+       MOVEWORD(5);\r
+    }\r
+    ADD_ROUND_KEY_6;\r
+    LASTWORD(0);\r
+    LASTWORD(1);\r
+    LASTWORD(2);\r
+    LASTWORD(3);\r
+    LASTWORD(4);\r
+    LASTWORD(5);\r
+    MOVEWORD(0);\r
+    MOVEWORD(1);\r
+    MOVEWORD(2);\r
+    MOVEWORD(3);\r
+    MOVEWORD(4);\r
+    MOVEWORD(5);\r
+    ADD_ROUND_KEY_6;\r
+}\r
+static void aes_decrypt_nb_8(AESContext * ctx, word32 * block)\r
+{\r
+    int i;\r
+    static const int C1 = 8 - 1, C2 = 8 - 3, C3 = 8 - 4, Nb = 8;\r
+    word32 *keysched = ctx->invkeysched;\r
+    word32 newstate[8];\r
+    for (i = 0; i < ctx->Nr - 1; i++) {\r
+       ADD_ROUND_KEY_8;\r
+       MAKEWORD(0);\r
+       MAKEWORD(1);\r
+       MAKEWORD(2);\r
+       MAKEWORD(3);\r
+       MAKEWORD(4);\r
+       MAKEWORD(5);\r
+       MAKEWORD(6);\r
+       MAKEWORD(7);\r
+       MOVEWORD(0);\r
+       MOVEWORD(1);\r
+       MOVEWORD(2);\r
+       MOVEWORD(3);\r
+       MOVEWORD(4);\r
+       MOVEWORD(5);\r
+       MOVEWORD(6);\r
+       MOVEWORD(7);\r
+    }\r
+    ADD_ROUND_KEY_8;\r
+    LASTWORD(0);\r
+    LASTWORD(1);\r
+    LASTWORD(2);\r
+    LASTWORD(3);\r
+    LASTWORD(4);\r
+    LASTWORD(5);\r
+    LASTWORD(6);\r
+    LASTWORD(7);\r
+    MOVEWORD(0);\r
+    MOVEWORD(1);\r
+    MOVEWORD(2);\r
+    MOVEWORD(3);\r
+    MOVEWORD(4);\r
+    MOVEWORD(5);\r
+    MOVEWORD(6);\r
+    MOVEWORD(7);\r
+    ADD_ROUND_KEY_8;\r
+}\r
+\r
+#undef MAKEWORD\r
+#undef LASTWORD\r
+\r
+\r
+/*\r
+ * Set up an AESContext. `keylen' and `blocklen' are measured in\r
+ * bytes; each can be either 16 (128-bit), 24 (192-bit), or 32\r
+ * (256-bit).\r
+ */\r
+static void aes_setup(AESContext * ctx, int blocklen,\r
+              unsigned char *key, int keylen)\r
+{\r
+    int i, j, Nk, rconst;\r
+\r
+    assert(blocklen == 16 || blocklen == 24 || blocklen == 32);\r
+    assert(keylen == 16 || keylen == 24 || keylen == 32);\r
+\r
+    /*\r
+     * Basic parameters. Words per block, words in key, rounds.\r
+     */\r
+    Nk = keylen / 4;\r
+    ctx->Nb = blocklen / 4;\r
+    ctx->Nr = 6 + (ctx->Nb > Nk ? ctx->Nb : Nk);\r
+\r
+    /*\r
+     * Assign core-function pointers.\r
+     */\r
+    if (ctx->Nb == 8)\r
+       ctx->encrypt = aes_encrypt_nb_8, ctx->decrypt = aes_decrypt_nb_8;\r
+    else if (ctx->Nb == 6)\r
+       ctx->encrypt = aes_encrypt_nb_6, ctx->decrypt = aes_decrypt_nb_6;\r
+    else if (ctx->Nb == 4)\r
+       ctx->encrypt = aes_encrypt_nb_4, ctx->decrypt = aes_decrypt_nb_4;\r
+\r
+    /*\r
+     * Now do the key setup itself.\r
+     */\r
+    rconst = 1;\r
+    for (i = 0; i < (ctx->Nr + 1) * ctx->Nb; i++) {\r
+       if (i < Nk)\r
+           ctx->keysched[i] = GET_32BIT_MSB_FIRST(key + 4 * i);\r
+       else {\r
+           word32 temp = ctx->keysched[i - 1];\r
+           if (i % Nk == 0) {\r
+               int a, b, c, d;\r
+               a = (temp >> 16) & 0xFF;\r
+               b = (temp >> 8) & 0xFF;\r
+               c = (temp >> 0) & 0xFF;\r
+               d = (temp >> 24) & 0xFF;\r
+               temp = Sbox[a] ^ rconst;\r
+               temp = (temp << 8) | Sbox[b];\r
+               temp = (temp << 8) | Sbox[c];\r
+               temp = (temp << 8) | Sbox[d];\r
+               rconst = mulby2(rconst);\r
+           } else if (i % Nk == 4 && Nk > 6) {\r
+               int a, b, c, d;\r
+               a = (temp >> 24) & 0xFF;\r
+               b = (temp >> 16) & 0xFF;\r
+               c = (temp >> 8) & 0xFF;\r
+               d = (temp >> 0) & 0xFF;\r
+               temp = Sbox[a];\r
+               temp = (temp << 8) | Sbox[b];\r
+               temp = (temp << 8) | Sbox[c];\r
+               temp = (temp << 8) | Sbox[d];\r
+           }\r
+           ctx->keysched[i] = ctx->keysched[i - Nk] ^ temp;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Now prepare the modified keys for the inverse cipher.\r
+     */\r
+    for (i = 0; i <= ctx->Nr; i++) {\r
+       for (j = 0; j < ctx->Nb; j++) {\r
+           word32 temp;\r
+           temp = ctx->keysched[(ctx->Nr - i) * ctx->Nb + j];\r
+           if (i != 0 && i != ctx->Nr) {\r
+               /*\r
+                * Perform the InvMixColumn operation on i. The D\r
+                * tables give the result of InvMixColumn applied\r
+                * to Sboxinv on individual bytes, so we should\r
+                * compose Sbox with the D tables for this.\r
+                */\r
+               int a, b, c, d;\r
+               a = (temp >> 24) & 0xFF;\r
+               b = (temp >> 16) & 0xFF;\r
+               c = (temp >> 8) & 0xFF;\r
+               d = (temp >> 0) & 0xFF;\r
+               temp = D0[Sbox[a]];\r
+               temp ^= D1[Sbox[b]];\r
+               temp ^= D2[Sbox[c]];\r
+               temp ^= D3[Sbox[d]];\r
+           }\r
+           ctx->invkeysched[i * ctx->Nb + j] = temp;\r
+       }\r
+    }\r
+}\r
+\r
+static void aes_encrypt(AESContext * ctx, word32 * block)\r
+{\r
+    ctx->encrypt(ctx, block);\r
+}\r
+\r
+static void aes_decrypt(AESContext * ctx, word32 * block)\r
+{\r
+    ctx->decrypt(ctx, block);\r
+}\r
+\r
+static void aes_encrypt_cbc(unsigned char *blk, int len, AESContext * ctx)\r
+{\r
+    word32 iv[4];\r
+    int i;\r
+\r
+    assert((len & 15) == 0);\r
+\r
+    memcpy(iv, ctx->iv, sizeof(iv));\r
+\r
+    while (len > 0) {\r
+       for (i = 0; i < 4; i++)\r
+           iv[i] ^= GET_32BIT_MSB_FIRST(blk + 4 * i);\r
+       aes_encrypt(ctx, iv);\r
+       for (i = 0; i < 4; i++)\r
+           PUT_32BIT_MSB_FIRST(blk + 4 * i, iv[i]);\r
+       blk += 16;\r
+       len -= 16;\r
+    }\r
+\r
+    memcpy(ctx->iv, iv, sizeof(iv));\r
+}\r
+\r
+static void aes_decrypt_cbc(unsigned char *blk, int len, AESContext * ctx)\r
+{\r
+    word32 iv[4], x[4], ct[4];\r
+    int i;\r
+\r
+    assert((len & 15) == 0);\r
+\r
+    memcpy(iv, ctx->iv, sizeof(iv));\r
+\r
+    while (len > 0) {\r
+       for (i = 0; i < 4; i++)\r
+           x[i] = ct[i] = GET_32BIT_MSB_FIRST(blk + 4 * i);\r
+       aes_decrypt(ctx, x);\r
+       for (i = 0; i < 4; i++) {\r
+           PUT_32BIT_MSB_FIRST(blk + 4 * i, iv[i] ^ x[i]);\r
+           iv[i] = ct[i];\r
+       }\r
+       blk += 16;\r
+       len -= 16;\r
+    }\r
+\r
+    memcpy(ctx->iv, iv, sizeof(iv));\r
+}\r
+\r
+static void aes_sdctr(unsigned char *blk, int len, AESContext *ctx)\r
+{\r
+    word32 iv[4], b[4], tmp;\r
+    int i;\r
+\r
+    assert((len & 15) == 0);\r
+\r
+    memcpy(iv, ctx->iv, sizeof(iv));\r
+\r
+    while (len > 0) {\r
+       memcpy(b, iv, sizeof(b));\r
+       aes_encrypt(ctx, b);\r
+       for (i = 0; i < 4; i++) {\r
+           tmp = GET_32BIT_MSB_FIRST(blk + 4 * i);\r
+           PUT_32BIT_MSB_FIRST(blk + 4 * i, tmp ^ b[i]);\r
+       }\r
+       for (i = 3; i >= 0; i--)\r
+           if ((iv[i] = (iv[i] + 1) & 0xffffffff) != 0)\r
+               break;\r
+       blk += 16;\r
+       len -= 16;\r
+    }\r
+\r
+    memcpy(ctx->iv, iv, sizeof(iv));\r
+}\r
+\r
+void *aes_make_context(void)\r
+{\r
+    return snew(AESContext);\r
+}\r
+\r
+void aes_free_context(void *handle)\r
+{\r
+    sfree(handle);\r
+}\r
+\r
+void aes128_key(void *handle, unsigned char *key)\r
+{\r
+    AESContext *ctx = (AESContext *)handle;\r
+    aes_setup(ctx, 16, key, 16);\r
+}\r
+\r
+void aes192_key(void *handle, unsigned char *key)\r
+{\r
+    AESContext *ctx = (AESContext *)handle;\r
+    aes_setup(ctx, 16, key, 24);\r
+}\r
+\r
+void aes256_key(void *handle, unsigned char *key)\r
+{\r
+    AESContext *ctx = (AESContext *)handle;\r
+    aes_setup(ctx, 16, key, 32);\r
+}\r
+\r
+void aes_iv(void *handle, unsigned char *iv)\r
+{\r
+    AESContext *ctx = (AESContext *)handle;\r
+    int i;\r
+    for (i = 0; i < 4; i++)\r
+       ctx->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i);\r
+}\r
+\r
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    AESContext *ctx = (AESContext *)handle;\r
+    aes_encrypt_cbc(blk, len, ctx);\r
+}\r
+\r
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    AESContext *ctx = (AESContext *)handle;\r
+    aes_decrypt_cbc(blk, len, ctx);\r
+}\r
+\r
+static void aes_ssh2_sdctr(void *handle, unsigned char *blk, int len)\r
+{\r
+    AESContext *ctx = (AESContext *)handle;\r
+    aes_sdctr(blk, len, ctx);\r
+}\r
+\r
+void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len)\r
+{\r
+    AESContext ctx;\r
+    aes_setup(&ctx, 16, key, 32);\r
+    memset(ctx.iv, 0, sizeof(ctx.iv));\r
+    aes_encrypt_cbc(blk, len, &ctx);\r
+    memset(&ctx, 0, sizeof(ctx));\r
+}\r
+\r
+void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len)\r
+{\r
+    AESContext ctx;\r
+    aes_setup(&ctx, 16, key, 32);\r
+    memset(ctx.iv, 0, sizeof(ctx.iv));\r
+    aes_decrypt_cbc(blk, len, &ctx);\r
+    memset(&ctx, 0, sizeof(ctx));\r
+}\r
+\r
+static const struct ssh2_cipher ssh_aes128_ctr = {\r
+    aes_make_context, aes_free_context, aes_iv, aes128_key,\r
+    aes_ssh2_sdctr, aes_ssh2_sdctr,\r
+    "aes128-ctr",\r
+    16, 128, 0, "AES-128 SDCTR"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_aes192_ctr = {\r
+    aes_make_context, aes_free_context, aes_iv, aes192_key,\r
+    aes_ssh2_sdctr, aes_ssh2_sdctr,\r
+    "aes192-ctr",\r
+    16, 192, 0, "AES-192 SDCTR"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_aes256_ctr = {\r
+    aes_make_context, aes_free_context, aes_iv, aes256_key,\r
+    aes_ssh2_sdctr, aes_ssh2_sdctr,\r
+    "aes256-ctr",\r
+    16, 256, 0, "AES-256 SDCTR"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_aes128 = {\r
+    aes_make_context, aes_free_context, aes_iv, aes128_key,\r
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,\r
+    "aes128-cbc",\r
+    16, 128, SSH_CIPHER_IS_CBC, "AES-128 CBC"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_aes192 = {\r
+    aes_make_context, aes_free_context, aes_iv, aes192_key,\r
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,\r
+    "aes192-cbc",\r
+    16, 192, SSH_CIPHER_IS_CBC, "AES-192 CBC"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_aes256 = {\r
+    aes_make_context, aes_free_context, aes_iv, aes256_key,\r
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,\r
+    "aes256-cbc",\r
+    16, 256, SSH_CIPHER_IS_CBC, "AES-256 CBC"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_rijndael_lysator = {\r
+    aes_make_context, aes_free_context, aes_iv, aes256_key,\r
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,\r
+    "rijndael-cbc@lysator.liu.se",\r
+    16, 256, SSH_CIPHER_IS_CBC, "AES-256 CBC"\r
+};\r
+\r
+static const struct ssh2_cipher *const aes_list[] = {\r
+    &ssh_aes256_ctr,\r
+    &ssh_aes256,\r
+    &ssh_rijndael_lysator,\r
+    &ssh_aes192_ctr,\r
+    &ssh_aes192,\r
+    &ssh_aes128_ctr,\r
+    &ssh_aes128,\r
+};\r
+\r
+const struct ssh2_ciphers ssh2_aes = {\r
+    sizeof(aes_list) / sizeof(*aes_list),\r
+    aes_list\r
+};\r
diff --git a/putty/SSHARCF.C b/putty/SSHARCF.C
new file mode 100644 (file)
index 0000000..e0b247e
--- /dev/null
@@ -0,0 +1,123 @@
+/*\r
+ * Arcfour (RC4) implementation for PuTTY.\r
+ *\r
+ * Coded from Schneier.\r
+ */\r
+\r
+#include <assert.h>\r
+#include "ssh.h"\r
+\r
+typedef struct {\r
+    unsigned char i, j, s[256];\r
+} ArcfourContext;\r
+\r
+static void arcfour_block(void *handle, unsigned char *blk, int len)\r
+{\r
+    ArcfourContext *ctx = (ArcfourContext *)handle;\r
+    unsigned k;\r
+    unsigned char tmp, i, j, *s;\r
+\r
+    s = ctx->s;\r
+    i = ctx->i; j = ctx->j;\r
+    for (k = 0; (int)k < len; k++) {\r
+       i  = (i + 1) & 0xff;\r
+       j  = (j + s[i]) & 0xff;\r
+       tmp = s[i]; s[i] = s[j]; s[j] = tmp;\r
+       blk[k] ^= s[(s[i]+s[j]) & 0xff];\r
+    }\r
+    ctx->i = i; ctx->j = j;\r
+}\r
+\r
+static void arcfour_setkey(ArcfourContext *ctx, unsigned char const *key,\r
+                          unsigned keybytes)\r
+{\r
+    unsigned char tmp, k[256], *s;\r
+    unsigned i, j;\r
+\r
+    s = ctx->s;\r
+    assert(keybytes <= 256);\r
+    ctx->i = ctx->j = 0;\r
+    for (i = 0; i < 256; i++) {\r
+       s[i] = i;\r
+       k[i] = key[i % keybytes];\r
+    }\r
+    j = 0;\r
+    for (i = 0; i < 256; i++) {\r
+       j = (j + s[i] + k[i]) & 0xff;\r
+       tmp = s[i]; s[i] = s[j]; s[j] = tmp;\r
+    }\r
+}\r
+\r
+/* -- Interface with PuTTY -- */\r
+\r
+/*\r
+ * We don't implement Arcfour in SSH-1 because it's utterly insecure in\r
+ * several ways.  See CERT Vulnerability Notes VU#25309, VU#665372,\r
+ * and VU#565052.\r
+ * \r
+ * We don't implement the "arcfour" algorithm in SSH-2 because it doesn't\r
+ * stir the cipher state before emitting keystream, and hence is likely\r
+ * to leak data about the key.\r
+ */\r
+\r
+static void *arcfour_make_context(void)\r
+{\r
+    return snew(ArcfourContext);\r
+}\r
+\r
+static void arcfour_free_context(void *handle)\r
+{\r
+    sfree(handle);\r
+}\r
+\r
+static void arcfour_stir(ArcfourContext *ctx)\r
+{\r
+    unsigned char *junk = snewn(1536, unsigned char);\r
+    memset(junk, 0, 1536);\r
+    arcfour_block(ctx, junk, 1536);\r
+    memset(junk, 0, 1536);\r
+    sfree(junk);\r
+}\r
+\r
+static void arcfour128_key(void *handle, unsigned char *key)\r
+{\r
+    ArcfourContext *ctx = (ArcfourContext *)handle;\r
+    arcfour_setkey(ctx, key, 16);\r
+    arcfour_stir(ctx);\r
+}\r
+\r
+static void arcfour256_key(void *handle, unsigned char *key)\r
+{\r
+    ArcfourContext *ctx = (ArcfourContext *)handle;\r
+    arcfour_setkey(ctx, key, 32);\r
+    arcfour_stir(ctx);\r
+}\r
+\r
+static void arcfour_iv(void *handle, unsigned char *key)\r
+{\r
+\r
+}\r
+\r
+const struct ssh2_cipher ssh_arcfour128_ssh2 = {\r
+    arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour128_key,\r
+    arcfour_block, arcfour_block,\r
+    "arcfour128",\r
+    1, 128, 0, "Arcfour-128"\r
+};\r
+\r
+const struct ssh2_cipher ssh_arcfour256_ssh2 = {\r
+    arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour256_key,\r
+    arcfour_block, arcfour_block,\r
+    "arcfour256",\r
+    1, 256, 0, "Arcfour-256"\r
+};\r
+\r
+static const struct ssh2_cipher *const arcfour_list[] = {\r
+    &ssh_arcfour256_ssh2,\r
+    &ssh_arcfour128_ssh2,\r
+};\r
+\r
+const struct ssh2_ciphers ssh2_arcfour = {\r
+    sizeof(arcfour_list) / sizeof(*arcfour_list),\r
+    arcfour_list\r
+};\r
diff --git a/putty/SSHBLOWF.C b/putty/SSHBLOWF.C
new file mode 100644 (file)
index 0000000..f770170
--- /dev/null
@@ -0,0 +1,588 @@
+/*\r
+ * Blowfish implementation for PuTTY.\r
+ *\r
+ * Coded from scratch from the algorithm description.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+#include "ssh.h"\r
+\r
+typedef struct {\r
+    word32 S0[256], S1[256], S2[256], S3[256], P[18];\r
+    word32 iv0, iv1;                  /* for CBC mode */\r
+} BlowfishContext;\r
+\r
+/*\r
+ * The Blowfish init data: hex digits of the fractional part of pi.\r
+ * (ie pi as a hex fraction is 3.243F6A8885A308D3...)\r
+ */\r
+static const word32 parray[] = {\r
+    0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,\r
+    0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,\r
+    0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B,\r
+};\r
+\r
+static const word32 sbox0[] = {\r
+    0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96,\r
+    0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,\r
+    0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,\r
+    0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,\r
+    0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E,\r
+    0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,\r
+    0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6,\r
+    0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,\r
+    0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,\r
+    0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,\r
+    0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1,\r
+    0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,\r
+    0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A,\r
+    0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,\r
+    0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,\r
+    0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,\r
+    0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706,\r
+    0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,\r
+    0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B,\r
+    0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,\r
+    0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,\r
+    0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,\r
+    0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A,\r
+    0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,\r
+    0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760,\r
+    0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,\r
+    0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,\r
+    0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,\r
+    0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33,\r
+    0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,\r
+    0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0,\r
+    0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,\r
+    0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,\r
+    0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,\r
+    0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705,\r
+    0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,\r
+    0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E,\r
+    0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,\r
+    0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,\r
+    0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,\r
+    0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F,\r
+    0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,\r
+    0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A,\r
+};\r
+\r
+static const word32 sbox1[] = {\r
+    0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D,\r
+    0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,\r
+    0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65,\r
+    0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,\r
+    0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9,\r
+    0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,\r
+    0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D,\r
+    0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,\r
+    0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC,\r
+    0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,\r
+    0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908,\r
+    0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,\r
+    0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124,\r
+    0x501ADDE6, 0x9F84CD87, 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,\r
+    0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908,\r
+    0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,\r
+    0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B,\r
+    0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,\r
+    0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA,\r
+    0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,\r
+    0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D,\r
+    0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,\r
+    0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5,\r
+    0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,\r
+    0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96,\r
+    0x0334FE1E, 0xAA0363CF, 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,\r
+    0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA,\r
+    0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,\r
+    0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77,\r
+    0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,\r
+    0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054,\r
+    0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,\r
+    0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA,\r
+    0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,\r
+    0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646,\r
+    0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,\r
+    0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA,\r
+    0x1DADF43E, 0x233F7061, 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,\r
+    0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E,\r
+    0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,\r
+    0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD,\r
+    0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,\r
+    0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7,\r
+};\r
+\r
+static const word32 sbox2[] = {\r
+    0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7,\r
+    0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,\r
+    0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF,\r
+    0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,\r
+    0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4,\r
+    0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,\r
+    0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC,\r
+    0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,\r
+    0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332,\r
+    0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,\r
+    0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, 0x55A867BC, 0xA1159A58,\r
+    0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,\r
+    0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22,\r
+    0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,\r
+    0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60,\r
+    0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,\r
+    0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99,\r
+    0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,\r
+    0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74,\r
+    0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,\r
+    0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3,\r
+    0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,\r
+    0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, 0x37392EB3, 0xCC115979,\r
+    0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,\r
+    0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA,\r
+    0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,\r
+    0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086,\r
+    0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,\r
+    0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24,\r
+    0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,\r
+    0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84,\r
+    0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,\r
+    0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09,\r
+    0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,\r
+    0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, 0xDCB7DA83, 0x573906FE,\r
+    0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,\r
+    0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0,\r
+    0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,\r
+    0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188,\r
+    0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,\r
+    0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8,\r
+    0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,\r
+    0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0,\r
+};\r
+\r
+static const word32 sbox3[] = {\r
+    0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742,\r
+    0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,\r
+    0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,\r
+    0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,\r
+    0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A,\r
+    0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,\r
+    0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1,\r
+    0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,\r
+    0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,\r
+    0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,\r
+    0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6,\r
+    0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,\r
+    0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA,\r
+    0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,\r
+    0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,\r
+    0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,\r
+    0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE,\r
+    0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,\r
+    0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD,\r
+    0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,\r
+    0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,\r
+    0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,\r
+    0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC,\r
+    0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,\r
+    0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC,\r
+    0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,\r
+    0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,\r
+    0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,\r
+    0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A,\r
+    0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,\r
+    0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B,\r
+    0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,\r
+    0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,\r
+    0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,\r
+    0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623,\r
+    0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,\r
+    0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A,\r
+    0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,\r
+    0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,\r
+    0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,\r
+    0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C,\r
+    0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,\r
+    0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6,\r
+};\r
+\r
+#define Fprime(a,b,c,d) ( ( (S0[a] + S1[b]) ^ S2[c] ) + S3[d] )\r
+#define F(x) Fprime( ((x>>24)&0xFF), ((x>>16)&0xFF), ((x>>8)&0xFF), (x&0xFF) )\r
+#define ROUND(n) ( xL ^= P[n], t = xL, xL = F(xL) ^ xR, xR = t )\r
+\r
+static void blowfish_encrypt(word32 xL, word32 xR, word32 * output,\r
+                            BlowfishContext * ctx)\r
+{\r
+    word32 *S0 = ctx->S0;\r
+    word32 *S1 = ctx->S1;\r
+    word32 *S2 = ctx->S2;\r
+    word32 *S3 = ctx->S3;\r
+    word32 *P = ctx->P;\r
+    word32 t;\r
+\r
+    ROUND(0);\r
+    ROUND(1);\r
+    ROUND(2);\r
+    ROUND(3);\r
+    ROUND(4);\r
+    ROUND(5);\r
+    ROUND(6);\r
+    ROUND(7);\r
+    ROUND(8);\r
+    ROUND(9);\r
+    ROUND(10);\r
+    ROUND(11);\r
+    ROUND(12);\r
+    ROUND(13);\r
+    ROUND(14);\r
+    ROUND(15);\r
+    xL ^= P[16];\r
+    xR ^= P[17];\r
+\r
+    output[0] = xR;\r
+    output[1] = xL;\r
+}\r
+\r
+static void blowfish_decrypt(word32 xL, word32 xR, word32 * output,\r
+                            BlowfishContext * ctx)\r
+{\r
+    word32 *S0 = ctx->S0;\r
+    word32 *S1 = ctx->S1;\r
+    word32 *S2 = ctx->S2;\r
+    word32 *S3 = ctx->S3;\r
+    word32 *P = ctx->P;\r
+    word32 t;\r
+\r
+    ROUND(17);\r
+    ROUND(16);\r
+    ROUND(15);\r
+    ROUND(14);\r
+    ROUND(13);\r
+    ROUND(12);\r
+    ROUND(11);\r
+    ROUND(10);\r
+    ROUND(9);\r
+    ROUND(8);\r
+    ROUND(7);\r
+    ROUND(6);\r
+    ROUND(5);\r
+    ROUND(4);\r
+    ROUND(3);\r
+    ROUND(2);\r
+    xL ^= P[1];\r
+    xR ^= P[0];\r
+\r
+    output[0] = xR;\r
+    output[1] = xL;\r
+}\r
+\r
+static void blowfish_lsb_encrypt_cbc(unsigned char *blk, int len,\r
+                                    BlowfishContext * ctx)\r
+{\r
+    word32 xL, xR, out[2], iv0, iv1;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = ctx->iv0;\r
+    iv1 = ctx->iv1;\r
+\r
+    while (len > 0) {\r
+       xL = GET_32BIT_LSB_FIRST(blk);\r
+       xR = GET_32BIT_LSB_FIRST(blk + 4);\r
+       iv0 ^= xL;\r
+       iv1 ^= xR;\r
+       blowfish_encrypt(iv0, iv1, out, ctx);\r
+       iv0 = out[0];\r
+       iv1 = out[1];\r
+       PUT_32BIT_LSB_FIRST(blk, iv0);\r
+       PUT_32BIT_LSB_FIRST(blk + 4, iv1);\r
+       blk += 8;\r
+       len -= 8;\r
+    }\r
+\r
+    ctx->iv0 = iv0;\r
+    ctx->iv1 = iv1;\r
+}\r
+\r
+static void blowfish_lsb_decrypt_cbc(unsigned char *blk, int len,\r
+                                    BlowfishContext * ctx)\r
+{\r
+    word32 xL, xR, out[2], iv0, iv1;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = ctx->iv0;\r
+    iv1 = ctx->iv1;\r
+\r
+    while (len > 0) {\r
+       xL = GET_32BIT_LSB_FIRST(blk);\r
+       xR = GET_32BIT_LSB_FIRST(blk + 4);\r
+       blowfish_decrypt(xL, xR, out, ctx);\r
+       iv0 ^= out[0];\r
+       iv1 ^= out[1];\r
+       PUT_32BIT_LSB_FIRST(blk, iv0);\r
+       PUT_32BIT_LSB_FIRST(blk + 4, iv1);\r
+       iv0 = xL;\r
+       iv1 = xR;\r
+       blk += 8;\r
+       len -= 8;\r
+    }\r
+\r
+    ctx->iv0 = iv0;\r
+    ctx->iv1 = iv1;\r
+}\r
+\r
+static void blowfish_msb_encrypt_cbc(unsigned char *blk, int len,\r
+                                    BlowfishContext * ctx)\r
+{\r
+    word32 xL, xR, out[2], iv0, iv1;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = ctx->iv0;\r
+    iv1 = ctx->iv1;\r
+\r
+    while (len > 0) {\r
+       xL = GET_32BIT_MSB_FIRST(blk);\r
+       xR = GET_32BIT_MSB_FIRST(blk + 4);\r
+       iv0 ^= xL;\r
+       iv1 ^= xR;\r
+       blowfish_encrypt(iv0, iv1, out, ctx);\r
+       iv0 = out[0];\r
+       iv1 = out[1];\r
+       PUT_32BIT_MSB_FIRST(blk, iv0);\r
+       PUT_32BIT_MSB_FIRST(blk + 4, iv1);\r
+       blk += 8;\r
+       len -= 8;\r
+    }\r
+\r
+    ctx->iv0 = iv0;\r
+    ctx->iv1 = iv1;\r
+}\r
+\r
+static void blowfish_msb_decrypt_cbc(unsigned char *blk, int len,\r
+                                    BlowfishContext * ctx)\r
+{\r
+    word32 xL, xR, out[2], iv0, iv1;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = ctx->iv0;\r
+    iv1 = ctx->iv1;\r
+\r
+    while (len > 0) {\r
+       xL = GET_32BIT_MSB_FIRST(blk);\r
+       xR = GET_32BIT_MSB_FIRST(blk + 4);\r
+       blowfish_decrypt(xL, xR, out, ctx);\r
+       iv0 ^= out[0];\r
+       iv1 ^= out[1];\r
+       PUT_32BIT_MSB_FIRST(blk, iv0);\r
+       PUT_32BIT_MSB_FIRST(blk + 4, iv1);\r
+       iv0 = xL;\r
+       iv1 = xR;\r
+       blk += 8;\r
+       len -= 8;\r
+    }\r
+\r
+    ctx->iv0 = iv0;\r
+    ctx->iv1 = iv1;\r
+}\r
+\r
+static void blowfish_msb_sdctr(unsigned char *blk, int len,\r
+                                    BlowfishContext * ctx)\r
+{\r
+    word32 b[2], iv0, iv1, tmp;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = ctx->iv0;\r
+    iv1 = ctx->iv1;\r
+\r
+    while (len > 0) {\r
+       blowfish_encrypt(iv0, iv1, b, ctx);\r
+       tmp = GET_32BIT_MSB_FIRST(blk);\r
+       PUT_32BIT_MSB_FIRST(blk, tmp ^ b[0]);\r
+       tmp = GET_32BIT_MSB_FIRST(blk + 4);\r
+       PUT_32BIT_MSB_FIRST(blk + 4, tmp ^ b[1]);\r
+       if ((iv1 = (iv1 + 1) & 0xffffffff) == 0)\r
+           iv0 = (iv0 + 1) & 0xffffffff;\r
+       blk += 8;\r
+       len -= 8;\r
+    }\r
+\r
+    ctx->iv0 = iv0;\r
+    ctx->iv1 = iv1;\r
+}\r
+\r
+static void blowfish_setkey(BlowfishContext * ctx,\r
+                           const unsigned char *key, short keybytes)\r
+{\r
+    word32 *S0 = ctx->S0;\r
+    word32 *S1 = ctx->S1;\r
+    word32 *S2 = ctx->S2;\r
+    word32 *S3 = ctx->S3;\r
+    word32 *P = ctx->P;\r
+    word32 str[2];\r
+    int i;\r
+\r
+    for (i = 0; i < 18; i++) {\r
+       P[i] = parray[i];\r
+       P[i] ^=\r
+           ((word32) (unsigned char) (key[(i * 4 + 0) % keybytes])) << 24;\r
+       P[i] ^=\r
+           ((word32) (unsigned char) (key[(i * 4 + 1) % keybytes])) << 16;\r
+       P[i] ^=\r
+           ((word32) (unsigned char) (key[(i * 4 + 2) % keybytes])) << 8;\r
+       P[i] ^= ((word32) (unsigned char) (key[(i * 4 + 3) % keybytes]));\r
+    }\r
+\r
+    for (i = 0; i < 256; i++) {\r
+       S0[i] = sbox0[i];\r
+       S1[i] = sbox1[i];\r
+       S2[i] = sbox2[i];\r
+       S3[i] = sbox3[i];\r
+    }\r
+\r
+    str[0] = str[1] = 0;\r
+\r
+    for (i = 0; i < 18; i += 2) {\r
+       blowfish_encrypt(str[0], str[1], str, ctx);\r
+       P[i] = str[0];\r
+       P[i + 1] = str[1];\r
+    }\r
+\r
+    for (i = 0; i < 256; i += 2) {\r
+       blowfish_encrypt(str[0], str[1], str, ctx);\r
+       S0[i] = str[0];\r
+       S0[i + 1] = str[1];\r
+    }\r
+    for (i = 0; i < 256; i += 2) {\r
+       blowfish_encrypt(str[0], str[1], str, ctx);\r
+       S1[i] = str[0];\r
+       S1[i + 1] = str[1];\r
+    }\r
+    for (i = 0; i < 256; i += 2) {\r
+       blowfish_encrypt(str[0], str[1], str, ctx);\r
+       S2[i] = str[0];\r
+       S2[i + 1] = str[1];\r
+    }\r
+    for (i = 0; i < 256; i += 2) {\r
+       blowfish_encrypt(str[0], str[1], str, ctx);\r
+       S3[i] = str[0];\r
+       S3[i + 1] = str[1];\r
+    }\r
+}\r
+\r
+/* -- Interface with PuTTY -- */\r
+\r
+#define SSH_SESSION_KEY_LENGTH 32\r
+\r
+static void *blowfish_make_context(void)\r
+{\r
+    return snew(BlowfishContext);\r
+}\r
+\r
+static void *blowfish_ssh1_make_context(void)\r
+{\r
+    /* In SSH-1, need one key for each direction */\r
+    return snewn(2, BlowfishContext);\r
+}\r
+\r
+static void blowfish_free_context(void *handle)\r
+{\r
+    sfree(handle);\r
+}\r
+\r
+static void blowfish_key(void *handle, unsigned char *key)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_setkey(ctx, key, 16);\r
+}\r
+\r
+static void blowfish256_key(void *handle, unsigned char *key)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_setkey(ctx, key, 32);\r
+}\r
+\r
+static void blowfish_iv(void *handle, unsigned char *key)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    ctx->iv0 = GET_32BIT_MSB_FIRST(key);\r
+    ctx->iv1 = GET_32BIT_MSB_FIRST(key + 4);\r
+}\r
+\r
+static void blowfish_sesskey(void *handle, unsigned char *key)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_setkey(ctx, key, SSH_SESSION_KEY_LENGTH);\r
+    ctx->iv0 = 0;\r
+    ctx->iv1 = 0;\r
+    ctx[1] = ctx[0];                  /* structure copy */\r
+}\r
+\r
+static void blowfish_ssh1_encrypt_blk(void *handle, unsigned char *blk,\r
+                                     int len)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_lsb_encrypt_cbc(blk, len, ctx);\r
+}\r
+\r
+static void blowfish_ssh1_decrypt_blk(void *handle, unsigned char *blk,\r
+                                     int len)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_lsb_decrypt_cbc(blk, len, ctx+1);\r
+}\r
+\r
+static void blowfish_ssh2_encrypt_blk(void *handle, unsigned char *blk,\r
+                                     int len)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_msb_encrypt_cbc(blk, len, ctx);\r
+}\r
+\r
+static void blowfish_ssh2_decrypt_blk(void *handle, unsigned char *blk,\r
+                                     int len)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_msb_decrypt_cbc(blk, len, ctx);\r
+}\r
+\r
+static void blowfish_ssh2_sdctr(void *handle, unsigned char *blk,\r
+                                     int len)\r
+{\r
+    BlowfishContext *ctx = (BlowfishContext *)handle;\r
+    blowfish_msb_sdctr(blk, len, ctx);\r
+}\r
+\r
+const struct ssh_cipher ssh_blowfish_ssh1 = {\r
+    blowfish_ssh1_make_context, blowfish_free_context, blowfish_sesskey,\r
+    blowfish_ssh1_encrypt_blk, blowfish_ssh1_decrypt_blk,\r
+    8, "Blowfish-128 CBC"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_blowfish_ssh2 = {\r
+    blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish_key,\r
+    blowfish_ssh2_encrypt_blk, blowfish_ssh2_decrypt_blk,\r
+    "blowfish-cbc",\r
+    8, 128, SSH_CIPHER_IS_CBC, "Blowfish-128 CBC"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_blowfish_ssh2_ctr = {\r
+    blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish256_key,\r
+    blowfish_ssh2_sdctr, blowfish_ssh2_sdctr,\r
+    "blowfish-ctr",\r
+    8, 256, 0, "Blowfish-256 SDCTR"\r
+};\r
+\r
+static const struct ssh2_cipher *const blowfish_list[] = {\r
+    &ssh_blowfish_ssh2_ctr,\r
+    &ssh_blowfish_ssh2\r
+};\r
+\r
+const struct ssh2_ciphers ssh2_blowfish = {\r
+    sizeof(blowfish_list) / sizeof(*blowfish_list),\r
+    blowfish_list\r
+};\r
diff --git a/putty/SSHBN.C b/putty/SSHBN.C
new file mode 100644 (file)
index 0000000..51cecdf
--- /dev/null
@@ -0,0 +1,1918 @@
+/*\r
+ * Bignum routines for RSA and DH and stuff.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include "misc.h"\r
+\r
+/*\r
+ * Usage notes:\r
+ *  * Do not call the DIVMOD_WORD macro with expressions such as array\r
+ *    subscripts, as some implementations object to this (see below).\r
+ *  * Note that none of the division methods below will cope if the\r
+ *    quotient won't fit into BIGNUM_INT_BITS. Callers should be careful\r
+ *    to avoid this case.\r
+ *    If this condition occurs, in the case of the x86 DIV instruction,\r
+ *    an overflow exception will occur, which (according to a correspondent)\r
+ *    will manifest on Windows as something like\r
+ *      0xC0000095: Integer overflow\r
+ *    The C variant won't give the right answer, either.\r
+ */\r
+\r
+#if defined __GNUC__ && defined __i386__\r
+typedef unsigned long BignumInt;\r
+typedef unsigned long long BignumDblInt;\r
+#define BIGNUM_INT_MASK  0xFFFFFFFFUL\r
+#define BIGNUM_TOP_BIT   0x80000000UL\r
+#define BIGNUM_INT_BITS  32\r
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)\r
+#define DIVMOD_WORD(q, r, hi, lo, w) \\r
+    __asm__("div %2" : \\r
+           "=d" (r), "=a" (q) : \\r
+           "r" (w), "d" (hi), "a" (lo))\r
+#elif defined _MSC_VER && defined _M_IX86\r
+typedef unsigned __int32 BignumInt;\r
+typedef unsigned __int64 BignumDblInt;\r
+#define BIGNUM_INT_MASK  0xFFFFFFFFUL\r
+#define BIGNUM_TOP_BIT   0x80000000UL\r
+#define BIGNUM_INT_BITS  32\r
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)\r
+/* Note: MASM interprets array subscripts in the macro arguments as\r
+ * assembler syntax, which gives the wrong answer. Don't supply them.\r
+ * <http://msdn2.microsoft.com/en-us/library/bf1dw62z.aspx> */\r
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \\r
+    __asm mov edx, hi \\r
+    __asm mov eax, lo \\r
+    __asm div w \\r
+    __asm mov r, edx \\r
+    __asm mov q, eax \\r
+} while(0)\r
+#elif defined _LP64\r
+/* 64-bit architectures can do 32x32->64 chunks at a time */\r
+typedef unsigned int BignumInt;\r
+typedef unsigned long BignumDblInt;\r
+#define BIGNUM_INT_MASK  0xFFFFFFFFU\r
+#define BIGNUM_TOP_BIT   0x80000000U\r
+#define BIGNUM_INT_BITS  32\r
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)\r
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \\r
+    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \\r
+    q = n / w; \\r
+    r = n % w; \\r
+} while (0)\r
+#elif defined _LLP64\r
+/* 64-bit architectures in which unsigned long is 32 bits, not 64 */\r
+typedef unsigned long BignumInt;\r
+typedef unsigned long long BignumDblInt;\r
+#define BIGNUM_INT_MASK  0xFFFFFFFFUL\r
+#define BIGNUM_TOP_BIT   0x80000000UL\r
+#define BIGNUM_INT_BITS  32\r
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)\r
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \\r
+    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \\r
+    q = n / w; \\r
+    r = n % w; \\r
+} while (0)\r
+#else\r
+/* Fallback for all other cases */\r
+typedef unsigned short BignumInt;\r
+typedef unsigned long BignumDblInt;\r
+#define BIGNUM_INT_MASK  0xFFFFU\r
+#define BIGNUM_TOP_BIT   0x8000U\r
+#define BIGNUM_INT_BITS  16\r
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)\r
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \\r
+    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \\r
+    q = n / w; \\r
+    r = n % w; \\r
+} while (0)\r
+#endif\r
+\r
+#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)\r
+\r
+#define BIGNUM_INTERNAL\r
+typedef BignumInt *Bignum;\r
+\r
+#include "ssh.h"\r
+\r
+BignumInt bnZero[1] = { 0 };\r
+BignumInt bnOne[2] = { 1, 1 };\r
+\r
+/*\r
+ * The Bignum format is an array of `BignumInt'. The first\r
+ * element of the array counts the remaining elements. The\r
+ * remaining elements express the actual number, base 2^BIGNUM_INT_BITS, _least_\r
+ * significant digit first. (So it's trivial to extract the bit\r
+ * with value 2^n for any n.)\r
+ *\r
+ * All Bignums in this module are positive. Negative numbers must\r
+ * be dealt with outside it.\r
+ *\r
+ * INVARIANT: the most significant word of any Bignum must be\r
+ * nonzero.\r
+ */\r
+\r
+Bignum Zero = bnZero, One = bnOne;\r
+\r
+static Bignum newbn(int length)\r
+{\r
+    Bignum b = snewn(length + 1, BignumInt);\r
+    if (!b)\r
+       abort();                       /* FIXME */\r
+    memset(b, 0, (length + 1) * sizeof(*b));\r
+    b[0] = length;\r
+    return b;\r
+}\r
+\r
+void bn_restore_invariant(Bignum b)\r
+{\r
+    while (b[0] > 1 && b[b[0]] == 0)\r
+       b[0]--;\r
+}\r
+\r
+Bignum copybn(Bignum orig)\r
+{\r
+    Bignum b = snewn(orig[0] + 1, BignumInt);\r
+    if (!b)\r
+       abort();                       /* FIXME */\r
+    memcpy(b, orig, (orig[0] + 1) * sizeof(*b));\r
+    return b;\r
+}\r
+\r
+void freebn(Bignum b)\r
+{\r
+    /*\r
+     * Burn the evidence, just in case.\r
+     */\r
+    memset(b, 0, sizeof(b[0]) * (b[0] + 1));\r
+    sfree(b);\r
+}\r
+\r
+Bignum bn_power_2(int n)\r
+{\r
+    Bignum ret = newbn(n / BIGNUM_INT_BITS + 1);\r
+    bignum_set_bit(ret, n, 1);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Internal addition. Sets c = a - b, where 'a', 'b' and 'c' are all\r
+ * big-endian arrays of 'len' BignumInts. Returns a BignumInt carried\r
+ * off the top.\r
+ */\r
+static BignumInt internal_add(const BignumInt *a, const BignumInt *b,\r
+                              BignumInt *c, int len)\r
+{\r
+    int i;\r
+    BignumDblInt carry = 0;\r
+\r
+    for (i = len-1; i >= 0; i--) {\r
+        carry += (BignumDblInt)a[i] + b[i];\r
+        c[i] = (BignumInt)carry;\r
+        carry >>= BIGNUM_INT_BITS;\r
+    }\r
+\r
+    return (BignumInt)carry;\r
+}\r
+\r
+/*\r
+ * Internal subtraction. Sets c = a - b, where 'a', 'b' and 'c' are\r
+ * all big-endian arrays of 'len' BignumInts. Any borrow from the top\r
+ * is ignored.\r
+ */\r
+static void internal_sub(const BignumInt *a, const BignumInt *b,\r
+                         BignumInt *c, int len)\r
+{\r
+    int i;\r
+    BignumDblInt carry = 1;\r
+\r
+    for (i = len-1; i >= 0; i--) {\r
+        carry += (BignumDblInt)a[i] + (b[i] ^ BIGNUM_INT_MASK);\r
+        c[i] = (BignumInt)carry;\r
+        carry >>= BIGNUM_INT_BITS;\r
+    }\r
+}\r
+\r
+/*\r
+ * Compute c = a * b.\r
+ * Input is in the first len words of a and b.\r
+ * Result is returned in the first 2*len words of c.\r
+ *\r
+ * 'scratch' must point to an array of BignumInt of size at least\r
+ * mul_compute_scratch(len). (This covers the needs of internal_mul\r
+ * and all its recursive calls to itself.)\r
+ */\r
+#define KARATSUBA_THRESHOLD 50\r
+static int mul_compute_scratch(int len)\r
+{\r
+    int ret = 0;\r
+    while (len > KARATSUBA_THRESHOLD) {\r
+        int toplen = len/2, botlen = len - toplen; /* botlen is the bigger */\r
+        int midlen = botlen + 1;\r
+        ret += 4*midlen;\r
+        len = midlen;\r
+    }\r
+    return ret;\r
+}\r
+static void internal_mul(const BignumInt *a, const BignumInt *b,\r
+                        BignumInt *c, int len, BignumInt *scratch)\r
+{\r
+    if (len > KARATSUBA_THRESHOLD) {\r
+        int i;\r
+\r
+        /*\r
+         * Karatsuba divide-and-conquer algorithm. Cut each input in\r
+         * half, so that it's expressed as two big 'digits' in a giant\r
+         * base D:\r
+         *\r
+         *   a = a_1 D + a_0\r
+         *   b = b_1 D + b_0\r
+         *\r
+         * Then the product is of course\r
+         *\r
+         *  ab = a_1 b_1 D^2 + (a_1 b_0 + a_0 b_1) D + a_0 b_0\r
+         *\r
+         * and we compute the three coefficients by recursively\r
+         * calling ourself to do half-length multiplications.\r
+         *\r
+         * The clever bit that makes this worth doing is that we only\r
+         * need _one_ half-length multiplication for the central\r
+         * coefficient rather than the two that it obviouly looks\r
+         * like, because we can use a single multiplication to compute\r
+         *\r
+         *   (a_1 + a_0) (b_1 + b_0) = a_1 b_1 + a_1 b_0 + a_0 b_1 + a_0 b_0\r
+         *\r
+         * and then we subtract the other two coefficients (a_1 b_1\r
+         * and a_0 b_0) which we were computing anyway.\r
+         *\r
+         * Hence we get to multiply two numbers of length N in about\r
+         * three times as much work as it takes to multiply numbers of\r
+         * length N/2, which is obviously better than the four times\r
+         * as much work it would take if we just did a long\r
+         * conventional multiply.\r
+         */\r
+\r
+        int toplen = len/2, botlen = len - toplen; /* botlen is the bigger */\r
+        int midlen = botlen + 1;\r
+        BignumDblInt carry;\r
+#ifdef KARA_DEBUG\r
+        int i;\r
+#endif\r
+\r
+        /*\r
+         * The coefficients a_1 b_1 and a_0 b_0 just avoid overlapping\r
+         * in the output array, so we can compute them immediately in\r
+         * place.\r
+         */\r
+\r
+#ifdef KARA_DEBUG\r
+        printf("a1,a0 = 0x");\r
+        for (i = 0; i < len; i++) {\r
+            if (i == toplen) printf(", 0x");\r
+            printf("%0*x", BIGNUM_INT_BITS/4, a[i]);\r
+        }\r
+        printf("\n");\r
+        printf("b1,b0 = 0x");\r
+        for (i = 0; i < len; i++) {\r
+            if (i == toplen) printf(", 0x");\r
+            printf("%0*x", BIGNUM_INT_BITS/4, b[i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+        /* a_1 b_1 */\r
+        internal_mul(a, b, c, toplen, scratch);\r
+#ifdef KARA_DEBUG\r
+        printf("a1b1 = 0x");\r
+        for (i = 0; i < 2*toplen; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, c[i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+        /* a_0 b_0 */\r
+        internal_mul(a + toplen, b + toplen, c + 2*toplen, botlen, scratch);\r
+#ifdef KARA_DEBUG\r
+        printf("a0b0 = 0x");\r
+        for (i = 0; i < 2*botlen; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, c[2*toplen+i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+        /* Zero padding. midlen exceeds toplen by at most 2, so just\r
+         * zero the first two words of each input and the rest will be\r
+         * copied over. */\r
+        scratch[0] = scratch[1] = scratch[midlen] = scratch[midlen+1] = 0;\r
+\r
+        for (i = 0; i < toplen; i++) {\r
+            scratch[midlen - toplen + i] = a[i]; /* a_1 */\r
+            scratch[2*midlen - toplen + i] = b[i]; /* b_1 */\r
+        }\r
+\r
+        /* compute a_1 + a_0 */\r
+        scratch[0] = internal_add(scratch+1, a+toplen, scratch+1, botlen);\r
+#ifdef KARA_DEBUG\r
+        printf("a1plusa0 = 0x");\r
+        for (i = 0; i < midlen; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+        /* compute b_1 + b_0 */\r
+        scratch[midlen] = internal_add(scratch+midlen+1, b+toplen,\r
+                                       scratch+midlen+1, botlen);\r
+#ifdef KARA_DEBUG\r
+        printf("b1plusb0 = 0x");\r
+        for (i = 0; i < midlen; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[midlen+i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+        /*\r
+         * Now we can do the third multiplication.\r
+         */\r
+        internal_mul(scratch, scratch + midlen, scratch + 2*midlen, midlen,\r
+                     scratch + 4*midlen);\r
+#ifdef KARA_DEBUG\r
+        printf("a1plusa0timesb1plusb0 = 0x");\r
+        for (i = 0; i < 2*midlen; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[2*midlen+i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+        /*\r
+         * Now we can reuse the first half of 'scratch' to compute the\r
+         * sum of the outer two coefficients, to subtract from that\r
+         * product to obtain the middle one.\r
+         */\r
+        scratch[0] = scratch[1] = scratch[2] = scratch[3] = 0;\r
+        for (i = 0; i < 2*toplen; i++)\r
+            scratch[2*midlen - 2*toplen + i] = c[i];\r
+        scratch[1] = internal_add(scratch+2, c + 2*toplen,\r
+                                  scratch+2, 2*botlen);\r
+#ifdef KARA_DEBUG\r
+        printf("a1b1plusa0b0 = 0x");\r
+        for (i = 0; i < 2*midlen; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+        internal_sub(scratch + 2*midlen, scratch,\r
+                     scratch + 2*midlen, 2*midlen);\r
+#ifdef KARA_DEBUG\r
+        printf("a1b0plusa0b1 = 0x");\r
+        for (i = 0; i < 2*midlen; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, scratch[2*midlen+i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+        /*\r
+         * And now all we need to do is to add that middle coefficient\r
+         * back into the output. We may have to propagate a carry\r
+         * further up the output, but we can be sure it won't\r
+         * propagate right the way off the top.\r
+         */\r
+        carry = internal_add(c + 2*len - botlen - 2*midlen,\r
+                             scratch + 2*midlen,\r
+                             c + 2*len - botlen - 2*midlen, 2*midlen);\r
+        i = 2*len - botlen - 2*midlen - 1;\r
+        while (carry) {\r
+            assert(i >= 0);\r
+            carry += c[i];\r
+            c[i] = (BignumInt)carry;\r
+            carry >>= BIGNUM_INT_BITS;\r
+            i--;\r
+        }\r
+#ifdef KARA_DEBUG\r
+        printf("ab = 0x");\r
+        for (i = 0; i < 2*len; i++) {\r
+            printf("%0*x", BIGNUM_INT_BITS/4, c[i]);\r
+        }\r
+        printf("\n");\r
+#endif\r
+\r
+    } else {\r
+        int i;\r
+        BignumInt carry;\r
+        BignumDblInt t;\r
+        const BignumInt *ap, *bp;\r
+        BignumInt *cp, *cps;\r
+\r
+        /*\r
+         * Multiply in the ordinary O(N^2) way.\r
+         */\r
+\r
+        for (i = 0; i < 2 * len; i++)\r
+            c[i] = 0;\r
+\r
+        for (cps = c + 2*len, ap = a + len; ap-- > a; cps--) {\r
+            carry = 0;\r
+            for (cp = cps, bp = b + len; cp--, bp-- > b ;) {\r
+                t = (MUL_WORD(*ap, *bp) + carry) + *cp;\r
+                *cp = (BignumInt) t;\r
+                carry = (BignumInt)(t >> BIGNUM_INT_BITS);\r
+            }\r
+            *cp = carry;\r
+        }\r
+    }\r
+}\r
+\r
+/*\r
+ * Variant form of internal_mul used for the initial step of\r
+ * Montgomery reduction. Only bothers outputting 'len' words\r
+ * (everything above that is thrown away).\r
+ */\r
+static void internal_mul_low(const BignumInt *a, const BignumInt *b,\r
+                             BignumInt *c, int len, BignumInt *scratch)\r
+{\r
+    if (len > KARATSUBA_THRESHOLD) {\r
+        int i;\r
+\r
+        /*\r
+         * Karatsuba-aware version of internal_mul_low. As before, we\r
+         * express each input value as a shifted combination of two\r
+         * halves:\r
+         *\r
+         *   a = a_1 D + a_0\r
+         *   b = b_1 D + b_0\r
+         *\r
+         * Then the full product is, as before,\r
+         *\r
+         *  ab = a_1 b_1 D^2 + (a_1 b_0 + a_0 b_1) D + a_0 b_0\r
+         *\r
+         * Provided we choose D on the large side (so that a_0 and b_0\r
+         * are _at least_ as long as a_1 and b_1), we don't need the\r
+         * topmost term at all, and we only need half of the middle\r
+         * term. So there's no point in doing the proper Karatsuba\r
+         * optimisation which computes the middle term using the top\r
+         * one, because we'd take as long computing the top one as\r
+         * just computing the middle one directly.\r
+         *\r
+         * So instead, we do a much more obvious thing: we call the\r
+         * fully optimised internal_mul to compute a_0 b_0, and we\r
+         * recursively call ourself to compute the _bottom halves_ of\r
+         * a_1 b_0 and a_0 b_1, each of which we add into the result\r
+         * in the obvious way.\r
+         *\r
+         * In other words, there's no actual Karatsuba _optimisation_\r
+         * in this function; the only benefit in doing it this way is\r
+         * that we call internal_mul proper for a large part of the\r
+         * work, and _that_ can optimise its operation.\r
+         */\r
+\r
+        int toplen = len/2, botlen = len - toplen; /* botlen is the bigger */\r
+\r
+        /*\r
+         * Scratch space for the various bits and pieces we're going\r
+         * to be adding together: we need botlen*2 words for a_0 b_0\r
+         * (though we may end up throwing away its topmost word), and\r
+         * toplen words for each of a_1 b_0 and a_0 b_1. That adds up\r
+         * to exactly 2*len.\r
+         */\r
+\r
+        /* a_0 b_0 */\r
+        internal_mul(a + toplen, b + toplen, scratch + 2*toplen, botlen,\r
+                     scratch + 2*len);\r
+\r
+        /* a_1 b_0 */\r
+        internal_mul_low(a, b + len - toplen, scratch + toplen, toplen,\r
+                         scratch + 2*len);\r
+\r
+        /* a_0 b_1 */\r
+        internal_mul_low(a + len - toplen, b, scratch, toplen,\r
+                         scratch + 2*len);\r
+\r
+        /* Copy the bottom half of the big coefficient into place */\r
+        for (i = 0; i < botlen; i++)\r
+            c[toplen + i] = scratch[2*toplen + botlen + i];\r
+\r
+        /* Add the two small coefficients, throwing away the returned carry */\r
+        internal_add(scratch, scratch + toplen, scratch, toplen);\r
+\r
+        /* And add that to the large coefficient, leaving the result in c. */\r
+        internal_add(scratch, scratch + 2*toplen + botlen - toplen,\r
+                     c, toplen);\r
+\r
+    } else {\r
+        int i;\r
+        BignumInt carry;\r
+        BignumDblInt t;\r
+        const BignumInt *ap, *bp;\r
+        BignumInt *cp, *cps;\r
+\r
+        /*\r
+         * Multiply in the ordinary O(N^2) way.\r
+         */\r
+\r
+        for (i = 0; i < len; i++)\r
+            c[i] = 0;\r
+\r
+        for (cps = c + len, ap = a + len; ap-- > a; cps--) {\r
+            carry = 0;\r
+            for (cp = cps, bp = b + len; bp--, cp-- > c ;) {\r
+                t = (MUL_WORD(*ap, *bp) + carry) + *cp;\r
+                *cp = (BignumInt) t;\r
+                carry = (BignumInt)(t >> BIGNUM_INT_BITS);\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+/*\r
+ * Montgomery reduction. Expects x to be a big-endian array of 2*len\r
+ * BignumInts whose value satisfies 0 <= x < rn (where r = 2^(len *\r
+ * BIGNUM_INT_BITS) is the Montgomery base). Returns in the same array\r
+ * a value x' which is congruent to xr^{-1} mod n, and satisfies 0 <=\r
+ * x' < n.\r
+ *\r
+ * 'n' and 'mninv' should be big-endian arrays of 'len' BignumInts\r
+ * each, containing respectively n and the multiplicative inverse of\r
+ * -n mod r.\r
+ *\r
+ * 'tmp' is an array of BignumInt used as scratch space, of length at\r
+ * least 3*len + mul_compute_scratch(len).\r
+ */\r
+static void monty_reduce(BignumInt *x, const BignumInt *n,\r
+                         const BignumInt *mninv, BignumInt *tmp, int len)\r
+{\r
+    int i;\r
+    BignumInt carry;\r
+\r
+    /*\r
+     * Multiply x by (-n)^{-1} mod r. This gives us a value m such\r
+     * that mn is congruent to -x mod r. Hence, mn+x is an exact\r
+     * multiple of r, and is also (obviously) congruent to x mod n.\r
+     */\r
+    internal_mul_low(x + len, mninv, tmp, len, tmp + 3*len);\r
+\r
+    /*\r
+     * Compute t = (mn+x)/r in ordinary, non-modular, integer\r
+     * arithmetic. By construction this is exact, and is congruent mod\r
+     * n to x * r^{-1}, i.e. the answer we want.\r
+     *\r
+     * The following multiply leaves that answer in the _most_\r
+     * significant half of the 'x' array, so then we must shift it\r
+     * down.\r
+     */\r
+    internal_mul(tmp, n, tmp+len, len, tmp + 3*len);\r
+    carry = internal_add(x, tmp+len, x, 2*len);\r
+    for (i = 0; i < len; i++)\r
+        x[len + i] = x[i], x[i] = 0;\r
+\r
+    /*\r
+     * Reduce t mod n. This doesn't require a full-on division by n,\r
+     * but merely a test and single optional subtraction, since we can\r
+     * show that 0 <= t < 2n.\r
+     *\r
+     * Proof:\r
+     *  + we computed m mod r, so 0 <= m < r.\r
+     *  + so 0 <= mn < rn, obviously\r
+     *  + hence we only need 0 <= x < rn to guarantee that 0 <= mn+x < 2rn\r
+     *  + yielding 0 <= (mn+x)/r < 2n as required.\r
+     */\r
+    if (!carry) {\r
+        for (i = 0; i < len; i++)\r
+            if (x[len + i] != n[i])\r
+                break;\r
+    }\r
+    if (carry || i >= len || x[len + i] > n[i])\r
+        internal_sub(x+len, n, x+len, len);\r
+}\r
+\r
+static void internal_add_shifted(BignumInt *number,\r
+                                unsigned n, int shift)\r
+{\r
+    int word = 1 + (shift / BIGNUM_INT_BITS);\r
+    int bshift = shift % BIGNUM_INT_BITS;\r
+    BignumDblInt addend;\r
+\r
+    addend = (BignumDblInt)n << bshift;\r
+\r
+    while (addend) {\r
+       addend += number[word];\r
+       number[word] = (BignumInt) addend & BIGNUM_INT_MASK;\r
+       addend >>= BIGNUM_INT_BITS;\r
+       word++;\r
+    }\r
+}\r
+\r
+/*\r
+ * Compute a = a % m.\r
+ * Input in first alen words of a and first mlen words of m.\r
+ * Output in first alen words of a\r
+ * (of which first alen-mlen words will be zero).\r
+ * The MSW of m MUST have its high bit set.\r
+ * Quotient is accumulated in the `quotient' array, which is a Bignum\r
+ * rather than the internal bigendian format. Quotient parts are shifted\r
+ * left by `qshift' before adding into quot.\r
+ */\r
+static void internal_mod(BignumInt *a, int alen,\r
+                        BignumInt *m, int mlen,\r
+                        BignumInt *quot, int qshift)\r
+{\r
+    BignumInt m0, m1;\r
+    unsigned int h;\r
+    int i, k;\r
+\r
+    m0 = m[0];\r
+    if (mlen > 1)\r
+       m1 = m[1];\r
+    else\r
+       m1 = 0;\r
+\r
+    for (i = 0; i <= alen - mlen; i++) {\r
+       BignumDblInt t;\r
+       unsigned int q, r, c, ai1;\r
+\r
+       if (i == 0) {\r
+           h = 0;\r
+       } else {\r
+           h = a[i - 1];\r
+           a[i - 1] = 0;\r
+       }\r
+\r
+       if (i == alen - 1)\r
+           ai1 = 0;\r
+       else\r
+           ai1 = a[i + 1];\r
+\r
+       /* Find q = h:a[i] / m0 */\r
+       if (h >= m0) {\r
+           /*\r
+            * Special case.\r
+            * \r
+            * To illustrate it, suppose a BignumInt is 8 bits, and\r
+            * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then\r
+            * our initial division will be 0xA123 / 0xA1, which\r
+            * will give a quotient of 0x100 and a divide overflow.\r
+            * However, the invariants in this division algorithm\r
+            * are not violated, since the full number A1:23:... is\r
+            * _less_ than the quotient prefix A1:B2:... and so the\r
+            * following correction loop would have sorted it out.\r
+            * \r
+            * In this situation we set q to be the largest\r
+            * quotient we _can_ stomach (0xFF, of course).\r
+            */\r
+           q = BIGNUM_INT_MASK;\r
+       } else {\r
+           /* Macro doesn't want an array subscript expression passed\r
+            * into it (see definition), so use a temporary. */\r
+           BignumInt tmplo = a[i];\r
+           DIVMOD_WORD(q, r, h, tmplo, m0);\r
+\r
+           /* Refine our estimate of q by looking at\r
+            h:a[i]:a[i+1] / m0:m1 */\r
+           t = MUL_WORD(m1, q);\r
+           if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) {\r
+               q--;\r
+               t -= m1;\r
+               r = (r + m0) & BIGNUM_INT_MASK;     /* overflow? */\r
+               if (r >= (BignumDblInt) m0 &&\r
+                   t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--;\r
+           }\r
+       }\r
+\r
+       /* Subtract q * m from a[i...] */\r
+       c = 0;\r
+       for (k = mlen - 1; k >= 0; k--) {\r
+           t = MUL_WORD(q, m[k]);\r
+           t += c;\r
+           c = (unsigned)(t >> BIGNUM_INT_BITS);\r
+           if ((BignumInt) t > a[i + k])\r
+               c++;\r
+           a[i + k] -= (BignumInt) t;\r
+       }\r
+\r
+       /* Add back m in case of borrow */\r
+       if (c != h) {\r
+           t = 0;\r
+           for (k = mlen - 1; k >= 0; k--) {\r
+               t += m[k];\r
+               t += a[i + k];\r
+               a[i + k] = (BignumInt) t;\r
+               t = t >> BIGNUM_INT_BITS;\r
+           }\r
+           q--;\r
+       }\r
+       if (quot)\r
+           internal_add_shifted(quot, q, qshift + BIGNUM_INT_BITS * (alen - mlen - i));\r
+    }\r
+}\r
+\r
+/*\r
+ * Compute (base ^ exp) % mod, the pedestrian way.\r
+ */\r
+Bignum modpow_simple(Bignum base_in, Bignum exp, Bignum mod)\r
+{\r
+    BignumInt *a, *b, *n, *m, *scratch;\r
+    int mshift;\r
+    int mlen, scratchlen, i, j;\r
+    Bignum base, result;\r
+\r
+    /*\r
+     * The most significant word of mod needs to be non-zero. It\r
+     * should already be, but let's make sure.\r
+     */\r
+    assert(mod[mod[0]] != 0);\r
+\r
+    /*\r
+     * Make sure the base is smaller than the modulus, by reducing\r
+     * it modulo the modulus if not.\r
+     */\r
+    base = bigmod(base_in, mod);\r
+\r
+    /* Allocate m of size mlen, copy mod to m */\r
+    /* We use big endian internally */\r
+    mlen = mod[0];\r
+    m = snewn(mlen, BignumInt);\r
+    for (j = 0; j < mlen; j++)\r
+       m[j] = mod[mod[0] - j];\r
+\r
+    /* Shift m left to make msb bit set */\r
+    for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++)\r
+       if ((m[0] << mshift) & BIGNUM_TOP_BIT)\r
+           break;\r
+    if (mshift) {\r
+       for (i = 0; i < mlen - 1; i++)\r
+           m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift));\r
+       m[mlen - 1] = m[mlen - 1] << mshift;\r
+    }\r
+\r
+    /* Allocate n of size mlen, copy base to n */\r
+    n = snewn(mlen, BignumInt);\r
+    i = mlen - base[0];\r
+    for (j = 0; j < i; j++)\r
+       n[j] = 0;\r
+    for (j = 0; j < (int)base[0]; j++)\r
+       n[i + j] = base[base[0] - j];\r
+\r
+    /* Allocate a and b of size 2*mlen. Set a = 1 */\r
+    a = snewn(2 * mlen, BignumInt);\r
+    b = snewn(2 * mlen, BignumInt);\r
+    for (i = 0; i < 2 * mlen; i++)\r
+       a[i] = 0;\r
+    a[2 * mlen - 1] = 1;\r
+\r
+    /* Scratch space for multiplies */\r
+    scratchlen = mul_compute_scratch(mlen);\r
+    scratch = snewn(scratchlen, BignumInt);\r
+\r
+    /* Skip leading zero bits of exp. */\r
+    i = 0;\r
+    j = BIGNUM_INT_BITS-1;\r
+    while (i < (int)exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) {\r
+       j--;\r
+       if (j < 0) {\r
+           i++;\r
+           j = BIGNUM_INT_BITS-1;\r
+       }\r
+    }\r
+\r
+    /* Main computation */\r
+    while (i < (int)exp[0]) {\r
+       while (j >= 0) {\r
+           internal_mul(a + mlen, a + mlen, b, mlen, scratch);\r
+           internal_mod(b, mlen * 2, m, mlen, NULL, 0);\r
+           if ((exp[exp[0] - i] & (1 << j)) != 0) {\r
+               internal_mul(b + mlen, n, a, mlen, scratch);\r
+               internal_mod(a, mlen * 2, m, mlen, NULL, 0);\r
+           } else {\r
+               BignumInt *t;\r
+               t = a;\r
+               a = b;\r
+               b = t;\r
+           }\r
+           j--;\r
+       }\r
+       i++;\r
+       j = BIGNUM_INT_BITS-1;\r
+    }\r
+\r
+    /* Fixup result in case the modulus was shifted */\r
+    if (mshift) {\r
+       for (i = mlen - 1; i < 2 * mlen - 1; i++)\r
+           a[i] = (a[i] << mshift) | (a[i + 1] >> (BIGNUM_INT_BITS - mshift));\r
+       a[2 * mlen - 1] = a[2 * mlen - 1] << mshift;\r
+       internal_mod(a, mlen * 2, m, mlen, NULL, 0);\r
+       for (i = 2 * mlen - 1; i >= mlen; i--)\r
+           a[i] = (a[i] >> mshift) | (a[i - 1] << (BIGNUM_INT_BITS - mshift));\r
+    }\r
+\r
+    /* Copy result to buffer */\r
+    result = newbn(mod[0]);\r
+    for (i = 0; i < mlen; i++)\r
+       result[result[0] - i] = a[i + mlen];\r
+    while (result[0] > 1 && result[result[0]] == 0)\r
+       result[0]--;\r
+\r
+    /* Free temporary arrays */\r
+    for (i = 0; i < 2 * mlen; i++)\r
+       a[i] = 0;\r
+    sfree(a);\r
+    for (i = 0; i < scratchlen; i++)\r
+       scratch[i] = 0;\r
+    sfree(scratch);\r
+    for (i = 0; i < 2 * mlen; i++)\r
+       b[i] = 0;\r
+    sfree(b);\r
+    for (i = 0; i < mlen; i++)\r
+       m[i] = 0;\r
+    sfree(m);\r
+    for (i = 0; i < mlen; i++)\r
+       n[i] = 0;\r
+    sfree(n);\r
+\r
+    freebn(base);\r
+\r
+    return result;\r
+}\r
+\r
+/*\r
+ * Compute (base ^ exp) % mod. Uses the Montgomery multiplication\r
+ * technique where possible, falling back to modpow_simple otherwise.\r
+ */\r
+Bignum modpow(Bignum base_in, Bignum exp, Bignum mod)\r
+{\r
+    BignumInt *a, *b, *x, *n, *mninv, *scratch;\r
+    int len, scratchlen, i, j;\r
+    Bignum base, base2, r, rn, inv, result;\r
+\r
+    /*\r
+     * The most significant word of mod needs to be non-zero. It\r
+     * should already be, but let's make sure.\r
+     */\r
+    assert(mod[mod[0]] != 0);\r
+\r
+    /*\r
+     * mod had better be odd, or we can't do Montgomery multiplication\r
+     * using a power of two at all.\r
+     */\r
+    if (!(mod[1] & 1))\r
+        return modpow_simple(base_in, exp, mod);\r
+\r
+    /*\r
+     * Make sure the base is smaller than the modulus, by reducing\r
+     * it modulo the modulus if not.\r
+     */\r
+    base = bigmod(base_in, mod);\r
+\r
+    /*\r
+     * Compute the inverse of n mod r, for monty_reduce. (In fact we\r
+     * want the inverse of _minus_ n mod r, but we'll sort that out\r
+     * below.)\r
+     */\r
+    len = mod[0];\r
+    r = bn_power_2(BIGNUM_INT_BITS * len);\r
+    inv = modinv(mod, r);\r
+\r
+    /*\r
+     * Multiply the base by r mod n, to get it into Montgomery\r
+     * representation.\r
+     */\r
+    base2 = modmul(base, r, mod);\r
+    freebn(base);\r
+    base = base2;\r
+\r
+    rn = bigmod(r, mod);               /* r mod n, i.e. Montgomerified 1 */\r
+\r
+    freebn(r);                         /* won't need this any more */\r
+\r
+    /*\r
+     * Set up internal arrays of the right lengths, in big-endian\r
+     * format, containing the base, the modulus, and the modulus's\r
+     * inverse.\r
+     */\r
+    n = snewn(len, BignumInt);\r
+    for (j = 0; j < len; j++)\r
+       n[len - 1 - j] = mod[j + 1];\r
+\r
+    mninv = snewn(len, BignumInt);\r
+    for (j = 0; j < len; j++)\r
+       mninv[len - 1 - j] = (j < (int)inv[0] ? inv[j + 1] : 0);\r
+    freebn(inv);         /* we don't need this copy of it any more */\r
+    /* Now negate mninv mod r, so it's the inverse of -n rather than +n. */\r
+    x = snewn(len, BignumInt);\r
+    for (j = 0; j < len; j++)\r
+        x[j] = 0;\r
+    internal_sub(x, mninv, mninv, len);\r
+\r
+    /* x = snewn(len, BignumInt); */ /* already done above */\r
+    for (j = 0; j < len; j++)\r
+       x[len - 1 - j] = (j < (int)base[0] ? base[j + 1] : 0);\r
+    freebn(base);        /* we don't need this copy of it any more */\r
+\r
+    a = snewn(2*len, BignumInt);\r
+    b = snewn(2*len, BignumInt);\r
+    for (j = 0; j < len; j++)\r
+       a[2*len - 1 - j] = (j < (int)rn[0] ? rn[j + 1] : 0);\r
+    freebn(rn);\r
+\r
+    /* Scratch space for multiplies */\r
+    scratchlen = 3*len + mul_compute_scratch(len);\r
+    scratch = snewn(scratchlen, BignumInt);\r
+\r
+    /* Skip leading zero bits of exp. */\r
+    i = 0;\r
+    j = BIGNUM_INT_BITS-1;\r
+    while (i < (int)exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) {\r
+       j--;\r
+       if (j < 0) {\r
+           i++;\r
+           j = BIGNUM_INT_BITS-1;\r
+       }\r
+    }\r
+\r
+    /* Main computation */\r
+    while (i < (int)exp[0]) {\r
+       while (j >= 0) {\r
+           internal_mul(a + len, a + len, b, len, scratch);\r
+            monty_reduce(b, n, mninv, scratch, len);\r
+           if ((exp[exp[0] - i] & (1 << j)) != 0) {\r
+                internal_mul(b + len, x, a, len,  scratch);\r
+                monty_reduce(a, n, mninv, scratch, len);\r
+           } else {\r
+               BignumInt *t;\r
+               t = a;\r
+               a = b;\r
+               b = t;\r
+           }\r
+           j--;\r
+       }\r
+       i++;\r
+       j = BIGNUM_INT_BITS-1;\r
+    }\r
+\r
+    /*\r
+     * Final monty_reduce to get back from the adjusted Montgomery\r
+     * representation.\r
+     */\r
+    monty_reduce(a, n, mninv, scratch, len);\r
+\r
+    /* Copy result to buffer */\r
+    result = newbn(mod[0]);\r
+    for (i = 0; i < len; i++)\r
+       result[result[0] - i] = a[i + len];\r
+    while (result[0] > 1 && result[result[0]] == 0)\r
+       result[0]--;\r
+\r
+    /* Free temporary arrays */\r
+    for (i = 0; i < scratchlen; i++)\r
+       scratch[i] = 0;\r
+    sfree(scratch);\r
+    for (i = 0; i < 2 * len; i++)\r
+       a[i] = 0;\r
+    sfree(a);\r
+    for (i = 0; i < 2 * len; i++)\r
+       b[i] = 0;\r
+    sfree(b);\r
+    for (i = 0; i < len; i++)\r
+       mninv[i] = 0;\r
+    sfree(mninv);\r
+    for (i = 0; i < len; i++)\r
+       n[i] = 0;\r
+    sfree(n);\r
+    for (i = 0; i < len; i++)\r
+       x[i] = 0;\r
+    sfree(x);\r
+\r
+    return result;\r
+}\r
+\r
+/*\r
+ * Compute (p * q) % mod.\r
+ * The most significant word of mod MUST be non-zero.\r
+ * We assume that the result array is the same size as the mod array.\r
+ */\r
+Bignum modmul(Bignum p, Bignum q, Bignum mod)\r
+{\r
+    BignumInt *a, *n, *m, *o, *scratch;\r
+    int mshift, scratchlen;\r
+    int pqlen, mlen, rlen, i, j;\r
+    Bignum result;\r
+\r
+    /* Allocate m of size mlen, copy mod to m */\r
+    /* We use big endian internally */\r
+    mlen = mod[0];\r
+    m = snewn(mlen, BignumInt);\r
+    for (j = 0; j < mlen; j++)\r
+       m[j] = mod[mod[0] - j];\r
+\r
+    /* Shift m left to make msb bit set */\r
+    for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++)\r
+       if ((m[0] << mshift) & BIGNUM_TOP_BIT)\r
+           break;\r
+    if (mshift) {\r
+       for (i = 0; i < mlen - 1; i++)\r
+           m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift));\r
+       m[mlen - 1] = m[mlen - 1] << mshift;\r
+    }\r
+\r
+    pqlen = (p[0] > q[0] ? p[0] : q[0]);\r
+\r
+    /* Allocate n of size pqlen, copy p to n */\r
+    n = snewn(pqlen, BignumInt);\r
+    i = pqlen - p[0];\r
+    for (j = 0; j < i; j++)\r
+       n[j] = 0;\r
+    for (j = 0; j < (int)p[0]; j++)\r
+       n[i + j] = p[p[0] - j];\r
+\r
+    /* Allocate o of size pqlen, copy q to o */\r
+    o = snewn(pqlen, BignumInt);\r
+    i = pqlen - q[0];\r
+    for (j = 0; j < i; j++)\r
+       o[j] = 0;\r
+    for (j = 0; j < (int)q[0]; j++)\r
+       o[i + j] = q[q[0] - j];\r
+\r
+    /* Allocate a of size 2*pqlen for result */\r
+    a = snewn(2 * pqlen, BignumInt);\r
+\r
+    /* Scratch space for multiplies */\r
+    scratchlen = mul_compute_scratch(pqlen);\r
+    scratch = snewn(scratchlen, BignumInt);\r
+\r
+    /* Main computation */\r
+    internal_mul(n, o, a, pqlen, scratch);\r
+    internal_mod(a, pqlen * 2, m, mlen, NULL, 0);\r
+\r
+    /* Fixup result in case the modulus was shifted */\r
+    if (mshift) {\r
+       for (i = 2 * pqlen - mlen - 1; i < 2 * pqlen - 1; i++)\r
+           a[i] = (a[i] << mshift) | (a[i + 1] >> (BIGNUM_INT_BITS - mshift));\r
+       a[2 * pqlen - 1] = a[2 * pqlen - 1] << mshift;\r
+       internal_mod(a, pqlen * 2, m, mlen, NULL, 0);\r
+       for (i = 2 * pqlen - 1; i >= 2 * pqlen - mlen; i--)\r
+           a[i] = (a[i] >> mshift) | (a[i - 1] << (BIGNUM_INT_BITS - mshift));\r
+    }\r
+\r
+    /* Copy result to buffer */\r
+    rlen = (mlen < pqlen * 2 ? mlen : pqlen * 2);\r
+    result = newbn(rlen);\r
+    for (i = 0; i < rlen; i++)\r
+       result[result[0] - i] = a[i + 2 * pqlen - rlen];\r
+    while (result[0] > 1 && result[result[0]] == 0)\r
+       result[0]--;\r
+\r
+    /* Free temporary arrays */\r
+    for (i = 0; i < scratchlen; i++)\r
+       scratch[i] = 0;\r
+    sfree(scratch);\r
+    for (i = 0; i < 2 * pqlen; i++)\r
+       a[i] = 0;\r
+    sfree(a);\r
+    for (i = 0; i < mlen; i++)\r
+       m[i] = 0;\r
+    sfree(m);\r
+    for (i = 0; i < pqlen; i++)\r
+       n[i] = 0;\r
+    sfree(n);\r
+    for (i = 0; i < pqlen; i++)\r
+       o[i] = 0;\r
+    sfree(o);\r
+\r
+    return result;\r
+}\r
+\r
+/*\r
+ * Compute p % mod.\r
+ * The most significant word of mod MUST be non-zero.\r
+ * We assume that the result array is the same size as the mod array.\r
+ * We optionally write out a quotient if `quotient' is non-NULL.\r
+ * We can avoid writing out the result if `result' is NULL.\r
+ */\r
+static void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient)\r
+{\r
+    BignumInt *n, *m;\r
+    int mshift;\r
+    int plen, mlen, i, j;\r
+\r
+    /* Allocate m of size mlen, copy mod to m */\r
+    /* We use big endian internally */\r
+    mlen = mod[0];\r
+    m = snewn(mlen, BignumInt);\r
+    for (j = 0; j < mlen; j++)\r
+       m[j] = mod[mod[0] - j];\r
+\r
+    /* Shift m left to make msb bit set */\r
+    for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++)\r
+       if ((m[0] << mshift) & BIGNUM_TOP_BIT)\r
+           break;\r
+    if (mshift) {\r
+       for (i = 0; i < mlen - 1; i++)\r
+           m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift));\r
+       m[mlen - 1] = m[mlen - 1] << mshift;\r
+    }\r
+\r
+    plen = p[0];\r
+    /* Ensure plen > mlen */\r
+    if (plen <= mlen)\r
+       plen = mlen + 1;\r
+\r
+    /* Allocate n of size plen, copy p to n */\r
+    n = snewn(plen, BignumInt);\r
+    for (j = 0; j < plen; j++)\r
+       n[j] = 0;\r
+    for (j = 1; j <= (int)p[0]; j++)\r
+       n[plen - j] = p[j];\r
+\r
+    /* Main computation */\r
+    internal_mod(n, plen, m, mlen, quotient, mshift);\r
+\r
+    /* Fixup result in case the modulus was shifted */\r
+    if (mshift) {\r
+       for (i = plen - mlen - 1; i < plen - 1; i++)\r
+           n[i] = (n[i] << mshift) | (n[i + 1] >> (BIGNUM_INT_BITS - mshift));\r
+       n[plen - 1] = n[plen - 1] << mshift;\r
+       internal_mod(n, plen, m, mlen, quotient, 0);\r
+       for (i = plen - 1; i >= plen - mlen; i--)\r
+           n[i] = (n[i] >> mshift) | (n[i - 1] << (BIGNUM_INT_BITS - mshift));\r
+    }\r
+\r
+    /* Copy result to buffer */\r
+    if (result) {\r
+       for (i = 1; i <= (int)result[0]; i++) {\r
+           int j = plen - i;\r
+           result[i] = j >= 0 ? n[j] : 0;\r
+       }\r
+    }\r
+\r
+    /* Free temporary arrays */\r
+    for (i = 0; i < mlen; i++)\r
+       m[i] = 0;\r
+    sfree(m);\r
+    for (i = 0; i < plen; i++)\r
+       n[i] = 0;\r
+    sfree(n);\r
+}\r
+\r
+/*\r
+ * Decrement a number.\r
+ */\r
+void decbn(Bignum bn)\r
+{\r
+    int i = 1;\r
+    while (i < (int)bn[0] && bn[i] == 0)\r
+       bn[i++] = BIGNUM_INT_MASK;\r
+    bn[i]--;\r
+}\r
+\r
+Bignum bignum_from_bytes(const unsigned char *data, int nbytes)\r
+{\r
+    Bignum result;\r
+    int w, i;\r
+\r
+    w = (nbytes + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES; /* bytes->words */\r
+\r
+    result = newbn(w);\r
+    for (i = 1; i <= w; i++)\r
+       result[i] = 0;\r
+    for (i = nbytes; i--;) {\r
+       unsigned char byte = *data++;\r
+       result[1 + i / BIGNUM_INT_BYTES] |= byte << (8*i % BIGNUM_INT_BITS);\r
+    }\r
+\r
+    while (result[0] > 1 && result[result[0]] == 0)\r
+       result[0]--;\r
+    return result;\r
+}\r
+\r
+/*\r
+ * Read an SSH-1-format bignum from a data buffer. Return the number\r
+ * of bytes consumed, or -1 if there wasn't enough data.\r
+ */\r
+int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result)\r
+{\r
+    const unsigned char *p = data;\r
+    int i;\r
+    int w, b;\r
+\r
+    if (len < 2)\r
+       return -1;\r
+\r
+    w = 0;\r
+    for (i = 0; i < 2; i++)\r
+       w = (w << 8) + *p++;\r
+    b = (w + 7) / 8;                  /* bits -> bytes */\r
+\r
+    if (len < b+2)\r
+       return -1;\r
+\r
+    if (!result)                      /* just return length */\r
+       return b + 2;\r
+\r
+    *result = bignum_from_bytes(p, b);\r
+\r
+    return p + b - data;\r
+}\r
+\r
+/*\r
+ * Return the bit count of a bignum, for SSH-1 encoding.\r
+ */\r
+int bignum_bitcount(Bignum bn)\r
+{\r
+    int bitcount = bn[0] * BIGNUM_INT_BITS - 1;\r
+    while (bitcount >= 0\r
+          && (bn[bitcount / BIGNUM_INT_BITS + 1] >> (bitcount % BIGNUM_INT_BITS)) == 0) bitcount--;\r
+    return bitcount + 1;\r
+}\r
+\r
+/*\r
+ * Return the byte length of a bignum when SSH-1 encoded.\r
+ */\r
+int ssh1_bignum_length(Bignum bn)\r
+{\r
+    return 2 + (bignum_bitcount(bn) + 7) / 8;\r
+}\r
+\r
+/*\r
+ * Return the byte length of a bignum when SSH-2 encoded.\r
+ */\r
+int ssh2_bignum_length(Bignum bn)\r
+{\r
+    return 4 + (bignum_bitcount(bn) + 8) / 8;\r
+}\r
+\r
+/*\r
+ * Return a byte from a bignum; 0 is least significant, etc.\r
+ */\r
+int bignum_byte(Bignum bn, int i)\r
+{\r
+    if (i >= (int)(BIGNUM_INT_BYTES * bn[0]))\r
+       return 0;                      /* beyond the end */\r
+    else\r
+       return (bn[i / BIGNUM_INT_BYTES + 1] >>\r
+               ((i % BIGNUM_INT_BYTES)*8)) & 0xFF;\r
+}\r
+\r
+/*\r
+ * Return a bit from a bignum; 0 is least significant, etc.\r
+ */\r
+int bignum_bit(Bignum bn, int i)\r
+{\r
+    if (i >= (int)(BIGNUM_INT_BITS * bn[0]))\r
+       return 0;                      /* beyond the end */\r
+    else\r
+       return (bn[i / BIGNUM_INT_BITS + 1] >> (i % BIGNUM_INT_BITS)) & 1;\r
+}\r
+\r
+/*\r
+ * Set a bit in a bignum; 0 is least significant, etc.\r
+ */\r
+void bignum_set_bit(Bignum bn, int bitnum, int value)\r
+{\r
+    if (bitnum >= (int)(BIGNUM_INT_BITS * bn[0]))\r
+       abort();                       /* beyond the end */\r
+    else {\r
+       int v = bitnum / BIGNUM_INT_BITS + 1;\r
+       int mask = 1 << (bitnum % BIGNUM_INT_BITS);\r
+       if (value)\r
+           bn[v] |= mask;\r
+       else\r
+           bn[v] &= ~mask;\r
+    }\r
+}\r
+\r
+/*\r
+ * Write a SSH-1-format bignum into a buffer. It is assumed the\r
+ * buffer is big enough. Returns the number of bytes used.\r
+ */\r
+int ssh1_write_bignum(void *data, Bignum bn)\r
+{\r
+    unsigned char *p = data;\r
+    int len = ssh1_bignum_length(bn);\r
+    int i;\r
+    int bitc = bignum_bitcount(bn);\r
+\r
+    *p++ = (bitc >> 8) & 0xFF;\r
+    *p++ = (bitc) & 0xFF;\r
+    for (i = len - 2; i--;)\r
+       *p++ = bignum_byte(bn, i);\r
+    return len;\r
+}\r
+\r
+/*\r
+ * Compare two bignums. Returns like strcmp.\r
+ */\r
+int bignum_cmp(Bignum a, Bignum b)\r
+{\r
+    int amax = a[0], bmax = b[0];\r
+    int i = (amax > bmax ? amax : bmax);\r
+    while (i) {\r
+       BignumInt aval = (i > amax ? 0 : a[i]);\r
+       BignumInt bval = (i > bmax ? 0 : b[i]);\r
+       if (aval < bval)\r
+           return -1;\r
+       if (aval > bval)\r
+           return +1;\r
+       i--;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Right-shift one bignum to form another.\r
+ */\r
+Bignum bignum_rshift(Bignum a, int shift)\r
+{\r
+    Bignum ret;\r
+    int i, shiftw, shiftb, shiftbb, bits;\r
+    BignumInt ai, ai1;\r
+\r
+    bits = bignum_bitcount(a) - shift;\r
+    ret = newbn((bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS);\r
+\r
+    if (ret) {\r
+       shiftw = shift / BIGNUM_INT_BITS;\r
+       shiftb = shift % BIGNUM_INT_BITS;\r
+       shiftbb = BIGNUM_INT_BITS - shiftb;\r
+\r
+       ai1 = a[shiftw + 1];\r
+       for (i = 1; i <= (int)ret[0]; i++) {\r
+           ai = ai1;\r
+           ai1 = (i + shiftw + 1 <= (int)a[0] ? a[i + shiftw + 1] : 0);\r
+           ret[i] = ((ai >> shiftb) | (ai1 << shiftbb)) & BIGNUM_INT_MASK;\r
+       }\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Non-modular multiplication and addition.\r
+ */\r
+Bignum bigmuladd(Bignum a, Bignum b, Bignum addend)\r
+{\r
+    int alen = a[0], blen = b[0];\r
+    int mlen = (alen > blen ? alen : blen);\r
+    int rlen, i, maxspot;\r
+    int wslen;\r
+    BignumInt *workspace;\r
+    Bignum ret;\r
+\r
+    /* mlen space for a, mlen space for b, 2*mlen for result,\r
+     * plus scratch space for multiplication */\r
+    wslen = mlen * 4 + mul_compute_scratch(mlen);\r
+    workspace = snewn(wslen, BignumInt);\r
+    for (i = 0; i < mlen; i++) {\r
+       workspace[0 * mlen + i] = (mlen - i <= (int)a[0] ? a[mlen - i] : 0);\r
+       workspace[1 * mlen + i] = (mlen - i <= (int)b[0] ? b[mlen - i] : 0);\r
+    }\r
+\r
+    internal_mul(workspace + 0 * mlen, workspace + 1 * mlen,\r
+                workspace + 2 * mlen, mlen, workspace + 4 * mlen);\r
+\r
+    /* now just copy the result back */\r
+    rlen = alen + blen + 1;\r
+    if (addend && rlen <= (int)addend[0])\r
+       rlen = addend[0] + 1;\r
+    ret = newbn(rlen);\r
+    maxspot = 0;\r
+    for (i = 1; i <= (int)ret[0]; i++) {\r
+       ret[i] = (i <= 2 * mlen ? workspace[4 * mlen - i] : 0);\r
+       if (ret[i] != 0)\r
+           maxspot = i;\r
+    }\r
+    ret[0] = maxspot;\r
+\r
+    /* now add in the addend, if any */\r
+    if (addend) {\r
+       BignumDblInt carry = 0;\r
+       for (i = 1; i <= rlen; i++) {\r
+           carry += (i <= (int)ret[0] ? ret[i] : 0);\r
+           carry += (i <= (int)addend[0] ? addend[i] : 0);\r
+           ret[i] = (BignumInt) carry & BIGNUM_INT_MASK;\r
+           carry >>= BIGNUM_INT_BITS;\r
+           if (ret[i] != 0 && i > maxspot)\r
+               maxspot = i;\r
+       }\r
+    }\r
+    ret[0] = maxspot;\r
+\r
+    for (i = 0; i < wslen; i++)\r
+        workspace[i] = 0;\r
+    sfree(workspace);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Non-modular multiplication.\r
+ */\r
+Bignum bigmul(Bignum a, Bignum b)\r
+{\r
+    return bigmuladd(a, b, NULL);\r
+}\r
+\r
+/*\r
+ * Simple addition.\r
+ */\r
+Bignum bigadd(Bignum a, Bignum b)\r
+{\r
+    int alen = a[0], blen = b[0];\r
+    int rlen = (alen > blen ? alen : blen) + 1;\r
+    int i, maxspot;\r
+    Bignum ret;\r
+    BignumDblInt carry;\r
+\r
+    ret = newbn(rlen);\r
+\r
+    carry = 0;\r
+    maxspot = 0;\r
+    for (i = 1; i <= rlen; i++) {\r
+        carry += (i <= (int)a[0] ? a[i] : 0);\r
+        carry += (i <= (int)b[0] ? b[i] : 0);\r
+        ret[i] = (BignumInt) carry & BIGNUM_INT_MASK;\r
+        carry >>= BIGNUM_INT_BITS;\r
+        if (ret[i] != 0 && i > maxspot)\r
+            maxspot = i;\r
+    }\r
+    ret[0] = maxspot;\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Subtraction. Returns a-b, or NULL if the result would come out\r
+ * negative (recall that this entire bignum module only handles\r
+ * positive numbers).\r
+ */\r
+Bignum bigsub(Bignum a, Bignum b)\r
+{\r
+    int alen = a[0], blen = b[0];\r
+    int rlen = (alen > blen ? alen : blen);\r
+    int i, maxspot;\r
+    Bignum ret;\r
+    BignumDblInt carry;\r
+\r
+    ret = newbn(rlen);\r
+\r
+    carry = 1;\r
+    maxspot = 0;\r
+    for (i = 1; i <= rlen; i++) {\r
+        carry += (i <= (int)a[0] ? a[i] : 0);\r
+        carry += (i <= (int)b[0] ? b[i] ^ BIGNUM_INT_MASK : BIGNUM_INT_MASK);\r
+        ret[i] = (BignumInt) carry & BIGNUM_INT_MASK;\r
+        carry >>= BIGNUM_INT_BITS;\r
+        if (ret[i] != 0 && i > maxspot)\r
+            maxspot = i;\r
+    }\r
+    ret[0] = maxspot;\r
+\r
+    if (!carry) {\r
+        freebn(ret);\r
+        return NULL;\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Create a bignum which is the bitmask covering another one. That\r
+ * is, the smallest integer which is >= N and is also one less than\r
+ * a power of two.\r
+ */\r
+Bignum bignum_bitmask(Bignum n)\r
+{\r
+    Bignum ret = copybn(n);\r
+    int i;\r
+    BignumInt j;\r
+\r
+    i = ret[0];\r
+    while (n[i] == 0 && i > 0)\r
+       i--;\r
+    if (i <= 0)\r
+       return ret;                    /* input was zero */\r
+    j = 1;\r
+    while (j < n[i])\r
+       j = 2 * j + 1;\r
+    ret[i] = j;\r
+    while (--i > 0)\r
+       ret[i] = BIGNUM_INT_MASK;\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Convert a (max 32-bit) long into a bignum.\r
+ */\r
+Bignum bignum_from_long(unsigned long nn)\r
+{\r
+    Bignum ret;\r
+    BignumDblInt n = nn;\r
+\r
+    ret = newbn(3);\r
+    ret[1] = (BignumInt)(n & BIGNUM_INT_MASK);\r
+    ret[2] = (BignumInt)((n >> BIGNUM_INT_BITS) & BIGNUM_INT_MASK);\r
+    ret[3] = 0;\r
+    ret[0] = (ret[2]  ? 2 : 1);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Add a long to a bignum.\r
+ */\r
+Bignum bignum_add_long(Bignum number, unsigned long addendx)\r
+{\r
+    Bignum ret = newbn(number[0] + 1);\r
+    int i, maxspot = 0;\r
+    BignumDblInt carry = 0, addend = addendx;\r
+\r
+    for (i = 1; i <= (int)ret[0]; i++) {\r
+       carry += addend & BIGNUM_INT_MASK;\r
+       carry += (i <= (int)number[0] ? number[i] : 0);\r
+       addend >>= BIGNUM_INT_BITS;\r
+       ret[i] = (BignumInt) carry & BIGNUM_INT_MASK;\r
+       carry >>= BIGNUM_INT_BITS;\r
+       if (ret[i] != 0)\r
+           maxspot = i;\r
+    }\r
+    ret[0] = maxspot;\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Compute the residue of a bignum, modulo a (max 16-bit) short.\r
+ */\r
+unsigned short bignum_mod_short(Bignum number, unsigned short modulus)\r
+{\r
+    BignumDblInt mod, r;\r
+    int i;\r
+\r
+    r = 0;\r
+    mod = modulus;\r
+    for (i = number[0]; i > 0; i--)\r
+       r = (r * (BIGNUM_TOP_BIT % mod) * 2 + number[i] % mod) % mod;\r
+    return (unsigned short) r;\r
+}\r
+\r
+#ifdef DEBUG\r
+void diagbn(char *prefix, Bignum md)\r
+{\r
+    int i, nibbles, morenibbles;\r
+    static const char hex[] = "0123456789ABCDEF";\r
+\r
+    debug(("%s0x", prefix ? prefix : ""));\r
+\r
+    nibbles = (3 + bignum_bitcount(md)) / 4;\r
+    if (nibbles < 1)\r
+       nibbles = 1;\r
+    morenibbles = 4 * md[0] - nibbles;\r
+    for (i = 0; i < morenibbles; i++)\r
+       debug(("-"));\r
+    for (i = nibbles; i--;)\r
+       debug(("%c",\r
+              hex[(bignum_byte(md, i / 2) >> (4 * (i % 2))) & 0xF]));\r
+\r
+    if (prefix)\r
+       debug(("\n"));\r
+}\r
+#endif\r
+\r
+/*\r
+ * Simple division.\r
+ */\r
+Bignum bigdiv(Bignum a, Bignum b)\r
+{\r
+    Bignum q = newbn(a[0]);\r
+    bigdivmod(a, b, NULL, q);\r
+    return q;\r
+}\r
+\r
+/*\r
+ * Simple remainder.\r
+ */\r
+Bignum bigmod(Bignum a, Bignum b)\r
+{\r
+    Bignum r = newbn(b[0]);\r
+    bigdivmod(a, b, r, NULL);\r
+    return r;\r
+}\r
+\r
+/*\r
+ * Greatest common divisor.\r
+ */\r
+Bignum biggcd(Bignum av, Bignum bv)\r
+{\r
+    Bignum a = copybn(av);\r
+    Bignum b = copybn(bv);\r
+\r
+    while (bignum_cmp(b, Zero) != 0) {\r
+       Bignum t = newbn(b[0]);\r
+       bigdivmod(a, b, t, NULL);\r
+       while (t[0] > 1 && t[t[0]] == 0)\r
+           t[0]--;\r
+       freebn(a);\r
+       a = b;\r
+       b = t;\r
+    }\r
+\r
+    freebn(b);\r
+    return a;\r
+}\r
+\r
+/*\r
+ * Modular inverse, using Euclid's extended algorithm.\r
+ */\r
+Bignum modinv(Bignum number, Bignum modulus)\r
+{\r
+    Bignum a = copybn(modulus);\r
+    Bignum b = copybn(number);\r
+    Bignum xp = copybn(Zero);\r
+    Bignum x = copybn(One);\r
+    int sign = +1;\r
+\r
+    while (bignum_cmp(b, One) != 0) {\r
+       Bignum t = newbn(b[0]);\r
+       Bignum q = newbn(a[0]);\r
+       bigdivmod(a, b, t, q);\r
+       while (t[0] > 1 && t[t[0]] == 0)\r
+           t[0]--;\r
+       freebn(a);\r
+       a = b;\r
+       b = t;\r
+       t = xp;\r
+       xp = x;\r
+       x = bigmuladd(q, xp, t);\r
+       sign = -sign;\r
+       freebn(t);\r
+       freebn(q);\r
+    }\r
+\r
+    freebn(b);\r
+    freebn(a);\r
+    freebn(xp);\r
+\r
+    /* now we know that sign * x == 1, and that x < modulus */\r
+    if (sign < 0) {\r
+       /* set a new x to be modulus - x */\r
+       Bignum newx = newbn(modulus[0]);\r
+       BignumInt carry = 0;\r
+       int maxspot = 1;\r
+       int i;\r
+\r
+       for (i = 1; i <= (int)newx[0]; i++) {\r
+           BignumInt aword = (i <= (int)modulus[0] ? modulus[i] : 0);\r
+           BignumInt bword = (i <= (int)x[0] ? x[i] : 0);\r
+           newx[i] = aword - bword - carry;\r
+           bword = ~bword;\r
+           carry = carry ? (newx[i] >= bword) : (newx[i] > bword);\r
+           if (newx[i] != 0)\r
+               maxspot = i;\r
+       }\r
+       newx[0] = maxspot;\r
+       freebn(x);\r
+       x = newx;\r
+    }\r
+\r
+    /* and return. */\r
+    return x;\r
+}\r
+\r
+/*\r
+ * Render a bignum into decimal. Return a malloced string holding\r
+ * the decimal representation.\r
+ */\r
+char *bignum_decimal(Bignum x)\r
+{\r
+    int ndigits, ndigit;\r
+    int i, iszero;\r
+    BignumDblInt carry;\r
+    char *ret;\r
+    BignumInt *workspace;\r
+\r
+    /*\r
+     * First, estimate the number of digits. Since log(10)/log(2)\r
+     * is just greater than 93/28 (the joys of continued fraction\r
+     * approximations...) we know that for every 93 bits, we need\r
+     * at most 28 digits. This will tell us how much to malloc.\r
+     *\r
+     * Formally: if x has i bits, that means x is strictly less\r
+     * than 2^i. Since 2 is less than 10^(28/93), this is less than\r
+     * 10^(28i/93). We need an integer power of ten, so we must\r
+     * round up (rounding down might make it less than x again).\r
+     * Therefore if we multiply the bit count by 28/93, rounding\r
+     * up, we will have enough digits.\r
+     *\r
+     * i=0 (i.e., x=0) is an irritating special case.\r
+     */\r
+    i = bignum_bitcount(x);\r
+    if (!i)\r
+       ndigits = 1;                   /* x = 0 */\r
+    else\r
+       ndigits = (28 * i + 92) / 93;  /* multiply by 28/93 and round up */\r
+    ndigits++;                        /* allow for trailing \0 */\r
+    ret = snewn(ndigits, char);\r
+\r
+    /*\r
+     * Now allocate some workspace to hold the binary form as we\r
+     * repeatedly divide it by ten. Initialise this to the\r
+     * big-endian form of the number.\r
+     */\r
+    workspace = snewn(x[0], BignumInt);\r
+    for (i = 0; i < (int)x[0]; i++)\r
+       workspace[i] = x[x[0] - i];\r
+\r
+    /*\r
+     * Next, write the decimal number starting with the last digit.\r
+     * We use ordinary short division, dividing 10 into the\r
+     * workspace.\r
+     */\r
+    ndigit = ndigits - 1;\r
+    ret[ndigit] = '\0';\r
+    do {\r
+       iszero = 1;\r
+       carry = 0;\r
+       for (i = 0; i < (int)x[0]; i++) {\r
+           carry = (carry << BIGNUM_INT_BITS) + workspace[i];\r
+           workspace[i] = (BignumInt) (carry / 10);\r
+           if (workspace[i])\r
+               iszero = 0;\r
+           carry %= 10;\r
+       }\r
+       ret[--ndigit] = (char) (carry + '0');\r
+    } while (!iszero);\r
+\r
+    /*\r
+     * There's a chance we've fallen short of the start of the\r
+     * string. Correct if so.\r
+     */\r
+    if (ndigit > 0)\r
+       memmove(ret, ret + ndigit, ndigits - ndigit);\r
+\r
+    /*\r
+     * Done.\r
+     */\r
+    sfree(workspace);\r
+    return ret;\r
+}\r
+\r
+#ifdef TESTBN\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+/*\r
+ * gcc -g -O0 -DTESTBN -o testbn sshbn.c misc.c -I unix -I charset\r
+ *\r
+ * Then feed to this program's standard input the output of\r
+ * testdata/bignum.py .\r
+ */\r
+\r
+void modalfatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    exit(1);\r
+}\r
+\r
+#define fromxdigit(c) ( (c)>'9' ? ((c)&0xDF) - 'A' + 10 : (c) - '0' )\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    char *buf;\r
+    int line = 0;\r
+    int passes = 0, fails = 0;\r
+\r
+    while ((buf = fgetline(stdin)) != NULL) {\r
+        int maxlen = strlen(buf);\r
+        unsigned char *data = snewn(maxlen, unsigned char);\r
+        unsigned char *ptrs[5], *q;\r
+        int ptrnum;\r
+        char *bufp = buf;\r
+\r
+        line++;\r
+\r
+        q = data;\r
+        ptrnum = 0;\r
+\r
+        while (*bufp && !isspace((unsigned char)*bufp))\r
+            bufp++;\r
+        if (bufp)\r
+            *bufp++ = '\0';\r
+\r
+        while (*bufp) {\r
+            char *start, *end;\r
+            int i;\r
+\r
+            while (*bufp && !isxdigit((unsigned char)*bufp))\r
+                bufp++;\r
+            start = bufp;\r
+\r
+            if (!*bufp)\r
+                break;\r
+\r
+            while (*bufp && isxdigit((unsigned char)*bufp))\r
+                bufp++;\r
+            end = bufp;\r
+\r
+            if (ptrnum >= lenof(ptrs))\r
+                break;\r
+            ptrs[ptrnum++] = q;\r
+            \r
+            for (i = -((end - start) & 1); i < end-start; i += 2) {\r
+                unsigned char val = (i < 0 ? 0 : fromxdigit(start[i]));\r
+                val = val * 16 + fromxdigit(start[i+1]);\r
+                *q++ = val;\r
+            }\r
+\r
+            ptrs[ptrnum] = q;\r
+        }\r
+\r
+        if (!strcmp(buf, "mul")) {\r
+            Bignum a, b, c, p;\r
+\r
+            if (ptrnum != 3) {\r
+                printf("%d: mul with %d parameters, expected 3\n", line);\r
+                exit(1);\r
+            }\r
+            a = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]);\r
+            b = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]);\r
+            c = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]);\r
+            p = bigmul(a, b);\r
+\r
+            if (bignum_cmp(c, p) == 0) {\r
+                passes++;\r
+            } else {\r
+                char *as = bignum_decimal(a);\r
+                char *bs = bignum_decimal(b);\r
+                char *cs = bignum_decimal(c);\r
+                char *ps = bignum_decimal(p);\r
+                \r
+                printf("%d: fail: %s * %s gave %s expected %s\n",\r
+                       line, as, bs, ps, cs);\r
+                fails++;\r
+\r
+                sfree(as);\r
+                sfree(bs);\r
+                sfree(cs);\r
+                sfree(ps);\r
+            }\r
+            freebn(a);\r
+            freebn(b);\r
+            freebn(c);\r
+            freebn(p);\r
+        } else if (!strcmp(buf, "pow")) {\r
+            Bignum base, expt, modulus, expected, answer;\r
+\r
+            if (ptrnum != 4) {\r
+                printf("%d: mul with %d parameters, expected 3\n", line);\r
+                exit(1);\r
+            }\r
+\r
+            base = bignum_from_bytes(ptrs[0], ptrs[1]-ptrs[0]);\r
+            expt = bignum_from_bytes(ptrs[1], ptrs[2]-ptrs[1]);\r
+            modulus = bignum_from_bytes(ptrs[2], ptrs[3]-ptrs[2]);\r
+            expected = bignum_from_bytes(ptrs[3], ptrs[4]-ptrs[3]);\r
+            answer = modpow(base, expt, modulus);\r
+\r
+            if (bignum_cmp(expected, answer) == 0) {\r
+                passes++;\r
+            } else {\r
+                char *as = bignum_decimal(base);\r
+                char *bs = bignum_decimal(expt);\r
+                char *cs = bignum_decimal(modulus);\r
+                char *ds = bignum_decimal(answer);\r
+                char *ps = bignum_decimal(expected);\r
+                \r
+                printf("%d: fail: %s ^ %s mod %s gave %s expected %s\n",\r
+                       line, as, bs, cs, ds, ps);\r
+                fails++;\r
+\r
+                sfree(as);\r
+                sfree(bs);\r
+                sfree(cs);\r
+                sfree(ds);\r
+                sfree(ps);\r
+            }\r
+            freebn(base);\r
+            freebn(expt);\r
+            freebn(modulus);\r
+            freebn(expected);\r
+            freebn(answer);\r
+        } else {\r
+            printf("%d: unrecognised test keyword: '%s'\n", line, buf);\r
+            exit(1);\r
+        }\r
+\r
+        sfree(buf);\r
+        sfree(data);\r
+    }\r
+\r
+    printf("passed %d failed %d total %d\n", passes, fails, passes+fails);\r
+    return fails != 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/SSHCRC.C b/putty/SSHCRC.C
new file mode 100644 (file)
index 0000000..0a72a31
--- /dev/null
@@ -0,0 +1,230 @@
+/*\r
+ * CRC32 implementation.\r
+ *\r
+ * The basic concept of a CRC is that you treat your bit-string\r
+ * abcdefg... as a ludicrously long polynomial M=a+bx+cx^2+dx^3+...\r
+ * over Z[2]. You then take a modulus polynomial P, and compute the\r
+ * remainder of M on division by P. Thus, an erroneous message N\r
+ * will only have the same CRC if the difference E = M-N is an\r
+ * exact multiple of P. (Note that as we are working over Z[2], M-N\r
+ * = N-M = M+N; but that's not very important.)\r
+ *\r
+ * What makes the CRC good is choosing P to have good properties:\r
+ *\r
+ *  - If its first and last terms are both nonzero then it cannot\r
+ *    be a factor of any single term x^i. Therefore if M and N\r
+ *    differ by exactly one bit their CRCs will guaranteeably\r
+ *    be distinct.\r
+ *\r
+ *  - If it has a prime (irreducible) factor with three terms then\r
+ *    it cannot divide a polynomial of the form x^i(1+x^j).\r
+ *    Therefore if M and N differ by exactly _two_ bits they will\r
+ *    have different CRCs.\r
+ *\r
+ *  - If it has a factor (x+1) then it cannot divide a polynomial\r
+ *    with an odd number of terms. Therefore if M and N differ by\r
+ *    _any odd_ number of bits they will have different CRCs.\r
+ *\r
+ *  - If the error term E is of the form x^i*B(x) where B(x) has\r
+ *    order less than P (i.e. a short _burst_ of errors) then P\r
+ *    cannot divide E (since no polynomial can divide a shorter\r
+ *    one), so any such error burst will be spotted.\r
+ *\r
+ * The CRC32 standard polynomial is\r
+ *   x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0\r
+ *\r
+ * In fact, we don't compute M mod P; we compute M*x^32 mod P.\r
+ *\r
+ * The concrete implementation of the CRC is this: we maintain at\r
+ * all times a 32-bit word which is the current remainder of the\r
+ * polynomial mod P. Whenever we receive an extra bit, we multiply\r
+ * the existing remainder by x, add (XOR) the x^32 term thus\r
+ * generated to the new x^32 term caused by the incoming bit, and\r
+ * remove the resulting combined x^32 term if present by replacing\r
+ * it with (P-x^32).\r
+ *\r
+ * Bit 0 of the word is the x^31 term and bit 31 is the x^0 term.\r
+ * Thus, multiplying by x means shifting right. So the actual\r
+ * algorithm goes like this:\r
+ *\r
+ *   x32term = (crcword & 1) ^ newbit;\r
+ *   crcword = (crcword >> 1) ^ (x32term * 0xEDB88320);\r
+ *\r
+ * In practice, we pre-compute what will happen to crcword on any\r
+ * given sequence of eight incoming bits, and store that in a table\r
+ * which we then use at run-time to do the job:\r
+ * \r
+ *   outgoingplusnew = (crcword & 0xFF) ^ newbyte;\r
+ *   crcword = (crcword >> 8) ^ table[outgoingplusnew];\r
+ *\r
+ * where table[outgoingplusnew] is computed by setting crcword=0\r
+ * and then iterating the first code fragment eight times (taking\r
+ * the incoming byte low bit first).\r
+ *\r
+ * Note that all shifts are rightward and thus no assumption is\r
+ * made about exact word length! (Although word length must be at\r
+ * _least_ 32 bits, but ANSI C guarantees this for `unsigned long'\r
+ * anyway.)\r
+ */\r
+\r
+#include <stdlib.h>\r
+\r
+#include "ssh.h"\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Multi-function module. Can be compiled three ways.\r
+ *\r
+ *  - Compile with no special #defines. Will generate a table\r
+ *    that's already initialised at compile time, and one function\r
+ *    crc32_compute(buf,len) that uses it. Normal usage.\r
+ *\r
+ *  - Compile with INITFUNC defined. Will generate an uninitialised\r
+ *    array as the table, and as well as crc32_compute(buf,len) it\r
+ *    will also generate void crc32_init(void) which sets up the\r
+ *    table at run time. Useful if binary size is important.\r
+ *\r
+ *  - Compile with GENPROGRAM defined. Will create a standalone\r
+ *    program that does the initialisation and outputs the table as\r
+ *    C code.\r
+ */\r
+\r
+#define POLY (0xEDB88320L)\r
+\r
+#ifdef GENPROGRAM\r
+#define INITFUNC                      /* the gen program needs the init func :-) */\r
+#endif\r
+\r
+#ifdef INITFUNC\r
+\r
+/*\r
+ * This variant of the code generates the table at run-time from an\r
+ * init function.\r
+ */\r
+static unsigned long crc32_table[256];\r
+\r
+void crc32_init(void)\r
+{\r
+    unsigned long crcword;\r
+    int i;\r
+\r
+    for (i = 0; i < 256; i++) {\r
+       unsigned long newbyte, x32term;\r
+       int j;\r
+       crcword = 0;\r
+       newbyte = i;\r
+       for (j = 0; j < 8; j++) {\r
+           x32term = (crcword ^ newbyte) & 1;\r
+           crcword = (crcword >> 1) ^ (x32term * POLY);\r
+           newbyte >>= 1;\r
+       }\r
+       crc32_table[i] = crcword;\r
+    }\r
+}\r
+\r
+#else\r
+\r
+/*\r
+ * This variant of the code has the data already prepared.\r
+ */\r
+static const unsigned long crc32_table[256] = {\r
+    0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,\r
+    0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,\r
+    0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,\r
+    0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,\r
+    0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,\r
+    0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,\r
+    0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,\r
+    0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,\r
+    0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,\r
+    0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,\r
+    0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,\r
+    0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,\r
+    0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,\r
+    0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,\r
+    0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,\r
+    0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,\r
+    0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,\r
+    0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,\r
+    0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,\r
+    0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,\r
+    0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,\r
+    0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,\r
+    0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,\r
+    0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,\r
+    0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,\r
+    0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,\r
+    0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,\r
+    0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,\r
+    0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,\r
+    0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,\r
+    0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,\r
+    0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,\r
+    0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,\r
+    0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,\r
+    0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,\r
+    0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,\r
+    0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,\r
+    0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,\r
+    0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,\r
+    0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,\r
+    0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,\r
+    0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,\r
+    0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,\r
+    0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,\r
+    0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,\r
+    0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,\r
+    0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,\r
+    0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,\r
+    0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,\r
+    0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,\r
+    0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,\r
+    0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,\r
+    0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,\r
+    0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,\r
+    0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,\r
+    0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,\r
+    0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,\r
+    0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,\r
+    0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,\r
+    0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,\r
+    0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,\r
+    0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,\r
+    0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,\r
+    0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL\r
+};\r
+\r
+#endif\r
+\r
+#ifdef GENPROGRAM\r
+int main(void)\r
+{\r
+    unsigned long crcword;\r
+    int i;\r
+\r
+    crc32_init();\r
+    for (i = 0; i < 256; i++) {\r
+       printf("%s0x%08XL%s",\r
+              (i % 4 == 0 ? "    " : " "),\r
+              crc32_table[i],\r
+              (i % 4 == 3 ? (i == 255 ? "\n" : ",\n") : ","));\r
+    }\r
+\r
+    return 0;\r
+}\r
+#endif\r
+\r
+unsigned long crc32_update(unsigned long crcword, const void *buf, size_t len)\r
+{\r
+    const unsigned char *p = (const unsigned char *) buf;\r
+    while (len--) {\r
+       unsigned long newbyte = *p++;\r
+       newbyte ^= crcword & 0xFFL;\r
+       crcword = (crcword >> 8) ^ crc32_table[newbyte];\r
+    }\r
+    return crcword;\r
+}\r
+\r
+unsigned long crc32_compute(const void *buf, size_t len)\r
+{\r
+    return crc32_update(0L, buf, len);\r
+}\r
diff --git a/putty/SSHCRCDA.C b/putty/SSHCRCDA.C
new file mode 100644 (file)
index 0000000..c2cf705
--- /dev/null
@@ -0,0 +1,172 @@
+/*     $OpenBSD: deattack.c,v 1.14 2001/06/23 15:12:18 itojun Exp $    */\r
+\r
+/*\r
+ * Cryptographic attack detector for ssh - source code\r
+ *\r
+ * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.\r
+ *\r
+ * All rights reserved. Redistribution and use in source and binary\r
+ * forms, with or without modification, are permitted provided that\r
+ * this copyright notice is retained.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED\r
+ * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR\r
+ * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS\r
+ * SOFTWARE.\r
+ *\r
+ * Ariel Futoransky <futo@core-sdi.com>\r
+ * <http://www.core-sdi.com>\r
+ * \r
+ * Modified for use in PuTTY by Simon Tatham\r
+ */\r
+\r
+#include <assert.h>\r
+#include "misc.h"\r
+#include "ssh.h"\r
+\r
+typedef unsigned char uchar;\r
+typedef unsigned short uint16;\r
+\r
+/* SSH Constants */\r
+#define SSH_MAXBLOCKS  (32 * 1024)\r
+#define SSH_BLOCKSIZE  (8)\r
+\r
+/* Hashing constants */\r
+#define HASH_MINSIZE   (8 * 1024)\r
+#define HASH_ENTRYSIZE (sizeof(uint16))\r
+#define HASH_FACTOR(x) ((x)*3/2)\r
+#define HASH_UNUSEDCHAR        (0xff)\r
+#define HASH_UNUSED    (0xffff)\r
+#define HASH_IV        (0xfffe)\r
+\r
+#define HASH_MINBLOCKS (7*SSH_BLOCKSIZE)\r
+\r
+/* Hash function (Input keys are cipher results) */\r
+#define HASH(x)                GET_32BIT_MSB_FIRST(x)\r
+\r
+#define CMP(a, b)      (memcmp(a, b, SSH_BLOCKSIZE))\r
+\r
+uchar ONE[4] = { 1, 0, 0, 0 };\r
+uchar ZERO[4] = { 0, 0, 0, 0 };\r
+\r
+struct crcda_ctx {\r
+    uint16 *h;\r
+    uint32 n;\r
+};\r
+\r
+void *crcda_make_context(void)\r
+{\r
+    struct crcda_ctx *ret = snew(struct crcda_ctx);\r
+    ret->h = NULL;\r
+    ret->n = HASH_MINSIZE / HASH_ENTRYSIZE;\r
+    return ret;\r
+}\r
+\r
+void crcda_free_context(void *handle)\r
+{\r
+    struct crcda_ctx *ctx = (struct crcda_ctx *)handle;\r
+    if (ctx) {\r
+       sfree(ctx->h);\r
+       ctx->h = NULL;\r
+       sfree(ctx);\r
+    }\r
+}\r
+\r
+static void crc_update(uint32 *a, void *b)\r
+{\r
+    *a = crc32_update(*a, b, 4);\r
+}\r
+\r
+/* detect if a block is used in a particular pattern */\r
+static int check_crc(uchar *S, uchar *buf, uint32 len, uchar *IV)\r
+{\r
+    uint32 crc;\r
+    uchar *c;\r
+\r
+    crc = 0;\r
+    if (IV && !CMP(S, IV)) {\r
+        crc_update(&crc, ONE);\r
+        crc_update(&crc, ZERO);\r
+    }\r
+    for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {\r
+        if (!CMP(S, c)) {\r
+            crc_update(&crc, ONE);\r
+            crc_update(&crc, ZERO);\r
+        } else {\r
+            crc_update(&crc, ZERO);\r
+            crc_update(&crc, ZERO);\r
+        }\r
+    }\r
+    return (crc == 0);\r
+}\r
+\r
+/* Detect a crc32 compensation attack on a packet */\r
+int detect_attack(void *handle, uchar *buf, uint32 len, uchar *IV)\r
+{\r
+    struct crcda_ctx *ctx = (struct crcda_ctx *)handle;\r
+    register uint32 i, j;\r
+    uint32 l;\r
+    register uchar *c;\r
+    uchar *d;\r
+\r
+    assert(!(len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||\r
+             len % SSH_BLOCKSIZE != 0));\r
+    for (l = ctx->n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2)\r
+        ;\r
+\r
+    if (ctx->h == NULL) {\r
+        ctx->n = l;\r
+        ctx->h = snewn(ctx->n, uint16);\r
+    } else {\r
+        if (l > ctx->n) {\r
+            ctx->n = l;\r
+            ctx->h = sresize(ctx->h, ctx->n, uint16);\r
+        }\r
+    }\r
+\r
+    if (len <= HASH_MINBLOCKS) {\r
+        for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {\r
+            if (IV && (!CMP(c, IV))) {\r
+                if ((check_crc(c, buf, len, IV)))\r
+                    return 1;          /* attack detected */\r
+                else\r
+                    break;\r
+            }\r
+            for (d = buf; d < c; d += SSH_BLOCKSIZE) {\r
+                if (!CMP(c, d)) {\r
+                    if ((check_crc(c, buf, len, IV)))\r
+                        return 1;      /* attack detected */\r
+                    else\r
+                        break;\r
+                }\r
+            }\r
+        }\r
+        return 0;                      /* ok */\r
+    }\r
+    memset(ctx->h, HASH_UNUSEDCHAR, ctx->n * HASH_ENTRYSIZE);\r
+\r
+    if (IV)\r
+        ctx->h[HASH(IV) & (ctx->n - 1)] = HASH_IV;\r
+\r
+    for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) {\r
+        for (i = HASH(c) & (ctx->n - 1); ctx->h[i] != HASH_UNUSED;\r
+             i = (i + 1) & (ctx->n - 1)) {\r
+            if (ctx->h[i] == HASH_IV) {\r
+                if (!CMP(c, IV)) {\r
+                    if (check_crc(c, buf, len, IV))\r
+                        return 1;      /* attack detected */\r
+                    else\r
+                        break;\r
+                }\r
+            } else if (!CMP(c, buf + ctx->h[i] * SSH_BLOCKSIZE)) {\r
+                if (check_crc(c, buf, len, IV))\r
+                    return 1;          /* attack detected */\r
+                else\r
+                    break;\r
+            }\r
+        }\r
+        ctx->h[i] = j;\r
+    }\r
+    return 0;                          /* ok */\r
+}\r
diff --git a/putty/SSHDES.C b/putty/SSHDES.C
new file mode 100644 (file)
index 0000000..0beb273
--- /dev/null
@@ -0,0 +1,1031 @@
+#include <assert.h>\r
+#include "ssh.h"\r
+\r
+\r
+/* des.c - implementation of DES\r
+ */\r
+\r
+/*\r
+ * Description of DES\r
+ * ------------------\r
+ *\r
+ * Unlike the description in FIPS 46, I'm going to use _sensible_ indices:\r
+ * bits in an n-bit word are numbered from 0 at the LSB to n-1 at the MSB.\r
+ * And S-boxes are indexed by six consecutive bits, not by the outer two\r
+ * followed by the middle four.\r
+ *\r
+ * The DES encryption routine requires a 64-bit input, and a key schedule K\r
+ * containing 16 48-bit elements.\r
+ *\r
+ *   First the input is permuted by the initial permutation IP.\r
+ *   Then the input is split into 32-bit words L and R. (L is the MSW.)\r
+ *   Next, 16 rounds. In each round:\r
+ *     (L, R) <- (R, L xor f(R, K[i]))\r
+ *   Then the pre-output words L and R are swapped.\r
+ *   Then L and R are glued back together into a 64-bit word. (L is the MSW,\r
+ *     again, but since we just swapped them, the MSW is the R that came out\r
+ *     of the last round.)\r
+ *   The 64-bit output block is permuted by the inverse of IP and returned.\r
+ *\r
+ * Decryption is identical except that the elements of K are used in the\r
+ * opposite order. (This wouldn't work if that word swap didn't happen.)\r
+ *\r
+ * The function f, used in each round, accepts a 32-bit word R and a\r
+ * 48-bit key block K. It produces a 32-bit output.\r
+ *\r
+ *   First R is expanded to 48 bits using the bit-selection function E.\r
+ *   The resulting 48-bit block is XORed with the key block K to produce\r
+ *     a 48-bit block X.\r
+ *   This block X is split into eight groups of 6 bits. Each group of 6\r
+ *     bits is then looked up in one of the eight S-boxes to convert\r
+ *     it to 4 bits. These eight groups of 4 bits are glued back\r
+ *     together to produce a 32-bit preoutput block.\r
+ *   The preoutput block is permuted using the permutation P and returned.\r
+ *\r
+ * Key setup maps a 64-bit key word into a 16x48-bit key schedule. Although\r
+ * the approved input format for the key is a 64-bit word, eight of the\r
+ * bits are discarded, so the actual quantity of key used is 56 bits.\r
+ *\r
+ *   First the input key is converted to two 28-bit words C and D using\r
+ *     the bit-selection function PC1.\r
+ *   Then 16 rounds of key setup occur. In each round, C and D are each\r
+ *     rotated left by either 1 or 2 bits (depending on which round), and\r
+ *     then converted into a key schedule element using the bit-selection\r
+ *     function PC2.\r
+ *\r
+ * That's the actual algorithm. Now for the tedious details: all those\r
+ * painful permutations and lookup tables.\r
+ *\r
+ * IP is a 64-to-64 bit permutation. Its output contains the following\r
+ * bits of its input (listed in order MSB to LSB of output).\r
+ *\r
+ *    6 14 22 30 38 46 54 62  4 12 20 28 36 44 52 60\r
+ *    2 10 18 26 34 42 50 58  0  8 16 24 32 40 48 56\r
+ *    7 15 23 31 39 47 55 63  5 13 21 29 37 45 53 61\r
+ *    3 11 19 27 35 43 51 59  1  9 17 25 33 41 49 57\r
+ *\r
+ * E is a 32-to-48 bit selection function. Its output contains the following\r
+ * bits of its input (listed in order MSB to LSB of output).\r
+ *\r
+ *    0 31 30 29 28 27 28 27 26 25 24 23 24 23 22 21 20 19 20 19 18 17 16 15\r
+ *   16 15 14 13 12 11 12 11 10  9  8  7  8  7  6  5  4  3  4  3  2  1  0 31\r
+ *\r
+ * The S-boxes are arbitrary table-lookups each mapping a 6-bit input to a\r
+ * 4-bit output. In other words, each S-box is an array[64] of 4-bit numbers.\r
+ * The S-boxes are listed below. The first S-box listed is applied to the\r
+ * most significant six bits of the block X; the last one is applied to the\r
+ * least significant.\r
+ *\r
+ *   14  0  4 15 13  7  1  4  2 14 15  2 11 13  8  1\r
+ *    3 10 10  6  6 12 12 11  5  9  9  5  0  3  7  8\r
+ *    4 15  1 12 14  8  8  2 13  4  6  9  2  1 11  7\r
+ *   15  5 12 11  9  3  7 14  3 10 10  0  5  6  0 13\r
+ *\r
+ *   15  3  1 13  8  4 14  7  6 15 11  2  3  8  4 14\r
+ *    9 12  7  0  2  1 13 10 12  6  0  9  5 11 10  5\r
+ *    0 13 14  8  7 10 11  1 10  3  4 15 13  4  1  2\r
+ *    5 11  8  6 12  7  6 12  9  0  3  5  2 14 15  9\r
+ *\r
+ *   10 13  0  7  9  0 14  9  6  3  3  4 15  6  5 10\r
+ *    1  2 13  8 12  5  7 14 11 12  4 11  2 15  8  1\r
+ *   13  1  6 10  4 13  9  0  8  6 15  9  3  8  0  7\r
+ *   11  4  1 15  2 14 12  3  5 11 10  5 14  2  7 12\r
+ *\r
+ *    7 13 13  8 14 11  3  5  0  6  6 15  9  0 10  3\r
+ *    1  4  2  7  8  2  5 12 11  1 12 10  4 14 15  9\r
+ *   10  3  6 15  9  0  0  6 12 10 11  1  7 13 13  8\r
+ *   15  9  1  4  3  5 14 11  5 12  2  7  8  2  4 14\r
+ *\r
+ *    2 14 12 11  4  2  1 12  7  4 10  7 11 13  6  1\r
+ *    8  5  5  0  3 15 15 10 13  3  0  9 14  8  9  6\r
+ *    4 11  2  8  1 12 11  7 10  1 13 14  7  2  8 13\r
+ *   15  6  9 15 12  0  5  9  6 10  3  4  0  5 14  3\r
+ *\r
+ *   12 10  1 15 10  4 15  2  9  7  2 12  6  9  8  5\r
+ *    0  6 13  1  3 13  4 14 14  0  7 11  5  3 11  8\r
+ *    9  4 14  3 15  2  5 12  2  9  8  5 12 15  3 10\r
+ *    7 11  0 14  4  1 10  7  1  6 13  0 11  8  6 13\r
+ *\r
+ *    4 13 11  0  2 11 14  7 15  4  0  9  8  1 13 10\r
+ *    3 14 12  3  9  5  7 12  5  2 10 15  6  8  1  6\r
+ *    1  6  4 11 11 13 13  8 12  1  3  4  7 10 14  7\r
+ *   10  9 15  5  6  0  8 15  0 14  5  2  9  3  2 12\r
+ *\r
+ *   13  1  2 15  8 13  4  8  6 10 15  3 11  7  1  4\r
+ *   10 12  9  5  3  6 14 11  5  0  0 14 12  9  7  2\r
+ *    7  2 11  1  4 14  1  7  9  4 12 10 14  8  2 13\r
+ *    0 15  6 12 10  9 13  0 15  3  3  5  5  6  8 11\r
+ *\r
+ * P is a 32-to-32 bit permutation. Its output contains the following\r
+ * bits of its input (listed in order MSB to LSB of output).\r
+ *\r
+ *   16 25 12 11  3 20  4 15 31 17  9  6 27 14  1 22\r
+ *   30 24  8 18  0  5 29 23 13 19  2 26 10 21 28  7\r
+ *\r
+ * PC1 is a 64-to-56 bit selection function. Its output is in two words,\r
+ * C and D. The word C contains the following bits of its input (listed\r
+ * in order MSB to LSB of output).\r
+ *\r
+ *    7 15 23 31 39 47 55 63  6 14 22 30 38 46\r
+ *   54 62  5 13 21 29 37 45 53 61  4 12 20 28\r
+ *\r
+ * And the word D contains these bits.\r
+ *\r
+ *    1  9 17 25 33 41 49 57  2 10 18 26 34 42\r
+ *   50 58  3 11 19 27 35 43 51 59 36 44 52 60\r
+ *\r
+ * PC2 is a 56-to-48 bit selection function. Its input is in two words,\r
+ * C and D. These are treated as one 56-bit word (with C more significant,\r
+ * so that bits 55 to 28 of the word are bits 27 to 0 of C, and bits 27 to\r
+ * 0 of the word are bits 27 to 0 of D). The output contains the following\r
+ * bits of this 56-bit input word (listed in order MSB to LSB of output).\r
+ *\r
+ *   42 39 45 32 55 51 53 28 41 50 35 46 33 37 44 52 30 48 40 49 29 36 43 54\r
+ *   15  4 25 19  9  1 26 16  5 11 23  8 12  7 17  0 22  3 10 14  6 20 27 24\r
+ */\r
+\r
+/*\r
+ * Implementation details\r
+ * ----------------------\r
+ * \r
+ * If you look at the code in this module, you'll find it looks\r
+ * nothing _like_ the above algorithm. Here I explain the\r
+ * differences...\r
+ *\r
+ * Key setup has not been heavily optimised here. We are not\r
+ * concerned with key agility: we aren't codebreakers. We don't\r
+ * mind a little delay (and it really is a little one; it may be a\r
+ * factor of five or so slower than it could be but it's still not\r
+ * an appreciable length of time) while setting up. The only tweaks\r
+ * in the key setup are ones which change the format of the key\r
+ * schedule to speed up the actual encryption. I'll describe those\r
+ * below.\r
+ *\r
+ * The first and most obvious optimisation is the S-boxes. Since\r
+ * each S-box always targets the same four bits in the final 32-bit\r
+ * word, so the output from (for example) S-box 0 must always be\r
+ * shifted left 28 bits, we can store the already-shifted outputs\r
+ * in the lookup tables. This reduces lookup-and-shift to lookup,\r
+ * so the S-box step is now just a question of ORing together eight\r
+ * table lookups.\r
+ *\r
+ * The permutation P is just a bit order change; it's invariant\r
+ * with respect to OR, in that P(x)|P(y) = P(x|y). Therefore, we\r
+ * can apply P to every entry of the S-box tables and then we don't\r
+ * have to do it in the code of f(). This yields a set of tables\r
+ * which might be called SP-boxes.\r
+ *\r
+ * The bit-selection function E is our next target. Note that E is\r
+ * immediately followed by the operation of splitting into 6-bit\r
+ * chunks. Examining the 6-bit chunks coming out of E we notice\r
+ * they're all contiguous within the word (speaking cyclically -\r
+ * the end two wrap round); so we can extract those bit strings\r
+ * individually rather than explicitly running E. This would yield\r
+ * code such as\r
+ *\r
+ *     y |= SPboxes[0][ (rotl(R, 5) ^  top6bitsofK) & 0x3F ];\r
+ *     t |= SPboxes[1][ (rotl(R,11) ^ next6bitsofK) & 0x3F ];\r
+ *\r
+ * and so on; and the key schedule preparation would have to\r
+ * provide each 6-bit chunk separately.\r
+ *\r
+ * Really we'd like to XOR in the key schedule element before\r
+ * looking up bit strings in R. This we can't do, naively, because\r
+ * the 6-bit strings we want overlap. But look at the strings:\r
+ *\r
+ *       3322222222221111111111\r
+ * bit   10987654321098765432109876543210\r
+ * \r
+ * box0  XXXXX                          X\r
+ * box1     XXXXXX\r
+ * box2         XXXXXX\r
+ * box3             XXXXXX\r
+ * box4                 XXXXXX\r
+ * box5                     XXXXXX\r
+ * box6                         XXXXXX\r
+ * box7  X                          XXXXX\r
+ *\r
+ * The bit strings we need to XOR in for boxes 0, 2, 4 and 6 don't\r
+ * overlap with each other. Neither do the ones for boxes 1, 3, 5\r
+ * and 7. So we could provide the key schedule in the form of two\r
+ * words that we can separately XOR into R, and then every S-box\r
+ * index is available as a (cyclically) contiguous 6-bit substring\r
+ * of one or the other of the results.\r
+ *\r
+ * The comments in Eric Young's libdes implementation point out\r
+ * that two of these bit strings require a rotation (rather than a\r
+ * simple shift) to extract. It's unavoidable that at least _one_\r
+ * must do; but we can actually run the whole inner algorithm (all\r
+ * 16 rounds) rotated one bit to the left, so that what the `real'\r
+ * DES description sees as L=0x80000001 we see as L=0x00000003.\r
+ * This requires rotating all our SP-box entries one bit to the\r
+ * left, and rotating each word of the key schedule elements one to\r
+ * the left, and rotating L and R one bit left just after IP and\r
+ * one bit right again just before FP. And in each round we convert\r
+ * a rotate into a shift, so we've saved a few per cent.\r
+ *\r
+ * That's about it for the inner loop; the SP-box tables as listed\r
+ * below are what I've described here (the original S value,\r
+ * shifted to its final place in the input to P, run through P, and\r
+ * then rotated one bit left). All that remains is to optimise the\r
+ * initial permutation IP.\r
+ *\r
+ * IP is not an arbitrary permutation. It has the nice property\r
+ * that if you take any bit number, write it in binary (6 bits),\r
+ * permute those 6 bits and invert some of them, you get the final\r
+ * position of that bit. Specifically, the bit whose initial\r
+ * position is given (in binary) as fedcba ends up in position\r
+ * AcbFED (where a capital letter denotes the inverse of a bit).\r
+ *\r
+ * We have the 64-bit data in two 32-bit words L and R, where bits\r
+ * in L are those with f=1 and bits in R are those with f=0. We\r
+ * note that we can do a simple transformation: suppose we exchange\r
+ * the bits with f=1,c=0 and the bits with f=0,c=1. This will cause\r
+ * the bit fedcba to be in position cedfba - we've `swapped' bits c\r
+ * and f in the position of each bit!\r
+ * \r
+ * Better still, this transformation is easy. In the example above,\r
+ * bits in L with c=0 are bits 0x0F0F0F0F, and those in R with c=1\r
+ * are 0xF0F0F0F0. So we can do\r
+ *\r
+ *     difference = ((R >> 4) ^ L) & 0x0F0F0F0F\r
+ *     R ^= (difference << 4)\r
+ *     L ^= difference\r
+ *\r
+ * to perform the swap. Let's denote this by bitswap(4,0x0F0F0F0F).\r
+ * Also, we can invert the bit at the top just by exchanging L and\r
+ * R. So in a few swaps and a few of these bit operations we can\r
+ * do:\r
+ * \r
+ * Initially the position of bit fedcba is     fedcba\r
+ * Swap L with R to make it                    Fedcba\r
+ * Perform bitswap( 4,0x0F0F0F0F) to make it   cedFba\r
+ * Perform bitswap(16,0x0000FFFF) to make it   ecdFba\r
+ * Swap L with R to make it                    EcdFba\r
+ * Perform bitswap( 2,0x33333333) to make it   bcdFEa\r
+ * Perform bitswap( 8,0x00FF00FF) to make it   dcbFEa\r
+ * Swap L with R to make it                    DcbFEa\r
+ * Perform bitswap( 1,0x55555555) to make it   acbFED\r
+ * Swap L with R to make it                    AcbFED\r
+ *\r
+ * (In the actual code the four swaps are implicit: R and L are\r
+ * simply used the other way round in the first, second and last\r
+ * bitswap operations.)\r
+ *\r
+ * The final permutation is just the inverse of IP, so it can be\r
+ * performed by a similar set of operations.\r
+ */\r
+\r
+typedef struct {\r
+    word32 k0246[16], k1357[16];\r
+    word32 iv0, iv1;\r
+} DESContext;\r
+\r
+#define rotl(x, c) ( (x << c) | (x >> (32-c)) )\r
+#define rotl28(x, c) ( ( (x << c) | (x >> (28-c)) ) & 0x0FFFFFFF)\r
+\r
+static word32 bitsel(word32 * input, const int *bitnums, int size)\r
+{\r
+    word32 ret = 0;\r
+    while (size--) {\r
+       int bitpos = *bitnums++;\r
+       ret <<= 1;\r
+       if (bitpos >= 0)\r
+           ret |= 1 & (input[bitpos / 32] >> (bitpos % 32));\r
+    }\r
+    return ret;\r
+}\r
+\r
+static void des_key_setup(word32 key_msw, word32 key_lsw, DESContext * sched)\r
+{\r
+\r
+    static const int PC1_Cbits[] = {\r
+       7, 15, 23, 31, 39, 47, 55, 63, 6, 14, 22, 30, 38, 46,\r
+       54, 62, 5, 13, 21, 29, 37, 45, 53, 61, 4, 12, 20, 28\r
+    };\r
+    static const int PC1_Dbits[] = {\r
+       1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, 42,\r
+       50, 58, 3, 11, 19, 27, 35, 43, 51, 59, 36, 44, 52, 60\r
+    };\r
+    /*\r
+     * The bit numbers in the two lists below don't correspond to\r
+     * the ones in the above description of PC2, because in the\r
+     * above description C and D are concatenated so `bit 28' means\r
+     * bit 0 of C. In this implementation we're using the standard\r
+     * `bitsel' function above and C is in the second word, so bit\r
+     * 0 of C is addressed by writing `32' here.\r
+     */\r
+    static const int PC2_0246[] = {\r
+       49, 36, 59, 55, -1, -1, 37, 41, 48, 56, 34, 52, -1, -1, 15, 4,\r
+       25, 19, 9, 1, -1, -1, 12, 7, 17, 0, 22, 3, -1, -1, 46, 43\r
+    };\r
+    static const int PC2_1357[] = {\r
+       -1, -1, 57, 32, 45, 54, 39, 50, -1, -1, 44, 53, 33, 40, 47, 58,\r
+       -1, -1, 26, 16, 5, 11, 23, 8, -1, -1, 10, 14, 6, 20, 27, 24\r
+    };\r
+    static const int leftshifts[] =\r
+       { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };\r
+\r
+    word32 C, D;\r
+    word32 buf[2];\r
+    int i;\r
+\r
+    buf[0] = key_lsw;\r
+    buf[1] = key_msw;\r
+\r
+    C = bitsel(buf, PC1_Cbits, 28);\r
+    D = bitsel(buf, PC1_Dbits, 28);\r
+\r
+    for (i = 0; i < 16; i++) {\r
+       C = rotl28(C, leftshifts[i]);\r
+       D = rotl28(D, leftshifts[i]);\r
+       buf[0] = D;\r
+       buf[1] = C;\r
+       sched->k0246[i] = bitsel(buf, PC2_0246, 32);\r
+       sched->k1357[i] = bitsel(buf, PC2_1357, 32);\r
+    }\r
+\r
+    sched->iv0 = sched->iv1 = 0;\r
+}\r
+\r
+static const word32 SPboxes[8][64] = {\r
+    {0x01010400, 0x00000000, 0x00010000, 0x01010404,\r
+     0x01010004, 0x00010404, 0x00000004, 0x00010000,\r
+     0x00000400, 0x01010400, 0x01010404, 0x00000400,\r
+     0x01000404, 0x01010004, 0x01000000, 0x00000004,\r
+     0x00000404, 0x01000400, 0x01000400, 0x00010400,\r
+     0x00010400, 0x01010000, 0x01010000, 0x01000404,\r
+     0x00010004, 0x01000004, 0x01000004, 0x00010004,\r
+     0x00000000, 0x00000404, 0x00010404, 0x01000000,\r
+     0x00010000, 0x01010404, 0x00000004, 0x01010000,\r
+     0x01010400, 0x01000000, 0x01000000, 0x00000400,\r
+     0x01010004, 0x00010000, 0x00010400, 0x01000004,\r
+     0x00000400, 0x00000004, 0x01000404, 0x00010404,\r
+     0x01010404, 0x00010004, 0x01010000, 0x01000404,\r
+     0x01000004, 0x00000404, 0x00010404, 0x01010400,\r
+     0x00000404, 0x01000400, 0x01000400, 0x00000000,\r
+     0x00010004, 0x00010400, 0x00000000, 0x01010004L},\r
+\r
+    {0x80108020, 0x80008000, 0x00008000, 0x00108020,\r
+     0x00100000, 0x00000020, 0x80100020, 0x80008020,\r
+     0x80000020, 0x80108020, 0x80108000, 0x80000000,\r
+     0x80008000, 0x00100000, 0x00000020, 0x80100020,\r
+     0x00108000, 0x00100020, 0x80008020, 0x00000000,\r
+     0x80000000, 0x00008000, 0x00108020, 0x80100000,\r
+     0x00100020, 0x80000020, 0x00000000, 0x00108000,\r
+     0x00008020, 0x80108000, 0x80100000, 0x00008020,\r
+     0x00000000, 0x00108020, 0x80100020, 0x00100000,\r
+     0x80008020, 0x80100000, 0x80108000, 0x00008000,\r
+     0x80100000, 0x80008000, 0x00000020, 0x80108020,\r
+     0x00108020, 0x00000020, 0x00008000, 0x80000000,\r
+     0x00008020, 0x80108000, 0x00100000, 0x80000020,\r
+     0x00100020, 0x80008020, 0x80000020, 0x00100020,\r
+     0x00108000, 0x00000000, 0x80008000, 0x00008020,\r
+     0x80000000, 0x80100020, 0x80108020, 0x00108000L},\r
+\r
+    {0x00000208, 0x08020200, 0x00000000, 0x08020008,\r
+     0x08000200, 0x00000000, 0x00020208, 0x08000200,\r
+     0x00020008, 0x08000008, 0x08000008, 0x00020000,\r
+     0x08020208, 0x00020008, 0x08020000, 0x00000208,\r
+     0x08000000, 0x00000008, 0x08020200, 0x00000200,\r
+     0x00020200, 0x08020000, 0x08020008, 0x00020208,\r
+     0x08000208, 0x00020200, 0x00020000, 0x08000208,\r
+     0x00000008, 0x08020208, 0x00000200, 0x08000000,\r
+     0x08020200, 0x08000000, 0x00020008, 0x00000208,\r
+     0x00020000, 0x08020200, 0x08000200, 0x00000000,\r
+     0x00000200, 0x00020008, 0x08020208, 0x08000200,\r
+     0x08000008, 0x00000200, 0x00000000, 0x08020008,\r
+     0x08000208, 0x00020000, 0x08000000, 0x08020208,\r
+     0x00000008, 0x00020208, 0x00020200, 0x08000008,\r
+     0x08020000, 0x08000208, 0x00000208, 0x08020000,\r
+     0x00020208, 0x00000008, 0x08020008, 0x00020200L},\r
+\r
+    {0x00802001, 0x00002081, 0x00002081, 0x00000080,\r
+     0x00802080, 0x00800081, 0x00800001, 0x00002001,\r
+     0x00000000, 0x00802000, 0x00802000, 0x00802081,\r
+     0x00000081, 0x00000000, 0x00800080, 0x00800001,\r
+     0x00000001, 0x00002000, 0x00800000, 0x00802001,\r
+     0x00000080, 0x00800000, 0x00002001, 0x00002080,\r
+     0x00800081, 0x00000001, 0x00002080, 0x00800080,\r
+     0x00002000, 0x00802080, 0x00802081, 0x00000081,\r
+     0x00800080, 0x00800001, 0x00802000, 0x00802081,\r
+     0x00000081, 0x00000000, 0x00000000, 0x00802000,\r
+     0x00002080, 0x00800080, 0x00800081, 0x00000001,\r
+     0x00802001, 0x00002081, 0x00002081, 0x00000080,\r
+     0x00802081, 0x00000081, 0x00000001, 0x00002000,\r
+     0x00800001, 0x00002001, 0x00802080, 0x00800081,\r
+     0x00002001, 0x00002080, 0x00800000, 0x00802001,\r
+     0x00000080, 0x00800000, 0x00002000, 0x00802080L},\r
+\r
+    {0x00000100, 0x02080100, 0x02080000, 0x42000100,\r
+     0x00080000, 0x00000100, 0x40000000, 0x02080000,\r
+     0x40080100, 0x00080000, 0x02000100, 0x40080100,\r
+     0x42000100, 0x42080000, 0x00080100, 0x40000000,\r
+     0x02000000, 0x40080000, 0x40080000, 0x00000000,\r
+     0x40000100, 0x42080100, 0x42080100, 0x02000100,\r
+     0x42080000, 0x40000100, 0x00000000, 0x42000000,\r
+     0x02080100, 0x02000000, 0x42000000, 0x00080100,\r
+     0x00080000, 0x42000100, 0x00000100, 0x02000000,\r
+     0x40000000, 0x02080000, 0x42000100, 0x40080100,\r
+     0x02000100, 0x40000000, 0x42080000, 0x02080100,\r
+     0x40080100, 0x00000100, 0x02000000, 0x42080000,\r
+     0x42080100, 0x00080100, 0x42000000, 0x42080100,\r
+     0x02080000, 0x00000000, 0x40080000, 0x42000000,\r
+     0x00080100, 0x02000100, 0x40000100, 0x00080000,\r
+     0x00000000, 0x40080000, 0x02080100, 0x40000100L},\r
+\r
+    {0x20000010, 0x20400000, 0x00004000, 0x20404010,\r
+     0x20400000, 0x00000010, 0x20404010, 0x00400000,\r
+     0x20004000, 0x00404010, 0x00400000, 0x20000010,\r
+     0x00400010, 0x20004000, 0x20000000, 0x00004010,\r
+     0x00000000, 0x00400010, 0x20004010, 0x00004000,\r
+     0x00404000, 0x20004010, 0x00000010, 0x20400010,\r
+     0x20400010, 0x00000000, 0x00404010, 0x20404000,\r
+     0x00004010, 0x00404000, 0x20404000, 0x20000000,\r
+     0x20004000, 0x00000010, 0x20400010, 0x00404000,\r
+     0x20404010, 0x00400000, 0x00004010, 0x20000010,\r
+     0x00400000, 0x20004000, 0x20000000, 0x00004010,\r
+     0x20000010, 0x20404010, 0x00404000, 0x20400000,\r
+     0x00404010, 0x20404000, 0x00000000, 0x20400010,\r
+     0x00000010, 0x00004000, 0x20400000, 0x00404010,\r
+     0x00004000, 0x00400010, 0x20004010, 0x00000000,\r
+     0x20404000, 0x20000000, 0x00400010, 0x20004010L},\r
+\r
+    {0x00200000, 0x04200002, 0x04000802, 0x00000000,\r
+     0x00000800, 0x04000802, 0x00200802, 0x04200800,\r
+     0x04200802, 0x00200000, 0x00000000, 0x04000002,\r
+     0x00000002, 0x04000000, 0x04200002, 0x00000802,\r
+     0x04000800, 0x00200802, 0x00200002, 0x04000800,\r
+     0x04000002, 0x04200000, 0x04200800, 0x00200002,\r
+     0x04200000, 0x00000800, 0x00000802, 0x04200802,\r
+     0x00200800, 0x00000002, 0x04000000, 0x00200800,\r
+     0x04000000, 0x00200800, 0x00200000, 0x04000802,\r
+     0x04000802, 0x04200002, 0x04200002, 0x00000002,\r
+     0x00200002, 0x04000000, 0x04000800, 0x00200000,\r
+     0x04200800, 0x00000802, 0x00200802, 0x04200800,\r
+     0x00000802, 0x04000002, 0x04200802, 0x04200000,\r
+     0x00200800, 0x00000000, 0x00000002, 0x04200802,\r
+     0x00000000, 0x00200802, 0x04200000, 0x00000800,\r
+     0x04000002, 0x04000800, 0x00000800, 0x00200002L},\r
+\r
+    {0x10001040, 0x00001000, 0x00040000, 0x10041040,\r
+     0x10000000, 0x10001040, 0x00000040, 0x10000000,\r
+     0x00040040, 0x10040000, 0x10041040, 0x00041000,\r
+     0x10041000, 0x00041040, 0x00001000, 0x00000040,\r
+     0x10040000, 0x10000040, 0x10001000, 0x00001040,\r
+     0x00041000, 0x00040040, 0x10040040, 0x10041000,\r
+     0x00001040, 0x00000000, 0x00000000, 0x10040040,\r
+     0x10000040, 0x10001000, 0x00041040, 0x00040000,\r
+     0x00041040, 0x00040000, 0x10041000, 0x00001000,\r
+     0x00000040, 0x10040040, 0x00001000, 0x00041040,\r
+     0x10001000, 0x00000040, 0x10000040, 0x10040000,\r
+     0x10040040, 0x10000000, 0x00040000, 0x10001040,\r
+     0x00000000, 0x10041040, 0x00040040, 0x10000040,\r
+     0x10040000, 0x10001000, 0x10001040, 0x00000000,\r
+     0x10041040, 0x00041000, 0x00041000, 0x00001040,\r
+     0x00001040, 0x00040040, 0x10000000, 0x10041000L}\r
+};\r
+\r
+#define f(R, K0246, K1357) (\\r
+    s0246 = R ^ K0246, \\r
+    s1357 = R ^ K1357, \\r
+    s0246 = rotl(s0246, 28), \\r
+    SPboxes[0] [(s0246 >> 24) & 0x3F] | \\r
+    SPboxes[1] [(s1357 >> 24) & 0x3F] | \\r
+    SPboxes[2] [(s0246 >> 16) & 0x3F] | \\r
+    SPboxes[3] [(s1357 >> 16) & 0x3F] | \\r
+    SPboxes[4] [(s0246 >>  8) & 0x3F] | \\r
+    SPboxes[5] [(s1357 >>  8) & 0x3F] | \\r
+    SPboxes[6] [(s0246      ) & 0x3F] | \\r
+    SPboxes[7] [(s1357      ) & 0x3F])\r
+\r
+#define bitswap(L, R, n, mask) (\\r
+    swap = mask & ( (R >> n) ^ L ), \\r
+    R ^= swap << n, \\r
+    L ^= swap)\r
+\r
+/* Initial permutation */\r
+#define IP(L, R) (\\r
+    bitswap(R, L,  4, 0x0F0F0F0F), \\r
+    bitswap(R, L, 16, 0x0000FFFF), \\r
+    bitswap(L, R,  2, 0x33333333), \\r
+    bitswap(L, R,  8, 0x00FF00FF), \\r
+    bitswap(R, L,  1, 0x55555555))\r
+\r
+/* Final permutation */\r
+#define FP(L, R) (\\r
+    bitswap(R, L,  1, 0x55555555), \\r
+    bitswap(L, R,  8, 0x00FF00FF), \\r
+    bitswap(L, R,  2, 0x33333333), \\r
+    bitswap(R, L, 16, 0x0000FFFF), \\r
+    bitswap(R, L,  4, 0x0F0F0F0F))\r
+\r
+static void des_encipher(word32 * output, word32 L, word32 R,\r
+                        DESContext * sched)\r
+{\r
+    word32 swap, s0246, s1357;\r
+\r
+    IP(L, R);\r
+\r
+    L = rotl(L, 1);\r
+    R = rotl(R, 1);\r
+\r
+    L ^= f(R, sched->k0246[0], sched->k1357[0]);\r
+    R ^= f(L, sched->k0246[1], sched->k1357[1]);\r
+    L ^= f(R, sched->k0246[2], sched->k1357[2]);\r
+    R ^= f(L, sched->k0246[3], sched->k1357[3]);\r
+    L ^= f(R, sched->k0246[4], sched->k1357[4]);\r
+    R ^= f(L, sched->k0246[5], sched->k1357[5]);\r
+    L ^= f(R, sched->k0246[6], sched->k1357[6]);\r
+    R ^= f(L, sched->k0246[7], sched->k1357[7]);\r
+    L ^= f(R, sched->k0246[8], sched->k1357[8]);\r
+    R ^= f(L, sched->k0246[9], sched->k1357[9]);\r
+    L ^= f(R, sched->k0246[10], sched->k1357[10]);\r
+    R ^= f(L, sched->k0246[11], sched->k1357[11]);\r
+    L ^= f(R, sched->k0246[12], sched->k1357[12]);\r
+    R ^= f(L, sched->k0246[13], sched->k1357[13]);\r
+    L ^= f(R, sched->k0246[14], sched->k1357[14]);\r
+    R ^= f(L, sched->k0246[15], sched->k1357[15]);\r
+\r
+    L = rotl(L, 31);\r
+    R = rotl(R, 31);\r
+\r
+    swap = L;\r
+    L = R;\r
+    R = swap;\r
+\r
+    FP(L, R);\r
+\r
+    output[0] = L;\r
+    output[1] = R;\r
+}\r
+\r
+static void des_decipher(word32 * output, word32 L, word32 R,\r
+                        DESContext * sched)\r
+{\r
+    word32 swap, s0246, s1357;\r
+\r
+    IP(L, R);\r
+\r
+    L = rotl(L, 1);\r
+    R = rotl(R, 1);\r
+\r
+    L ^= f(R, sched->k0246[15], sched->k1357[15]);\r
+    R ^= f(L, sched->k0246[14], sched->k1357[14]);\r
+    L ^= f(R, sched->k0246[13], sched->k1357[13]);\r
+    R ^= f(L, sched->k0246[12], sched->k1357[12]);\r
+    L ^= f(R, sched->k0246[11], sched->k1357[11]);\r
+    R ^= f(L, sched->k0246[10], sched->k1357[10]);\r
+    L ^= f(R, sched->k0246[9], sched->k1357[9]);\r
+    R ^= f(L, sched->k0246[8], sched->k1357[8]);\r
+    L ^= f(R, sched->k0246[7], sched->k1357[7]);\r
+    R ^= f(L, sched->k0246[6], sched->k1357[6]);\r
+    L ^= f(R, sched->k0246[5], sched->k1357[5]);\r
+    R ^= f(L, sched->k0246[4], sched->k1357[4]);\r
+    L ^= f(R, sched->k0246[3], sched->k1357[3]);\r
+    R ^= f(L, sched->k0246[2], sched->k1357[2]);\r
+    L ^= f(R, sched->k0246[1], sched->k1357[1]);\r
+    R ^= f(L, sched->k0246[0], sched->k1357[0]);\r
+\r
+    L = rotl(L, 31);\r
+    R = rotl(R, 31);\r
+\r
+    swap = L;\r
+    L = R;\r
+    R = swap;\r
+\r
+    FP(L, R);\r
+\r
+    output[0] = L;\r
+    output[1] = R;\r
+}\r
+\r
+static void des_cbc_encrypt(unsigned char *blk,\r
+                           unsigned int len, DESContext * sched)\r
+{\r
+    word32 out[2], iv0, iv1;\r
+    unsigned int i;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = sched->iv0;\r
+    iv1 = sched->iv1;\r
+    for (i = 0; i < len; i += 8) {\r
+       iv0 ^= GET_32BIT_MSB_FIRST(blk);\r
+       iv1 ^= GET_32BIT_MSB_FIRST(blk + 4);\r
+       des_encipher(out, iv0, iv1, sched);\r
+       iv0 = out[0];\r
+       iv1 = out[1];\r
+       PUT_32BIT_MSB_FIRST(blk, iv0);\r
+       PUT_32BIT_MSB_FIRST(blk + 4, iv1);\r
+       blk += 8;\r
+    }\r
+    sched->iv0 = iv0;\r
+    sched->iv1 = iv1;\r
+}\r
+\r
+static void des_cbc_decrypt(unsigned char *blk,\r
+                           unsigned int len, DESContext * sched)\r
+{\r
+    word32 out[2], iv0, iv1, xL, xR;\r
+    unsigned int i;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = sched->iv0;\r
+    iv1 = sched->iv1;\r
+    for (i = 0; i < len; i += 8) {\r
+       xL = GET_32BIT_MSB_FIRST(blk);\r
+       xR = GET_32BIT_MSB_FIRST(blk + 4);\r
+       des_decipher(out, xL, xR, sched);\r
+       iv0 ^= out[0];\r
+       iv1 ^= out[1];\r
+       PUT_32BIT_MSB_FIRST(blk, iv0);\r
+       PUT_32BIT_MSB_FIRST(blk + 4, iv1);\r
+       blk += 8;\r
+       iv0 = xL;\r
+       iv1 = xR;\r
+    }\r
+    sched->iv0 = iv0;\r
+    sched->iv1 = iv1;\r
+}\r
+\r
+static void des_3cbc_encrypt(unsigned char *blk,\r
+                            unsigned int len, DESContext * scheds)\r
+{\r
+    des_cbc_encrypt(blk, len, &scheds[0]);\r
+    des_cbc_decrypt(blk, len, &scheds[1]);\r
+    des_cbc_encrypt(blk, len, &scheds[2]);\r
+}\r
+\r
+static void des_cbc3_encrypt(unsigned char *blk,\r
+                            unsigned int len, DESContext * scheds)\r
+{\r
+    word32 out[2], iv0, iv1;\r
+    unsigned int i;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = scheds->iv0;\r
+    iv1 = scheds->iv1;\r
+    for (i = 0; i < len; i += 8) {\r
+       iv0 ^= GET_32BIT_MSB_FIRST(blk);\r
+       iv1 ^= GET_32BIT_MSB_FIRST(blk + 4);\r
+       des_encipher(out, iv0, iv1, &scheds[0]);\r
+       des_decipher(out, out[0], out[1], &scheds[1]);\r
+       des_encipher(out, out[0], out[1], &scheds[2]);\r
+       iv0 = out[0];\r
+       iv1 = out[1];\r
+       PUT_32BIT_MSB_FIRST(blk, iv0);\r
+       PUT_32BIT_MSB_FIRST(blk + 4, iv1);\r
+       blk += 8;\r
+    }\r
+    scheds->iv0 = iv0;\r
+    scheds->iv1 = iv1;\r
+}\r
+\r
+static void des_3cbc_decrypt(unsigned char *blk,\r
+                            unsigned int len, DESContext * scheds)\r
+{\r
+    des_cbc_decrypt(blk, len, &scheds[2]);\r
+    des_cbc_encrypt(blk, len, &scheds[1]);\r
+    des_cbc_decrypt(blk, len, &scheds[0]);\r
+}\r
+\r
+static void des_cbc3_decrypt(unsigned char *blk,\r
+                            unsigned int len, DESContext * scheds)\r
+{\r
+    word32 out[2], iv0, iv1, xL, xR;\r
+    unsigned int i;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = scheds->iv0;\r
+    iv1 = scheds->iv1;\r
+    for (i = 0; i < len; i += 8) {\r
+       xL = GET_32BIT_MSB_FIRST(blk);\r
+       xR = GET_32BIT_MSB_FIRST(blk + 4);\r
+       des_decipher(out, xL, xR, &scheds[2]);\r
+       des_encipher(out, out[0], out[1], &scheds[1]);\r
+       des_decipher(out, out[0], out[1], &scheds[0]);\r
+       iv0 ^= out[0];\r
+       iv1 ^= out[1];\r
+       PUT_32BIT_MSB_FIRST(blk, iv0);\r
+       PUT_32BIT_MSB_FIRST(blk + 4, iv1);\r
+       blk += 8;\r
+       iv0 = xL;\r
+       iv1 = xR;\r
+    }\r
+    scheds->iv0 = iv0;\r
+    scheds->iv1 = iv1;\r
+}\r
+\r
+static void des_sdctr3(unsigned char *blk,\r
+                            unsigned int len, DESContext * scheds)\r
+{\r
+    word32 b[2], iv0, iv1, tmp;\r
+    unsigned int i;\r
+\r
+    assert((len & 7) == 0);\r
+\r
+    iv0 = scheds->iv0;\r
+    iv1 = scheds->iv1;\r
+    for (i = 0; i < len; i += 8) {\r
+       des_encipher(b, iv0, iv1, &scheds[0]);\r
+       des_decipher(b, b[0], b[1], &scheds[1]);\r
+       des_encipher(b, b[0], b[1], &scheds[2]);\r
+       tmp = GET_32BIT_MSB_FIRST(blk);\r
+       PUT_32BIT_MSB_FIRST(blk, tmp ^ b[0]);\r
+       blk += 4;\r
+       tmp = GET_32BIT_MSB_FIRST(blk);\r
+       PUT_32BIT_MSB_FIRST(blk, tmp ^ b[1]);\r
+       blk += 4;\r
+       if ((iv1 = (iv1 + 1) & 0xffffffff) == 0)\r
+           iv0 = (iv0 + 1) & 0xffffffff;\r
+    }\r
+    scheds->iv0 = iv0;\r
+    scheds->iv1 = iv1;\r
+}\r
+\r
+static void *des3_make_context(void)\r
+{\r
+    return snewn(3, DESContext);\r
+}\r
+\r
+static void *des3_ssh1_make_context(void)\r
+{\r
+    /* Need 3 keys for each direction, in SSH-1 */\r
+    return snewn(6, DESContext);\r
+}\r
+\r
+static void *des_make_context(void)\r
+{\r
+    return snew(DESContext);\r
+}\r
+\r
+static void *des_ssh1_make_context(void)\r
+{\r
+    /* Need one key for each direction, in SSH-1 */\r
+    return snewn(2, DESContext);\r
+}\r
+\r
+static void des3_free_context(void *handle)   /* used for both 3DES and DES */\r
+{\r
+    sfree(handle);\r
+}\r
+\r
+static void des3_key(void *handle, unsigned char *key)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &keys[0]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),\r
+                 GET_32BIT_MSB_FIRST(key + 12), &keys[1]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 16),\r
+                 GET_32BIT_MSB_FIRST(key + 20), &keys[2]);\r
+}\r
+\r
+static void des3_iv(void *handle, unsigned char *key)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    keys[0].iv0 = GET_32BIT_MSB_FIRST(key);\r
+    keys[0].iv1 = GET_32BIT_MSB_FIRST(key + 4);\r
+}\r
+\r
+static void des_key(void *handle, unsigned char *key)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &keys[0]);\r
+}\r
+\r
+static void des3_sesskey(void *handle, unsigned char *key)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des3_key(keys, key);\r
+    des3_key(keys+3, key);\r
+}\r
+\r
+static void des3_encrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_3cbc_encrypt(blk, len, keys);\r
+}\r
+\r
+static void des3_decrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_3cbc_decrypt(blk, len, keys+3);\r
+}\r
+\r
+static void des3_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_cbc3_encrypt(blk, len, keys);\r
+}\r
+\r
+static void des3_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_cbc3_decrypt(blk, len, keys);\r
+}\r
+\r
+static void des3_ssh2_sdctr(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_sdctr3(blk, len, keys);\r
+}\r
+\r
+static void des_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_cbc_encrypt(blk, len, keys);\r
+}\r
+\r
+static void des_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_cbc_decrypt(blk, len, keys);\r
+}\r
+\r
+void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len)\r
+{\r
+    DESContext ourkeys[3];\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),\r
+                 GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]);\r
+    des_3cbc_decrypt(blk, len, ourkeys);\r
+    memset(ourkeys, 0, sizeof(ourkeys));\r
+}\r
+\r
+void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len)\r
+{\r
+    DESContext ourkeys[3];\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),\r
+                 GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]);\r
+    des_3cbc_encrypt(blk, len, ourkeys);\r
+    memset(ourkeys, 0, sizeof(ourkeys));\r
+}\r
+\r
+void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,\r
+                             unsigned char *blk, int len)\r
+{\r
+    DESContext ourkeys[3];\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),\r
+                 GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 16),\r
+                 GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]);\r
+    ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv);\r
+    ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4);\r
+    des_cbc3_decrypt(blk, len, ourkeys);\r
+    memset(ourkeys, 0, sizeof(ourkeys));\r
+}\r
+\r
+void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,\r
+                             unsigned char *blk, int len)\r
+{\r
+    DESContext ourkeys[3];\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key),\r
+                 GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),\r
+                 GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 16),\r
+                 GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]);\r
+    ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv);\r
+    ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4);\r
+    des_cbc3_encrypt(blk, len, ourkeys);\r
+    memset(ourkeys, 0, sizeof(ourkeys));\r
+}\r
+\r
+static void des_keysetup_xdmauth(unsigned char *keydata, DESContext *dc)\r
+{\r
+    unsigned char key[8];\r
+    int i, nbits, j;\r
+    unsigned int bits;\r
+\r
+    bits = 0;\r
+    nbits = 0;\r
+    j = 0;\r
+    for (i = 0; i < 8; i++) {\r
+       if (nbits < 7) {\r
+           bits = (bits << 8) | keydata[j];\r
+           nbits += 8;\r
+           j++;\r
+       }\r
+       key[i] = (bits >> (nbits - 7)) << 1;\r
+       bits &= ~(0x7F << (nbits - 7));\r
+       nbits -= 7;\r
+    }\r
+\r
+    des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), dc);\r
+}\r
+\r
+void des_encrypt_xdmauth(unsigned char *keydata, unsigned char *blk, int len)\r
+{\r
+    DESContext dc;\r
+    des_keysetup_xdmauth(keydata, &dc);\r
+    des_cbc_encrypt(blk, 24, &dc);\r
+}\r
+\r
+void des_decrypt_xdmauth(unsigned char *keydata, unsigned char *blk, int len)\r
+{\r
+    DESContext dc;\r
+    des_keysetup_xdmauth(keydata, &dc);\r
+    des_cbc_decrypt(blk, 24, &dc);\r
+}\r
+\r
+static const struct ssh2_cipher ssh_3des_ssh2 = {\r
+    des3_make_context, des3_free_context, des3_iv, des3_key,\r
+    des3_ssh2_encrypt_blk, des3_ssh2_decrypt_blk,\r
+    "3des-cbc",\r
+    8, 168, SSH_CIPHER_IS_CBC, "triple-DES CBC"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_3des_ssh2_ctr = {\r
+    des3_make_context, des3_free_context, des3_iv, des3_key,\r
+    des3_ssh2_sdctr, des3_ssh2_sdctr,\r
+    "3des-ctr",\r
+    8, 168, 0, "triple-DES SDCTR"\r
+};\r
+\r
+/*\r
+ * Single DES in SSH-2. "des-cbc" is marked as HISTORIC in\r
+ * RFC 4250, referring to\r
+ * FIPS-46-3.  ("Single DES (i.e., DES) will be permitted \r
+ * for legacy systems only.") , but ssh.com support it and \r
+ * apparently aren't the only people to do so, so we sigh \r
+ * and implement it anyway.\r
+ */\r
+static const struct ssh2_cipher ssh_des_ssh2 = {\r
+    des_make_context, des3_free_context, des3_iv, des_key,\r
+    des_ssh2_encrypt_blk, des_ssh2_decrypt_blk,\r
+    "des-cbc",\r
+    8, 56, SSH_CIPHER_IS_CBC, "single-DES CBC"\r
+};\r
+\r
+static const struct ssh2_cipher ssh_des_sshcom_ssh2 = {\r
+    des_make_context, des3_free_context, des3_iv, des_key,\r
+    des_ssh2_encrypt_blk, des_ssh2_decrypt_blk,\r
+    "des-cbc@ssh.com",\r
+    8, 56, SSH_CIPHER_IS_CBC, "single-DES CBC"\r
+};\r
+\r
+static const struct ssh2_cipher *const des3_list[] = {\r
+    &ssh_3des_ssh2_ctr,\r
+    &ssh_3des_ssh2\r
+};\r
+\r
+const struct ssh2_ciphers ssh2_3des = {\r
+    sizeof(des3_list) / sizeof(*des3_list),\r
+    des3_list\r
+};\r
+\r
+static const struct ssh2_cipher *const des_list[] = {\r
+    &ssh_des_ssh2,\r
+    &ssh_des_sshcom_ssh2\r
+};\r
+\r
+const struct ssh2_ciphers ssh2_des = {\r
+    sizeof(des_list) / sizeof(*des_list),\r
+    des_list\r
+};\r
+\r
+const struct ssh_cipher ssh_3des = {\r
+    des3_ssh1_make_context, des3_free_context, des3_sesskey,\r
+    des3_encrypt_blk, des3_decrypt_blk,\r
+    8, "triple-DES inner-CBC"\r
+};\r
+\r
+static void des_sesskey(void *handle, unsigned char *key)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_key(keys, key);\r
+    des_key(keys+1, key);\r
+}\r
+\r
+static void des_encrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_cbc_encrypt(blk, len, keys);\r
+}\r
+\r
+static void des_decrypt_blk(void *handle, unsigned char *blk, int len)\r
+{\r
+    DESContext *keys = (DESContext *) handle;\r
+    des_cbc_decrypt(blk, len, keys+1);\r
+}\r
+\r
+const struct ssh_cipher ssh_des = {\r
+    des_ssh1_make_context, des3_free_context, des_sesskey,\r
+    des_encrypt_blk, des_decrypt_blk,\r
+    8, "single-DES CBC"\r
+};\r
diff --git a/putty/SSHDH.C b/putty/SSHDH.C
new file mode 100644 (file)
index 0000000..41df756
--- /dev/null
@@ -0,0 +1,230 @@
+/*\r
+ * Diffie-Hellman implementation for PuTTY.\r
+ */\r
+\r
+#include "ssh.h"\r
+\r
+/*\r
+ * The primes used in the group1 and group14 key exchange.\r
+ */\r
+static const unsigned char P1[] = {\r
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,\r
+    0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,\r
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,\r
+    0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,\r
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,\r
+    0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,\r
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,\r
+    0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,\r
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,\r
+    0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,\r
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF\r
+};\r
+static const unsigned char P14[] = {\r
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,\r
+    0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,\r
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,\r
+    0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,\r
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,\r
+    0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,\r
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,\r
+    0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,\r
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,\r
+    0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,\r
+    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,\r
+    0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,\r
+    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,\r
+    0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,\r
+    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,\r
+    0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,\r
+    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2,\r
+    0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,\r
+    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C,\r
+    0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,\r
+    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF,\r
+    0xFF, 0xFF, 0xFF, 0xFF\r
+};\r
+\r
+/*\r
+ * The generator g = 2 (used for both group1 and group14).\r
+ */\r
+static const unsigned char G[] = { 2 };\r
+\r
+static const struct ssh_kex ssh_diffiehellman_group1_sha1 = {\r
+    "diffie-hellman-group1-sha1", "group1",\r
+    KEXTYPE_DH, P1, G, lenof(P1), lenof(G), &ssh_sha1\r
+};\r
+\r
+static const struct ssh_kex *const group1_list[] = {\r
+    &ssh_diffiehellman_group1_sha1\r
+};\r
+\r
+const struct ssh_kexes ssh_diffiehellman_group1 = {\r
+    sizeof(group1_list) / sizeof(*group1_list),\r
+    group1_list\r
+};\r
+\r
+static const struct ssh_kex ssh_diffiehellman_group14_sha1 = {\r
+    "diffie-hellman-group14-sha1", "group14",\r
+    KEXTYPE_DH, P14, G, lenof(P14), lenof(G), &ssh_sha1\r
+};\r
+\r
+static const struct ssh_kex *const group14_list[] = {\r
+    &ssh_diffiehellman_group14_sha1\r
+};\r
+\r
+const struct ssh_kexes ssh_diffiehellman_group14 = {\r
+    sizeof(group14_list) / sizeof(*group14_list),\r
+    group14_list\r
+};\r
+\r
+static const struct ssh_kex ssh_diffiehellman_gex_sha256 = {\r
+    "diffie-hellman-group-exchange-sha256", NULL,\r
+    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha256\r
+};\r
+\r
+static const struct ssh_kex ssh_diffiehellman_gex_sha1 = {\r
+    "diffie-hellman-group-exchange-sha1", NULL,\r
+    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha1\r
+};\r
+\r
+static const struct ssh_kex *const gex_list[] = {\r
+    &ssh_diffiehellman_gex_sha256,\r
+    &ssh_diffiehellman_gex_sha1\r
+};\r
+\r
+const struct ssh_kexes ssh_diffiehellman_gex = {\r
+    sizeof(gex_list) / sizeof(*gex_list),\r
+    gex_list\r
+};\r
+\r
+/*\r
+ * Variables.\r
+ */\r
+struct dh_ctx {\r
+    Bignum x, e, p, q, qmask, g;\r
+};\r
+\r
+/*\r
+ * Common DH initialisation.\r
+ */\r
+static void dh_init(struct dh_ctx *ctx)\r
+{\r
+    ctx->q = bignum_rshift(ctx->p, 1);\r
+    ctx->qmask = bignum_bitmask(ctx->q);\r
+    ctx->x = ctx->e = NULL;\r
+}\r
+\r
+/*\r
+ * Initialise DH for a standard group.\r
+ */\r
+void *dh_setup_group(const struct ssh_kex *kex)\r
+{\r
+    struct dh_ctx *ctx = snew(struct dh_ctx);\r
+    ctx->p = bignum_from_bytes(kex->pdata, kex->plen);\r
+    ctx->g = bignum_from_bytes(kex->gdata, kex->glen);\r
+    dh_init(ctx);\r
+    return ctx;\r
+}\r
+\r
+/*\r
+ * Initialise DH for a server-supplied group.\r
+ */\r
+void *dh_setup_gex(Bignum pval, Bignum gval)\r
+{\r
+    struct dh_ctx *ctx = snew(struct dh_ctx);\r
+    ctx->p = copybn(pval);\r
+    ctx->g = copybn(gval);\r
+    dh_init(ctx);\r
+    return ctx;\r
+}\r
+\r
+/*\r
+ * Clean up and free a context.\r
+ */\r
+void dh_cleanup(void *handle)\r
+{\r
+    struct dh_ctx *ctx = (struct dh_ctx *)handle;\r
+    freebn(ctx->x);\r
+    freebn(ctx->e);\r
+    freebn(ctx->p);\r
+    freebn(ctx->g);\r
+    freebn(ctx->q);\r
+    freebn(ctx->qmask);\r
+    sfree(ctx);\r
+}\r
+\r
+/*\r
+ * DH stage 1: invent a number x between 1 and q, and compute e =\r
+ * g^x mod p. Return e.\r
+ * \r
+ * If `nbits' is greater than zero, it is used as an upper limit\r
+ * for the number of bits in x. This is safe provided that (a) you\r
+ * use twice as many bits in x as the number of bits you expect to\r
+ * use in your session key, and (b) the DH group is a safe prime\r
+ * (which SSH demands that it must be).\r
+ * \r
+ * P. C. van Oorschot, M. J. Wiener\r
+ * "On Diffie-Hellman Key Agreement with Short Exponents".\r
+ * Advances in Cryptology: Proceedings of Eurocrypt '96\r
+ * Springer-Verlag, May 1996.\r
+ */\r
+Bignum dh_create_e(void *handle, int nbits)\r
+{\r
+    struct dh_ctx *ctx = (struct dh_ctx *)handle;\r
+    int i;\r
+\r
+    int nbytes;\r
+    unsigned char *buf;\r
+\r
+    nbytes = ssh1_bignum_length(ctx->qmask);\r
+    buf = snewn(nbytes, unsigned char);\r
+\r
+    do {\r
+       /*\r
+        * Create a potential x, by ANDing a string of random bytes\r
+        * with qmask.\r
+        */\r
+       if (ctx->x)\r
+           freebn(ctx->x);\r
+       if (nbits == 0 || nbits > bignum_bitcount(ctx->qmask)) {\r
+           ssh1_write_bignum(buf, ctx->qmask);\r
+           for (i = 2; i < nbytes; i++)\r
+               buf[i] &= random_byte();\r
+           ssh1_read_bignum(buf, nbytes, &ctx->x);   /* can't fail */\r
+       } else {\r
+           int b, nb;\r
+           ctx->x = bn_power_2(nbits);\r
+           b = nb = 0;\r
+           for (i = 0; i < nbits; i++) {\r
+               if (nb == 0) {\r
+                   nb = 8;\r
+                   b = random_byte();\r
+               }\r
+               bignum_set_bit(ctx->x, i, b & 1);\r
+               b >>= 1;\r
+               nb--;\r
+           }\r
+       }\r
+    } while (bignum_cmp(ctx->x, One) <= 0 || bignum_cmp(ctx->x, ctx->q) >= 0);\r
+\r
+    sfree(buf);\r
+\r
+    /*\r
+     * Done. Now compute e = g^x mod p.\r
+     */\r
+    ctx->e = modpow(ctx->g, ctx->x, ctx->p);\r
+\r
+    return ctx->e;\r
+}\r
+\r
+/*\r
+ * DH stage 2: given a number f, compute K = f^x mod p.\r
+ */\r
+Bignum dh_find_K(void *handle, Bignum f)\r
+{\r
+    struct dh_ctx *ctx = (struct dh_ctx *)handle;\r
+    Bignum ret;\r
+    ret = modpow(f, ctx->x, ctx->p);\r
+    return ret;\r
+}\r
diff --git a/putty/SSHDSS.C b/putty/SSHDSS.C
new file mode 100644 (file)
index 0000000..dba1db1
--- /dev/null
@@ -0,0 +1,643 @@
+/*\r
+ * Digital Signature Standard implementation for PuTTY.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+#include "ssh.h"\r
+#include "misc.h"\r
+\r
+static void sha_mpint(SHA_State * s, Bignum b)\r
+{\r
+    unsigned char lenbuf[4];\r
+    int len;\r
+    len = (bignum_bitcount(b) + 8) / 8;\r
+    PUT_32BIT(lenbuf, len);\r
+    SHA_Bytes(s, lenbuf, 4);\r
+    while (len-- > 0) {\r
+       lenbuf[0] = bignum_byte(b, len);\r
+       SHA_Bytes(s, lenbuf, 1);\r
+    }\r
+    memset(lenbuf, 0, sizeof(lenbuf));\r
+}\r
+\r
+static void sha512_mpint(SHA512_State * s, Bignum b)\r
+{\r
+    unsigned char lenbuf[4];\r
+    int len;\r
+    len = (bignum_bitcount(b) + 8) / 8;\r
+    PUT_32BIT(lenbuf, len);\r
+    SHA512_Bytes(s, lenbuf, 4);\r
+    while (len-- > 0) {\r
+       lenbuf[0] = bignum_byte(b, len);\r
+       SHA512_Bytes(s, lenbuf, 1);\r
+    }\r
+    memset(lenbuf, 0, sizeof(lenbuf));\r
+}\r
+\r
+static void getstring(char **data, int *datalen, char **p, int *length)\r
+{\r
+    *p = NULL;\r
+    if (*datalen < 4)\r
+       return;\r
+    *length = GET_32BIT(*data);\r
+    *datalen -= 4;\r
+    *data += 4;\r
+    if (*datalen < *length)\r
+       return;\r
+    *p = *data;\r
+    *data += *length;\r
+    *datalen -= *length;\r
+}\r
+static Bignum getmp(char **data, int *datalen)\r
+{\r
+    char *p;\r
+    int length;\r
+    Bignum b;\r
+\r
+    getstring(data, datalen, &p, &length);\r
+    if (!p)\r
+       return NULL;\r
+    if (p[0] & 0x80)\r
+       return NULL;                   /* negative mp */\r
+    b = bignum_from_bytes((unsigned char *)p, length);\r
+    return b;\r
+}\r
+\r
+static Bignum get160(char **data, int *datalen)\r
+{\r
+    Bignum b;\r
+\r
+    b = bignum_from_bytes((unsigned char *)*data, 20);\r
+    *data += 20;\r
+    *datalen -= 20;\r
+\r
+    return b;\r
+}\r
+\r
+static void *dss_newkey(char *data, int len)\r
+{\r
+    char *p;\r
+    int slen;\r
+    struct dss_key *dss;\r
+\r
+    dss = snew(struct dss_key);\r
+    if (!dss)\r
+       return NULL;\r
+    getstring(&data, &len, &p, &slen);\r
+\r
+#ifdef DEBUG_DSS\r
+    {\r
+       int i;\r
+       printf("key:");\r
+       for (i = 0; i < len; i++)\r
+           printf("  %02x", (unsigned char) (data[i]));\r
+       printf("\n");\r
+    }\r
+#endif\r
+\r
+    if (!p || memcmp(p, "ssh-dss", 7)) {\r
+       sfree(dss);\r
+       return NULL;\r
+    }\r
+    dss->p = getmp(&data, &len);\r
+    dss->q = getmp(&data, &len);\r
+    dss->g = getmp(&data, &len);\r
+    dss->y = getmp(&data, &len);\r
+\r
+    return dss;\r
+}\r
+\r
+static void dss_freekey(void *key)\r
+{\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    freebn(dss->p);\r
+    freebn(dss->q);\r
+    freebn(dss->g);\r
+    freebn(dss->y);\r
+    sfree(dss);\r
+}\r
+\r
+static char *dss_fmtkey(void *key)\r
+{\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    char *p;\r
+    int len, i, pos, nibbles;\r
+    static const char hex[] = "0123456789abcdef";\r
+    if (!dss->p)\r
+       return NULL;\r
+    len = 8 + 4 + 1;                  /* 4 x "0x", punctuation, \0 */\r
+    len += 4 * (bignum_bitcount(dss->p) + 15) / 16;\r
+    len += 4 * (bignum_bitcount(dss->q) + 15) / 16;\r
+    len += 4 * (bignum_bitcount(dss->g) + 15) / 16;\r
+    len += 4 * (bignum_bitcount(dss->y) + 15) / 16;\r
+    p = snewn(len, char);\r
+    if (!p)\r
+       return NULL;\r
+\r
+    pos = 0;\r
+    pos += sprintf(p + pos, "0x");\r
+    nibbles = (3 + bignum_bitcount(dss->p)) / 4;\r
+    if (nibbles < 1)\r
+       nibbles = 1;\r
+    for (i = nibbles; i--;)\r
+       p[pos++] =\r
+           hex[(bignum_byte(dss->p, i / 2) >> (4 * (i % 2))) & 0xF];\r
+    pos += sprintf(p + pos, ",0x");\r
+    nibbles = (3 + bignum_bitcount(dss->q)) / 4;\r
+    if (nibbles < 1)\r
+       nibbles = 1;\r
+    for (i = nibbles; i--;)\r
+       p[pos++] =\r
+           hex[(bignum_byte(dss->q, i / 2) >> (4 * (i % 2))) & 0xF];\r
+    pos += sprintf(p + pos, ",0x");\r
+    nibbles = (3 + bignum_bitcount(dss->g)) / 4;\r
+    if (nibbles < 1)\r
+       nibbles = 1;\r
+    for (i = nibbles; i--;)\r
+       p[pos++] =\r
+           hex[(bignum_byte(dss->g, i / 2) >> (4 * (i % 2))) & 0xF];\r
+    pos += sprintf(p + pos, ",0x");\r
+    nibbles = (3 + bignum_bitcount(dss->y)) / 4;\r
+    if (nibbles < 1)\r
+       nibbles = 1;\r
+    for (i = nibbles; i--;)\r
+       p[pos++] =\r
+           hex[(bignum_byte(dss->y, i / 2) >> (4 * (i % 2))) & 0xF];\r
+    p[pos] = '\0';\r
+    return p;\r
+}\r
+\r
+static char *dss_fingerprint(void *key)\r
+{\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    struct MD5Context md5c;\r
+    unsigned char digest[16], lenbuf[4];\r
+    char buffer[16 * 3 + 40];\r
+    char *ret;\r
+    int numlen, i;\r
+\r
+    MD5Init(&md5c);\r
+    MD5Update(&md5c, (unsigned char *)"\0\0\0\7ssh-dss", 11);\r
+\r
+#define ADD_BIGNUM(bignum) \\r
+    numlen = (bignum_bitcount(bignum)+8)/8; \\r
+    PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \\r
+    for (i = numlen; i-- ;) { \\r
+        unsigned char c = bignum_byte(bignum, i); \\r
+        MD5Update(&md5c, &c, 1); \\r
+    }\r
+    ADD_BIGNUM(dss->p);\r
+    ADD_BIGNUM(dss->q);\r
+    ADD_BIGNUM(dss->g);\r
+    ADD_BIGNUM(dss->y);\r
+#undef ADD_BIGNUM\r
+\r
+    MD5Final(digest, &md5c);\r
+\r
+    sprintf(buffer, "ssh-dss %d ", bignum_bitcount(dss->p));\r
+    for (i = 0; i < 16; i++)\r
+       sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",\r
+               digest[i]);\r
+    ret = snewn(strlen(buffer) + 1, char);\r
+    if (ret)\r
+       strcpy(ret, buffer);\r
+    return ret;\r
+}\r
+\r
+static int dss_verifysig(void *key, char *sig, int siglen,\r
+                        char *data, int datalen)\r
+{\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    char *p;\r
+    int slen;\r
+    char hash[20];\r
+    Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v;\r
+    int ret;\r
+\r
+    if (!dss->p)\r
+       return 0;\r
+\r
+#ifdef DEBUG_DSS\r
+    {\r
+       int i;\r
+       printf("sig:");\r
+       for (i = 0; i < siglen; i++)\r
+           printf("  %02x", (unsigned char) (sig[i]));\r
+       printf("\n");\r
+    }\r
+#endif\r
+    /*\r
+     * Commercial SSH (2.0.13) and OpenSSH disagree over the format\r
+     * of a DSA signature. OpenSSH is in line with RFC 4253:\r
+     * it uses a string "ssh-dss", followed by a 40-byte string\r
+     * containing two 160-bit integers end-to-end. Commercial SSH\r
+     * can't be bothered with the header bit, and considers a DSA\r
+     * signature blob to be _just_ the 40-byte string containing\r
+     * the two 160-bit integers. We tell them apart by measuring\r
+     * the length: length 40 means the commercial-SSH bug, anything\r
+     * else is assumed to be RFC-compliant.\r
+     */\r
+    if (siglen != 40) {                       /* bug not present; read admin fields */\r
+       getstring(&sig, &siglen, &p, &slen);\r
+       if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {\r
+           return 0;\r
+       }\r
+       sig += 4, siglen -= 4;         /* skip yet another length field */\r
+    }\r
+    r = get160(&sig, &siglen);\r
+    s = get160(&sig, &siglen);\r
+    if (!r || !s)\r
+       return 0;\r
+\r
+    /*\r
+     * Step 1. w <- s^-1 mod q.\r
+     */\r
+    w = modinv(s, dss->q);\r
+\r
+    /*\r
+     * Step 2. u1 <- SHA(message) * w mod q.\r
+     */\r
+    SHA_Simple(data, datalen, (unsigned char *)hash);\r
+    p = hash;\r
+    slen = 20;\r
+    sha = get160(&p, &slen);\r
+    u1 = modmul(sha, w, dss->q);\r
+\r
+    /*\r
+     * Step 3. u2 <- r * w mod q.\r
+     */\r
+    u2 = modmul(r, w, dss->q);\r
+\r
+    /*\r
+     * Step 4. v <- (g^u1 * y^u2 mod p) mod q.\r
+     */\r
+    gu1p = modpow(dss->g, u1, dss->p);\r
+    yu2p = modpow(dss->y, u2, dss->p);\r
+    gu1yu2p = modmul(gu1p, yu2p, dss->p);\r
+    v = modmul(gu1yu2p, One, dss->q);\r
+\r
+    /*\r
+     * Step 5. v should now be equal to r.\r
+     */\r
+\r
+    ret = !bignum_cmp(v, r);\r
+\r
+    freebn(w);\r
+    freebn(sha);\r
+    freebn(gu1p);\r
+    freebn(yu2p);\r
+    freebn(gu1yu2p);\r
+    freebn(v);\r
+    freebn(r);\r
+    freebn(s);\r
+\r
+    return ret;\r
+}\r
+\r
+static unsigned char *dss_public_blob(void *key, int *len)\r
+{\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    int plen, qlen, glen, ylen, bloblen;\r
+    int i;\r
+    unsigned char *blob, *p;\r
+\r
+    plen = (bignum_bitcount(dss->p) + 8) / 8;\r
+    qlen = (bignum_bitcount(dss->q) + 8) / 8;\r
+    glen = (bignum_bitcount(dss->g) + 8) / 8;\r
+    ylen = (bignum_bitcount(dss->y) + 8) / 8;\r
+\r
+    /*\r
+     * string "ssh-dss", mpint p, mpint q, mpint g, mpint y. Total\r
+     * 27 + sum of lengths. (five length fields, 20+7=27).\r
+     */\r
+    bloblen = 27 + plen + qlen + glen + ylen;\r
+    blob = snewn(bloblen, unsigned char);\r
+    p = blob;\r
+    PUT_32BIT(p, 7);\r
+    p += 4;\r
+    memcpy(p, "ssh-dss", 7);\r
+    p += 7;\r
+    PUT_32BIT(p, plen);\r
+    p += 4;\r
+    for (i = plen; i--;)\r
+       *p++ = bignum_byte(dss->p, i);\r
+    PUT_32BIT(p, qlen);\r
+    p += 4;\r
+    for (i = qlen; i--;)\r
+       *p++ = bignum_byte(dss->q, i);\r
+    PUT_32BIT(p, glen);\r
+    p += 4;\r
+    for (i = glen; i--;)\r
+       *p++ = bignum_byte(dss->g, i);\r
+    PUT_32BIT(p, ylen);\r
+    p += 4;\r
+    for (i = ylen; i--;)\r
+       *p++ = bignum_byte(dss->y, i);\r
+    assert(p == blob + bloblen);\r
+    *len = bloblen;\r
+    return blob;\r
+}\r
+\r
+static unsigned char *dss_private_blob(void *key, int *len)\r
+{\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    int xlen, bloblen;\r
+    int i;\r
+    unsigned char *blob, *p;\r
+\r
+    xlen = (bignum_bitcount(dss->x) + 8) / 8;\r
+\r
+    /*\r
+     * mpint x, string[20] the SHA of p||q||g. Total 4 + xlen.\r
+     */\r
+    bloblen = 4 + xlen;\r
+    blob = snewn(bloblen, unsigned char);\r
+    p = blob;\r
+    PUT_32BIT(p, xlen);\r
+    p += 4;\r
+    for (i = xlen; i--;)\r
+       *p++ = bignum_byte(dss->x, i);\r
+    assert(p == blob + bloblen);\r
+    *len = bloblen;\r
+    return blob;\r
+}\r
+\r
+static void *dss_createkey(unsigned char *pub_blob, int pub_len,\r
+                          unsigned char *priv_blob, int priv_len)\r
+{\r
+    struct dss_key *dss;\r
+    char *pb = (char *) priv_blob;\r
+    char *hash;\r
+    int hashlen;\r
+    SHA_State s;\r
+    unsigned char digest[20];\r
+    Bignum ytest;\r
+\r
+    dss = dss_newkey((char *) pub_blob, pub_len);\r
+    dss->x = getmp(&pb, &priv_len);\r
+\r
+    /*\r
+     * Check the obsolete hash in the old DSS key format.\r
+     */\r
+    hashlen = -1;\r
+    getstring(&pb, &priv_len, &hash, &hashlen);\r
+    if (hashlen == 20) {\r
+       SHA_Init(&s);\r
+       sha_mpint(&s, dss->p);\r
+       sha_mpint(&s, dss->q);\r
+       sha_mpint(&s, dss->g);\r
+       SHA_Final(&s, digest);\r
+       if (0 != memcmp(hash, digest, 20)) {\r
+           dss_freekey(dss);\r
+           return NULL;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Now ensure g^x mod p really is y.\r
+     */\r
+    ytest = modpow(dss->g, dss->x, dss->p);\r
+    if (0 != bignum_cmp(ytest, dss->y)) {\r
+       dss_freekey(dss);\r
+       return NULL;\r
+    }\r
+    freebn(ytest);\r
+\r
+    return dss;\r
+}\r
+\r
+static void *dss_openssh_createkey(unsigned char **blob, int *len)\r
+{\r
+    char **b = (char **) blob;\r
+    struct dss_key *dss;\r
+\r
+    dss = snew(struct dss_key);\r
+    if (!dss)\r
+       return NULL;\r
+\r
+    dss->p = getmp(b, len);\r
+    dss->q = getmp(b, len);\r
+    dss->g = getmp(b, len);\r
+    dss->y = getmp(b, len);\r
+    dss->x = getmp(b, len);\r
+\r
+    if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x) {\r
+       sfree(dss->p);\r
+       sfree(dss->q);\r
+       sfree(dss->g);\r
+       sfree(dss->y);\r
+       sfree(dss->x);\r
+       sfree(dss);\r
+       return NULL;\r
+    }\r
+\r
+    return dss;\r
+}\r
+\r
+static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len)\r
+{\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    int bloblen, i;\r
+\r
+    bloblen =\r
+       ssh2_bignum_length(dss->p) +\r
+       ssh2_bignum_length(dss->q) +\r
+       ssh2_bignum_length(dss->g) +\r
+       ssh2_bignum_length(dss->y) +\r
+       ssh2_bignum_length(dss->x);\r
+\r
+    if (bloblen > len)\r
+       return bloblen;\r
+\r
+    bloblen = 0;\r
+#define ENC(x) \\r
+    PUT_32BIT(blob+bloblen, ssh2_bignum_length((x))-4); bloblen += 4; \\r
+    for (i = ssh2_bignum_length((x))-4; i-- ;) blob[bloblen++]=bignum_byte((x),i);\r
+    ENC(dss->p);\r
+    ENC(dss->q);\r
+    ENC(dss->g);\r
+    ENC(dss->y);\r
+    ENC(dss->x);\r
+\r
+    return bloblen;\r
+}\r
+\r
+static int dss_pubkey_bits(void *blob, int len)\r
+{\r
+    struct dss_key *dss;\r
+    int ret;\r
+\r
+    dss = dss_newkey((char *) blob, len);\r
+    ret = bignum_bitcount(dss->p);\r
+    dss_freekey(dss);\r
+\r
+    return ret;\r
+}\r
+\r
+static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)\r
+{\r
+    /*\r
+     * The basic DSS signing algorithm is:\r
+     * \r
+     *  - invent a random k between 1 and q-1 (exclusive).\r
+     *  - Compute r = (g^k mod p) mod q.\r
+     *  - Compute s = k^-1 * (hash + x*r) mod q.\r
+     * \r
+     * This has the dangerous properties that:\r
+     * \r
+     *  - if an attacker in possession of the public key _and_ the\r
+     *    signature (for example, the host you just authenticated\r
+     *    to) can guess your k, he can reverse the computation of s\r
+     *    and work out x = r^-1 * (s*k - hash) mod q. That is, he\r
+     *    can deduce the private half of your key, and masquerade\r
+     *    as you for as long as the key is still valid.\r
+     * \r
+     *  - since r is a function purely of k and the public key, if\r
+     *    the attacker only has a _range of possibilities_ for k\r
+     *    it's easy for him to work through them all and check each\r
+     *    one against r; he'll never be unsure of whether he's got\r
+     *    the right one.\r
+     * \r
+     *  - if you ever sign two different hashes with the same k, it\r
+     *    will be immediately obvious because the two signatures\r
+     *    will have the same r, and moreover an attacker in\r
+     *    possession of both signatures (and the public key of\r
+     *    course) can compute k = (hash1-hash2) * (s1-s2)^-1 mod q,\r
+     *    and from there deduce x as before.\r
+     * \r
+     *  - the Bleichenbacher attack on DSA makes use of methods of\r
+     *    generating k which are significantly non-uniformly\r
+     *    distributed; in particular, generating a 160-bit random\r
+     *    number and reducing it mod q is right out.\r
+     * \r
+     * For this reason we must be pretty careful about how we\r
+     * generate our k. Since this code runs on Windows, with no\r
+     * particularly good system entropy sources, we can't trust our\r
+     * RNG itself to produce properly unpredictable data. Hence, we\r
+     * use a totally different scheme instead.\r
+     * \r
+     * What we do is to take a SHA-512 (_big_) hash of the private\r
+     * key x, and then feed this into another SHA-512 hash that\r
+     * also includes the message hash being signed. That is:\r
+     * \r
+     *   proto_k = SHA512 ( SHA512(x) || SHA160(message) )\r
+     * \r
+     * This number is 512 bits long, so reducing it mod q won't be\r
+     * noticeably non-uniform. So\r
+     * \r
+     *   k = proto_k mod q\r
+     * \r
+     * This has the interesting property that it's _deterministic_:\r
+     * signing the same hash twice with the same key yields the\r
+     * same signature.\r
+     * \r
+     * Despite this determinism, it's still not predictable to an\r
+     * attacker, because in order to repeat the SHA-512\r
+     * construction that created it, the attacker would have to\r
+     * know the private key value x - and by assumption he doesn't,\r
+     * because if he knew that he wouldn't be attacking k!\r
+     *\r
+     * (This trick doesn't, _per se_, protect against reuse of k.\r
+     * Reuse of k is left to chance; all it does is prevent\r
+     * _excessively high_ chances of reuse of k due to entropy\r
+     * problems.)\r
+     * \r
+     * Thanks to Colin Plumb for the general idea of using x to\r
+     * ensure k is hard to guess, and to the Cambridge University\r
+     * Computer Security Group for helping to argue out all the\r
+     * fine details.\r
+     */\r
+    struct dss_key *dss = (struct dss_key *) key;\r
+    SHA512_State ss;\r
+    unsigned char digest[20], digest512[64];\r
+    Bignum proto_k, k, gkp, hash, kinv, hxr, r, s;\r
+    unsigned char *bytes;\r
+    int nbytes, i;\r
+\r
+    SHA_Simple(data, datalen, digest);\r
+\r
+    /*\r
+     * Hash some identifying text plus x.\r
+     */\r
+    SHA512_Init(&ss);\r
+    SHA512_Bytes(&ss, "DSA deterministic k generator", 30);\r
+    sha512_mpint(&ss, dss->x);\r
+    SHA512_Final(&ss, digest512);\r
+\r
+    /*\r
+     * Now hash that digest plus the message hash.\r
+     */\r
+    SHA512_Init(&ss);\r
+    SHA512_Bytes(&ss, digest512, sizeof(digest512));\r
+    SHA512_Bytes(&ss, digest, sizeof(digest));\r
+    SHA512_Final(&ss, digest512);\r
+\r
+    memset(&ss, 0, sizeof(ss));\r
+\r
+    /*\r
+     * Now convert the result into a bignum, and reduce it mod q.\r
+     */\r
+    proto_k = bignum_from_bytes(digest512, 64);\r
+    k = bigmod(proto_k, dss->q);\r
+    freebn(proto_k);\r
+\r
+    memset(digest512, 0, sizeof(digest512));\r
+\r
+    /*\r
+     * Now we have k, so just go ahead and compute the signature.\r
+     */\r
+    gkp = modpow(dss->g, k, dss->p);   /* g^k mod p */\r
+    r = bigmod(gkp, dss->q);          /* r = (g^k mod p) mod q */\r
+    freebn(gkp);\r
+\r
+    hash = bignum_from_bytes(digest, 20);\r
+    kinv = modinv(k, dss->q);         /* k^-1 mod q */\r
+    hxr = bigmuladd(dss->x, r, hash);  /* hash + x*r */\r
+    s = modmul(kinv, hxr, dss->q);     /* s = k^-1 * (hash + x*r) mod q */\r
+    freebn(hxr);\r
+    freebn(kinv);\r
+    freebn(hash);\r
+\r
+    /*\r
+     * Signature blob is\r
+     * \r
+     *   string  "ssh-dss"\r
+     *   string  two 20-byte numbers r and s, end to end\r
+     * \r
+     * i.e. 4+7 + 4+40 bytes.\r
+     */\r
+    nbytes = 4 + 7 + 4 + 40;\r
+    bytes = snewn(nbytes, unsigned char);\r
+    PUT_32BIT(bytes, 7);\r
+    memcpy(bytes + 4, "ssh-dss", 7);\r
+    PUT_32BIT(bytes + 4 + 7, 40);\r
+    for (i = 0; i < 20; i++) {\r
+       bytes[4 + 7 + 4 + i] = bignum_byte(r, 19 - i);\r
+       bytes[4 + 7 + 4 + 20 + i] = bignum_byte(s, 19 - i);\r
+    }\r
+    freebn(r);\r
+    freebn(s);\r
+\r
+    *siglen = nbytes;\r
+    return bytes;\r
+}\r
+\r
+const struct ssh_signkey ssh_dss = {\r
+    dss_newkey,\r
+    dss_freekey,\r
+    dss_fmtkey,\r
+    dss_public_blob,\r
+    dss_private_blob,\r
+    dss_createkey,\r
+    dss_openssh_createkey,\r
+    dss_openssh_fmtkey,\r
+    dss_pubkey_bits,\r
+    dss_fingerprint,\r
+    dss_verifysig,\r
+    dss_sign,\r
+    "ssh-dss",\r
+    "dss"\r
+};\r
diff --git a/putty/SSHDSSG.C b/putty/SSHDSSG.C
new file mode 100644 (file)
index 0000000..a19bc27
--- /dev/null
@@ -0,0 +1,143 @@
+/*\r
+ * DSS key generation.\r
+ */\r
+\r
+#include "misc.h"\r
+#include "ssh.h"\r
+\r
+int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,\r
+                void *pfnparam)\r
+{\r
+    Bignum qm1, power, g, h, tmp;\r
+    int progress;\r
+\r
+    /*\r
+     * Set up the phase limits for the progress report. We do this\r
+     * by passing minus the phase number.\r
+     *\r
+     * For prime generation: our initial filter finds things\r
+     * coprime to everything below 2^16. Computing the product of\r
+     * (p-1)/p for all prime p below 2^16 gives about 20.33; so\r
+     * among B-bit integers, one in every 20.33 will get through\r
+     * the initial filter to be a candidate prime.\r
+     *\r
+     * Meanwhile, we are searching for primes in the region of 2^B;\r
+     * since pi(x) ~ x/log(x), when x is in the region of 2^B, the\r
+     * prime density will be d/dx pi(x) ~ 1/log(B), i.e. about\r
+     * 1/0.6931B. So the chance of any given candidate being prime\r
+     * is 20.33/0.6931B, which is roughly 29.34 divided by B.\r
+     *\r
+     * So now we have this probability P, we're looking at an\r
+     * exponential distribution with parameter P: we will manage in\r
+     * one attempt with probability P, in two with probability\r
+     * P(1-P), in three with probability P(1-P)^2, etc. The\r
+     * probability that we have still not managed to find a prime\r
+     * after N attempts is (1-P)^N.\r
+     * \r
+     * We therefore inform the progress indicator of the number B\r
+     * (29.34/B), so that it knows how much to increment by each\r
+     * time. We do this in 16-bit fixed point, so 29.34 becomes\r
+     * 0x1D.57C4.\r
+     */\r
+    pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x2800);\r
+    pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / 160);\r
+    pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x40 * bits);\r
+    pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / bits);\r
+\r
+    /*\r
+     * In phase three we are finding an order-q element of the\r
+     * multiplicative group of p, by finding an element whose order\r
+     * is _divisible_ by q and raising it to the power of (p-1)/q.\r
+     * _Most_ elements will have order divisible by q, since for a\r
+     * start phi(p) of them will be primitive roots. So\r
+     * realistically we don't need to set this much below 1 (64K).\r
+     * Still, we'll set it to 1/2 (32K) to be on the safe side.\r
+     */\r
+    pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x2000);\r
+    pfn(pfnparam, PROGFN_EXP_PHASE, 3, -32768);\r
+\r
+    /*\r
+     * In phase four we are finding an element x between 1 and q-1\r
+     * (exclusive), by inventing 160 random bits and hoping they\r
+     * come out to a plausible number; so assuming q is uniformly\r
+     * distributed between 2^159 and 2^160, the chance of any given\r
+     * attempt succeeding is somewhere between 0.5 and 1. Lacking\r
+     * the energy to arrange to be able to specify this probability\r
+     * _after_ generating q, we'll just set it to 0.75.\r
+     */\r
+    pfn(pfnparam, PROGFN_PHASE_EXTENT, 4, 0x2000);\r
+    pfn(pfnparam, PROGFN_EXP_PHASE, 4, -49152);\r
+\r
+    pfn(pfnparam, PROGFN_READY, 0, 0);\r
+\r
+    /*\r
+     * Generate q: a prime of length 160.\r
+     */\r
+    key->q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam);\r
+    /*\r
+     * Now generate p: a prime of length `bits', such that p-1 is\r
+     * divisible by q.\r
+     */\r
+    key->p = primegen(bits-160, 2, 2, key->q, 2, pfn, pfnparam);\r
+\r
+    /*\r
+     * Next we need g. Raise 2 to the power (p-1)/q modulo p, and\r
+     * if that comes out to one then try 3, then 4 and so on. As\r
+     * soon as we hit a non-unit (and non-zero!) one, that'll do\r
+     * for g.\r
+     */\r
+    power = bigdiv(key->p, key->q);    /* this is floor(p/q) == (p-1)/q */\r
+    h = bignum_from_long(1);\r
+    progress = 0;\r
+    while (1) {\r
+       pfn(pfnparam, PROGFN_PROGRESS, 3, ++progress);\r
+       g = modpow(h, power, key->p);\r
+       if (bignum_cmp(g, One) > 0)\r
+           break;                     /* got one */\r
+       tmp = h;\r
+       h = bignum_add_long(h, 1);\r
+       freebn(tmp);\r
+    }\r
+    key->g = g;\r
+    freebn(h);\r
+\r
+    /*\r
+     * Now we're nearly done. All we need now is our private key x,\r
+     * which should be a number between 1 and q-1 exclusive, and\r
+     * our public key y = g^x mod p.\r
+     */\r
+    qm1 = copybn(key->q);\r
+    decbn(qm1);\r
+    progress = 0;\r
+    while (1) {\r
+       int i, v, byte, bitsleft;\r
+       Bignum x;\r
+\r
+       pfn(pfnparam, PROGFN_PROGRESS, 4, ++progress);\r
+       x = bn_power_2(159);\r
+       byte = 0;\r
+       bitsleft = 0;\r
+\r
+       for (i = 0; i < 160; i++) {\r
+           if (bitsleft <= 0)\r
+               bitsleft = 8, byte = random_byte();\r
+           v = byte & 1;\r
+           byte >>= 1;\r
+           bitsleft--;\r
+           bignum_set_bit(x, i, v);\r
+       }\r
+\r
+       if (bignum_cmp(x, One) <= 0 || bignum_cmp(x, qm1) >= 0) {\r
+           freebn(x);\r
+           continue;\r
+       } else {\r
+           key->x = x;\r
+           break;\r
+       }\r
+    }\r
+    freebn(qm1);\r
+\r
+    key->y = modpow(key->g, key->x, key->p);\r
+\r
+    return 1;\r
+}\r
diff --git a/putty/SSHGSS.H b/putty/SSHGSS.H
new file mode 100644 (file)
index 0000000..5d8fca1
--- /dev/null
@@ -0,0 +1,188 @@
+#ifndef PUTTY_SSHGSS_H\r
+#define PUTTY_SSHGSS_H\r
+#include "putty.h"\r
+#include "pgssapi.h"\r
+\r
+#ifndef NO_GSSAPI\r
+\r
+#define SSH2_GSS_OIDTYPE 0x06\r
+typedef void *Ssh_gss_ctx;\r
+\r
+typedef enum Ssh_gss_stat {\r
+    SSH_GSS_OK = 0,\r
+    SSH_GSS_S_CONTINUE_NEEDED,\r
+    SSH_GSS_NO_MEM,\r
+    SSH_GSS_BAD_HOST_NAME,\r
+    SSH_GSS_FAILURE\r
+} Ssh_gss_stat;\r
+\r
+#define SSH_GSS_S_COMPLETE SSH_GSS_OK\r
+\r
+#define SSH_GSS_CLEAR_BUF(buf) do {            \\r
+    (*buf).length = 0;                         \\r
+    (*buf).value = NULL;                               \\r
+} while (0)\r
+\r
+typedef gss_buffer_desc Ssh_gss_buf;\r
+typedef gss_name_t Ssh_gss_name;\r
+\r
+/* Functions, provided by either wingss.c or sshgssc.c */\r
+\r
+struct ssh_gss_library;\r
+\r
+/*\r
+ * Prepare a collection of GSSAPI libraries for use in a single SSH\r
+ * connection. Returns a structure containing a list of libraries,\r
+ * with their ids (see struct ssh_gss_library below) filled in so\r
+ * that the client can go through them in the SSH user's preferred\r
+ * order.\r
+ *\r
+ * Must always return non-NULL. (Even if no libraries are available,\r
+ * it must return an empty structure.)\r
+ *\r
+ * The free function cleans up the structure, and its associated\r
+ * libraries (if any).\r
+ */\r
+struct ssh_gss_liblist {\r
+    struct ssh_gss_library *libraries;\r
+    int nlibraries;\r
+};\r
+struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg);\r
+void ssh_gss_cleanup(struct ssh_gss_liblist *list);\r
+\r
+/*\r
+ * Fills in buf with a string describing the GSSAPI mechanism in\r
+ * use. buf->data is not dynamically allocated.\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_indicate_mech)(struct ssh_gss_library *lib,\r
+                                               Ssh_gss_buf *buf);\r
+\r
+/*\r
+ * Converts a name such as a hostname into a GSSAPI internal form,\r
+ * which is placed in "out". The result should be freed by\r
+ * ssh_gss_release_name().\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_import_name)(struct ssh_gss_library *lib,\r
+                                             char *in, Ssh_gss_name *out);\r
+\r
+/*\r
+ * Frees the contents of an Ssh_gss_name structure filled in by\r
+ * ssh_gss_import_name().\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib,\r
+                                              Ssh_gss_name *name);\r
+\r
+/*\r
+ * The main GSSAPI security context setup function. The "out"\r
+ * parameter will need to be freed by ssh_gss_free_tok.\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context)\r
+    (struct ssh_gss_library *lib,\r
+     Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,\r
+     Ssh_gss_buf *in, Ssh_gss_buf *out);\r
+\r
+/*\r
+ * Frees the contents of an Ssh_gss_buf filled in by\r
+ * ssh_gss_init_sec_context(). Do not accidentally call this on\r
+ * something filled in by ssh_gss_get_mic() (which requires a\r
+ * different free function) or something filled in by any other\r
+ * way.\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib,\r
+                                          Ssh_gss_buf *);\r
+\r
+/*\r
+ * Acquires the credentials to perform authentication in the first\r
+ * place. Needs to be freed by ssh_gss_release_cred().\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib,\r
+                                              Ssh_gss_ctx *);\r
+\r
+/*\r
+ * Frees the contents of an Ssh_gss_ctx filled in by\r
+ * ssh_gss_acquire_cred().\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib,\r
+                                              Ssh_gss_ctx *);\r
+\r
+/*\r
+ * Gets a MIC for some input data. "out" needs to be freed by\r
+ * ssh_gss_free_mic().\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib,\r
+                                         Ssh_gss_ctx ctx, Ssh_gss_buf *in,\r
+                 Ssh_gss_buf *out);\r
+\r
+/*\r
+ * Frees the contents of an Ssh_gss_buf filled in by\r
+ * ssh_gss_get_mic(). Do not accidentally call this on something\r
+ * filled in by ssh_gss_init_sec_context() (which requires a\r
+ * different free function) or something filled in by any other\r
+ * way.\r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_free_mic)(struct ssh_gss_library *lib,\r
+                                          Ssh_gss_buf *);\r
+\r
+/*\r
+ * Return an error message after authentication failed. The\r
+ * message string is returned in "buf", with buf->len giving the\r
+ * number of characters of printable message text and buf->data\r
+ * containing one more character which is a trailing NUL.\r
+ * buf->data should be manually freed by the caller. \r
+ */\r
+typedef Ssh_gss_stat (*t_ssh_gss_display_status)(struct ssh_gss_library *lib,\r
+                                                Ssh_gss_ctx, Ssh_gss_buf *buf);\r
+\r
+struct ssh_gss_library {\r
+    /*\r
+     * Identifying number in the enumeration used by the\r
+     * configuration code to specify a preference order.\r
+     */\r
+    int id;\r
+\r
+    /*\r
+     * Filled in at initialisation time, if there's anything\r
+     * interesting to say about how GSSAPI was initialised (e.g.\r
+     * which of a number of alternative libraries was used).\r
+     */\r
+    const char *gsslogmsg;\r
+\r
+    /*\r
+     * Function pointers implementing the SSH wrapper layer on top\r
+     * of GSSAPI. (Defined in sshgssc, typically, though Windows\r
+     * provides an alternative layer to sit on top of the annoyingly\r
+     * different SSPI.)\r
+     */\r
+    t_ssh_gss_indicate_mech indicate_mech;\r
+    t_ssh_gss_import_name import_name;\r
+    t_ssh_gss_release_name release_name;\r
+    t_ssh_gss_init_sec_context init_sec_context;\r
+    t_ssh_gss_free_tok free_tok;\r
+    t_ssh_gss_acquire_cred acquire_cred;\r
+    t_ssh_gss_release_cred release_cred;\r
+    t_ssh_gss_get_mic get_mic;\r
+    t_ssh_gss_free_mic free_mic;\r
+    t_ssh_gss_display_status display_status;\r
+\r
+    /*\r
+     * Additional data for the wrapper layers.\r
+     */\r
+    union {\r
+       struct gssapi_functions gssapi;\r
+       /*\r
+        * The SSPI wrappers don't need to store their Windows API\r
+        * function pointers in this structure, because there can't\r
+        * be more than one set of them available.\r
+        */\r
+    } u;\r
+\r
+    /*\r
+     * Wrapper layers will often also need to store a library handle\r
+     * of some sort for cleanup time.\r
+     */\r
+    void *handle;\r
+};\r
+\r
+#endif /* NO_GSSAPI */\r
+\r
+#endif /*PUTTY_SSHGSS_H*/\r
diff --git a/putty/SSHGSSC.C b/putty/SSHGSSC.C
new file mode 100644 (file)
index 0000000..87e2236
--- /dev/null
@@ -0,0 +1,209 @@
+#include "putty.h"\r
+\r
+#include <string.h>\r
+#include "sshgssc.h"\r
+#include "misc.h"\r
+\r
+#ifndef NO_GSSAPI\r
+\r
+static Ssh_gss_stat ssh_gssapi_indicate_mech(struct ssh_gss_library *lib,\r
+                                            Ssh_gss_buf *mech)\r
+{\r
+    /* Copy constant into mech */\r
+    mech->length  = GSS_MECH_KRB5->length;\r
+    mech->value = GSS_MECH_KRB5->elements;\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib,\r
+                                          char *host,\r
+                                          Ssh_gss_name *srv_name)\r
+{\r
+    struct gssapi_functions *gss = &lib->u.gssapi;\r
+    OM_uint32 min_stat,maj_stat;\r
+    gss_buffer_desc host_buf;\r
+    char *pStr;\r
+\r
+    pStr = dupcat("host@", host, NULL);\r
+\r
+    host_buf.value = pStr;\r
+    host_buf.length = strlen(pStr);\r
+\r
+    maj_stat = gss->import_name(&min_stat, &host_buf,\r
+                               GSS_C_NT_HOSTBASED_SERVICE, srv_name);\r
+    /* Release buffer */\r
+    sfree(pStr);\r
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;\r
+    return SSH_GSS_FAILURE;\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_acquire_cred(struct ssh_gss_library *lib,\r
+                                           Ssh_gss_ctx *ctx)\r
+{\r
+    gssapi_ssh_gss_ctx *gssctx = snew(gssapi_ssh_gss_ctx);\r
+\r
+    gssctx->maj_stat =  gssctx->min_stat = GSS_S_COMPLETE;\r
+    gssctx->ctx = GSS_C_NO_CONTEXT;\r
+    *ctx = (Ssh_gss_ctx) gssctx;\r
+\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_init_sec_context(struct ssh_gss_library *lib,\r
+                                               Ssh_gss_ctx *ctx,\r
+                                               Ssh_gss_name srv_name,\r
+                                               int to_deleg,\r
+                                               Ssh_gss_buf *recv_tok,\r
+                                               Ssh_gss_buf *send_tok)\r
+{\r
+    struct gssapi_functions *gss = &lib->u.gssapi;\r
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx*) *ctx;\r
+    OM_uint32 ret_flags;\r
+\r
+    if (to_deleg) to_deleg = GSS_C_DELEG_FLAG;\r
+    gssctx->maj_stat = gss->init_sec_context(&gssctx->min_stat,\r
+                                            GSS_C_NO_CREDENTIAL,\r
+                                            &gssctx->ctx,\r
+                                            srv_name,\r
+                                            (gss_OID) GSS_MECH_KRB5,\r
+                                            GSS_C_MUTUAL_FLAG |\r
+                                            GSS_C_INTEG_FLAG | to_deleg,\r
+                                            0,\r
+                                            GSS_C_NO_CHANNEL_BINDINGS,\r
+                                            recv_tok,\r
+                                            NULL,   /* ignore mech type */\r
+                                            send_tok,\r
+                                            &ret_flags,\r
+                                            NULL);  /* ignore time_rec */\r
+\r
+    if (gssctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE;\r
+    if (gssctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;\r
+    return SSH_GSS_FAILURE;\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_display_status(struct ssh_gss_library *lib,\r
+                                             Ssh_gss_ctx ctx,\r
+                                             Ssh_gss_buf *buf)\r
+{\r
+    struct gssapi_functions *gss = &lib->u.gssapi;\r
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;\r
+    OM_uint32 lmin,lmax;\r
+    OM_uint32 ccc;\r
+    gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER;\r
+    gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER;\r
+\r
+    /* Return empty buffer in case of failure */\r
+    SSH_GSS_CLEAR_BUF(buf);\r
+\r
+    /* get first mesg from GSS */\r
+    ccc=0;\r
+    lmax=gss->display_status(&lmin,gssctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_maj);\r
+\r
+    if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE;\r
+\r
+    /* get first mesg from Kerberos */\r
+    ccc=0;\r
+    lmax=gss->display_status(&lmin,gssctx->min_stat,GSS_C_MECH_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_min);\r
+\r
+    if (lmax != GSS_S_COMPLETE) {\r
+        gss->release_buffer(&lmin, &msg_maj);\r
+        return SSH_GSS_FAILURE;\r
+    }\r
+\r
+    /* copy data into buffer */\r
+    buf->length = msg_maj.length + msg_min.length + 1;\r
+    buf->value = snewn(buf->length + 1, char);\r
+\r
+    /* copy mem */\r
+    memcpy((char *)buf->value, msg_maj.value, msg_maj.length);\r
+    ((char *)buf->value)[msg_maj.length] = ' ';\r
+    memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length);\r
+    ((char *)buf->value)[buf->length] = 0;\r
+    /* free mem & exit */\r
+    gss->release_buffer(&lmin, &msg_maj);\r
+    gss->release_buffer(&lmin, &msg_min);\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_free_tok(struct ssh_gss_library *lib,\r
+                                       Ssh_gss_buf *send_tok)\r
+{\r
+    struct gssapi_functions *gss = &lib->u.gssapi;\r
+    OM_uint32 min_stat,maj_stat;\r
+    maj_stat = gss->release_buffer(&min_stat, send_tok);\r
+\r
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;\r
+    return SSH_GSS_FAILURE;\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_release_cred(struct ssh_gss_library *lib,\r
+                                           Ssh_gss_ctx *ctx)\r
+{\r
+    struct gssapi_functions *gss = &lib->u.gssapi;\r
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) *ctx;\r
+    OM_uint32 min_stat;\r
+    OM_uint32 maj_stat=GSS_S_COMPLETE;\r
+\r
+    if (gssctx == NULL) return SSH_GSS_FAILURE;\r
+    if (gssctx->ctx != GSS_C_NO_CONTEXT)\r
+        maj_stat = gss->delete_sec_context(&min_stat,&gssctx->ctx,GSS_C_NO_BUFFER);\r
+    sfree(gssctx);\r
+\r
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;\r
+    return SSH_GSS_FAILURE;\r
+}\r
+\r
+\r
+static Ssh_gss_stat ssh_gssapi_release_name(struct ssh_gss_library *lib,\r
+                                           Ssh_gss_name *srv_name)\r
+{\r
+    struct gssapi_functions *gss = &lib->u.gssapi;\r
+    OM_uint32 min_stat,maj_stat;\r
+    maj_stat = gss->release_name(&min_stat, srv_name);\r
+\r
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;\r
+    return SSH_GSS_FAILURE;\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_get_mic(struct ssh_gss_library *lib,\r
+                                      Ssh_gss_ctx ctx, Ssh_gss_buf *buf,\r
+                                      Ssh_gss_buf *hash)\r
+{\r
+    struct gssapi_functions *gss = &lib->u.gssapi;\r
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;\r
+    if (gssctx == NULL) return SSH_GSS_FAILURE;\r
+    return gss->get_mic(&(gssctx->min_stat), gssctx->ctx, 0, buf, hash);\r
+}\r
+\r
+static Ssh_gss_stat ssh_gssapi_free_mic(struct ssh_gss_library *lib,\r
+                                       Ssh_gss_buf *hash)\r
+{\r
+    /* On Unix this is the same freeing process as ssh_gssapi_free_tok. */\r
+    return ssh_gssapi_free_tok(lib, hash);\r
+}\r
+\r
+void ssh_gssapi_bind_fns(struct ssh_gss_library *lib)\r
+{\r
+    lib->indicate_mech = ssh_gssapi_indicate_mech;\r
+    lib->import_name = ssh_gssapi_import_name;\r
+    lib->release_name = ssh_gssapi_release_name;\r
+    lib->init_sec_context = ssh_gssapi_init_sec_context;\r
+    lib->free_tok = ssh_gssapi_free_tok;\r
+    lib->acquire_cred = ssh_gssapi_acquire_cred;\r
+    lib->release_cred = ssh_gssapi_release_cred;\r
+    lib->get_mic = ssh_gssapi_get_mic;\r
+    lib->free_mic = ssh_gssapi_free_mic;\r
+    lib->display_status = ssh_gssapi_display_status;\r
+}\r
+\r
+#else\r
+\r
+/* Dummy function so this source file defines something if NO_GSSAPI\r
+   is defined. */\r
+\r
+int ssh_gssapi_init(void)\r
+{\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/SSHGSSC.H b/putty/SSHGSSC.H
new file mode 100644 (file)
index 0000000..0f271f8
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef PUTTY_SSHGSSC_H\r
+#define PUTTY_SSHGSSC_H\r
+#include "putty.h"\r
+#ifndef NO_GSSAPI\r
+\r
+#include "pgssapi.h"\r
+#include "sshgss.h"\r
+\r
+typedef struct gssapi_ssh_gss_ctx {\r
+    OM_uint32 maj_stat;\r
+    OM_uint32 min_stat;\r
+    gss_ctx_id_t ctx;\r
+} gssapi_ssh_gss_ctx;\r
+\r
+void ssh_gssapi_bind_fns(struct ssh_gss_library *lib);\r
+\r
+#else\r
+\r
+int ssh_gssapi_init(void);\r
+\r
+#endif /*NO_GSSAPI*/\r
+\r
+#endif /*PUTTY_SSHGSSC_H*/\r
diff --git a/putty/SSHMD5.C b/putty/SSHMD5.C
new file mode 100644 (file)
index 0000000..80474df
--- /dev/null
@@ -0,0 +1,344 @@
+#include "ssh.h"\r
+\r
+/*\r
+ * MD5 implementation for PuTTY. Written directly from the spec by\r
+ * Simon Tatham.\r
+ */\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Core MD5 algorithm: processes 16-word blocks into a message digest.\r
+ */\r
+\r
+#define F(x,y,z) ( ((x) & (y)) | ((~(x)) & (z)) )\r
+#define G(x,y,z) ( ((x) & (z)) | ((~(z)) & (y)) )\r
+#define H(x,y,z) ( (x) ^ (y) ^ (z) )\r
+#define I(x,y,z) ( (y) ^ ( (x) | ~(z) ) )\r
+\r
+#define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) )\r
+\r
+#define subround(f,w,x,y,z,k,s,ti) \\r
+       w = x + rol(w + f(x,y,z) + block[k] + ti, s)\r
+\r
+static void MD5_Core_Init(MD5_Core_State * s)\r
+{\r
+    s->h[0] = 0x67452301;\r
+    s->h[1] = 0xefcdab89;\r
+    s->h[2] = 0x98badcfe;\r
+    s->h[3] = 0x10325476;\r
+}\r
+\r
+static void MD5_Block(MD5_Core_State * s, uint32 * block)\r
+{\r
+    uint32 a, b, c, d;\r
+\r
+    a = s->h[0];\r
+    b = s->h[1];\r
+    c = s->h[2];\r
+    d = s->h[3];\r
+\r
+    subround(F, a, b, c, d, 0, 7, 0xd76aa478);\r
+    subround(F, d, a, b, c, 1, 12, 0xe8c7b756);\r
+    subround(F, c, d, a, b, 2, 17, 0x242070db);\r
+    subround(F, b, c, d, a, 3, 22, 0xc1bdceee);\r
+    subround(F, a, b, c, d, 4, 7, 0xf57c0faf);\r
+    subround(F, d, a, b, c, 5, 12, 0x4787c62a);\r
+    subround(F, c, d, a, b, 6, 17, 0xa8304613);\r
+    subround(F, b, c, d, a, 7, 22, 0xfd469501);\r
+    subround(F, a, b, c, d, 8, 7, 0x698098d8);\r
+    subround(F, d, a, b, c, 9, 12, 0x8b44f7af);\r
+    subround(F, c, d, a, b, 10, 17, 0xffff5bb1);\r
+    subround(F, b, c, d, a, 11, 22, 0x895cd7be);\r
+    subround(F, a, b, c, d, 12, 7, 0x6b901122);\r
+    subround(F, d, a, b, c, 13, 12, 0xfd987193);\r
+    subround(F, c, d, a, b, 14, 17, 0xa679438e);\r
+    subround(F, b, c, d, a, 15, 22, 0x49b40821);\r
+    subround(G, a, b, c, d, 1, 5, 0xf61e2562);\r
+    subround(G, d, a, b, c, 6, 9, 0xc040b340);\r
+    subround(G, c, d, a, b, 11, 14, 0x265e5a51);\r
+    subround(G, b, c, d, a, 0, 20, 0xe9b6c7aa);\r
+    subround(G, a, b, c, d, 5, 5, 0xd62f105d);\r
+    subround(G, d, a, b, c, 10, 9, 0x02441453);\r
+    subround(G, c, d, a, b, 15, 14, 0xd8a1e681);\r
+    subround(G, b, c, d, a, 4, 20, 0xe7d3fbc8);\r
+    subround(G, a, b, c, d, 9, 5, 0x21e1cde6);\r
+    subround(G, d, a, b, c, 14, 9, 0xc33707d6);\r
+    subround(G, c, d, a, b, 3, 14, 0xf4d50d87);\r
+    subround(G, b, c, d, a, 8, 20, 0x455a14ed);\r
+    subround(G, a, b, c, d, 13, 5, 0xa9e3e905);\r
+    subround(G, d, a, b, c, 2, 9, 0xfcefa3f8);\r
+    subround(G, c, d, a, b, 7, 14, 0x676f02d9);\r
+    subround(G, b, c, d, a, 12, 20, 0x8d2a4c8a);\r
+    subround(H, a, b, c, d, 5, 4, 0xfffa3942);\r
+    subround(H, d, a, b, c, 8, 11, 0x8771f681);\r
+    subround(H, c, d, a, b, 11, 16, 0x6d9d6122);\r
+    subround(H, b, c, d, a, 14, 23, 0xfde5380c);\r
+    subround(H, a, b, c, d, 1, 4, 0xa4beea44);\r
+    subround(H, d, a, b, c, 4, 11, 0x4bdecfa9);\r
+    subround(H, c, d, a, b, 7, 16, 0xf6bb4b60);\r
+    subround(H, b, c, d, a, 10, 23, 0xbebfbc70);\r
+    subround(H, a, b, c, d, 13, 4, 0x289b7ec6);\r
+    subround(H, d, a, b, c, 0, 11, 0xeaa127fa);\r
+    subround(H, c, d, a, b, 3, 16, 0xd4ef3085);\r
+    subround(H, b, c, d, a, 6, 23, 0x04881d05);\r
+    subround(H, a, b, c, d, 9, 4, 0xd9d4d039);\r
+    subround(H, d, a, b, c, 12, 11, 0xe6db99e5);\r
+    subround(H, c, d, a, b, 15, 16, 0x1fa27cf8);\r
+    subround(H, b, c, d, a, 2, 23, 0xc4ac5665);\r
+    subround(I, a, b, c, d, 0, 6, 0xf4292244);\r
+    subround(I, d, a, b, c, 7, 10, 0x432aff97);\r
+    subround(I, c, d, a, b, 14, 15, 0xab9423a7);\r
+    subround(I, b, c, d, a, 5, 21, 0xfc93a039);\r
+    subround(I, a, b, c, d, 12, 6, 0x655b59c3);\r
+    subround(I, d, a, b, c, 3, 10, 0x8f0ccc92);\r
+    subround(I, c, d, a, b, 10, 15, 0xffeff47d);\r
+    subround(I, b, c, d, a, 1, 21, 0x85845dd1);\r
+    subround(I, a, b, c, d, 8, 6, 0x6fa87e4f);\r
+    subround(I, d, a, b, c, 15, 10, 0xfe2ce6e0);\r
+    subround(I, c, d, a, b, 6, 15, 0xa3014314);\r
+    subround(I, b, c, d, a, 13, 21, 0x4e0811a1);\r
+    subround(I, a, b, c, d, 4, 6, 0xf7537e82);\r
+    subround(I, d, a, b, c, 11, 10, 0xbd3af235);\r
+    subround(I, c, d, a, b, 2, 15, 0x2ad7d2bb);\r
+    subround(I, b, c, d, a, 9, 21, 0xeb86d391);\r
+\r
+    s->h[0] += a;\r
+    s->h[1] += b;\r
+    s->h[2] += c;\r
+    s->h[3] += d;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Outer MD5 algorithm: take an arbitrary length byte string,\r
+ * convert it into 16-word blocks with the prescribed padding at\r
+ * the end, and pass those blocks to the core MD5 algorithm.\r
+ */\r
+\r
+#define BLKSIZE 64\r
+\r
+void MD5Init(struct MD5Context *s)\r
+{\r
+    MD5_Core_Init(&s->core);\r
+    s->blkused = 0;\r
+    s->lenhi = s->lenlo = 0;\r
+}\r
+\r
+void MD5Update(struct MD5Context *s, unsigned char const *p, unsigned len)\r
+{\r
+    unsigned char *q = (unsigned char *) p;\r
+    uint32 wordblock[16];\r
+    uint32 lenw = len;\r
+    int i;\r
+\r
+    /*\r
+     * Update the length field.\r
+     */\r
+    s->lenlo += lenw;\r
+    s->lenhi += (s->lenlo < lenw);\r
+\r
+    if (s->blkused + len < BLKSIZE) {\r
+       /*\r
+        * Trivial case: just add to the block.\r
+        */\r
+       memcpy(s->block + s->blkused, q, len);\r
+       s->blkused += len;\r
+    } else {\r
+       /*\r
+        * We must complete and process at least one block.\r
+        */\r
+       while (s->blkused + len >= BLKSIZE) {\r
+           memcpy(s->block + s->blkused, q, BLKSIZE - s->blkused);\r
+           q += BLKSIZE - s->blkused;\r
+           len -= BLKSIZE - s->blkused;\r
+           /* Now process the block. Gather bytes little-endian into words */\r
+           for (i = 0; i < 16; i++) {\r
+               wordblock[i] =\r
+                   (((uint32) s->block[i * 4 + 3]) << 24) |\r
+                   (((uint32) s->block[i * 4 + 2]) << 16) |\r
+                   (((uint32) s->block[i * 4 + 1]) << 8) |\r
+                   (((uint32) s->block[i * 4 + 0]) << 0);\r
+           }\r
+           MD5_Block(&s->core, wordblock);\r
+           s->blkused = 0;\r
+       }\r
+       memcpy(s->block, q, len);\r
+       s->blkused = len;\r
+    }\r
+}\r
+\r
+void MD5Final(unsigned char output[16], struct MD5Context *s)\r
+{\r
+    int i;\r
+    unsigned pad;\r
+    unsigned char c[64];\r
+    uint32 lenhi, lenlo;\r
+\r
+    if (s->blkused >= 56)\r
+       pad = 56 + 64 - s->blkused;\r
+    else\r
+       pad = 56 - s->blkused;\r
+\r
+    lenhi = (s->lenhi << 3) | (s->lenlo >> (32 - 3));\r
+    lenlo = (s->lenlo << 3);\r
+\r
+    memset(c, 0, pad);\r
+    c[0] = 0x80;\r
+    MD5Update(s, c, pad);\r
+\r
+    c[7] = (lenhi >> 24) & 0xFF;\r
+    c[6] = (lenhi >> 16) & 0xFF;\r
+    c[5] = (lenhi >> 8) & 0xFF;\r
+    c[4] = (lenhi >> 0) & 0xFF;\r
+    c[3] = (lenlo >> 24) & 0xFF;\r
+    c[2] = (lenlo >> 16) & 0xFF;\r
+    c[1] = (lenlo >> 8) & 0xFF;\r
+    c[0] = (lenlo >> 0) & 0xFF;\r
+\r
+    MD5Update(s, c, 8);\r
+\r
+    for (i = 0; i < 4; i++) {\r
+       output[4 * i + 3] = (s->core.h[i] >> 24) & 0xFF;\r
+       output[4 * i + 2] = (s->core.h[i] >> 16) & 0xFF;\r
+       output[4 * i + 1] = (s->core.h[i] >> 8) & 0xFF;\r
+       output[4 * i + 0] = (s->core.h[i] >> 0) & 0xFF;\r
+    }\r
+}\r
+\r
+void MD5Simple(void const *p, unsigned len, unsigned char output[16])\r
+{\r
+    struct MD5Context s;\r
+\r
+    MD5Init(&s);\r
+    MD5Update(&s, (unsigned char const *)p, len);\r
+    MD5Final(output, &s);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * The above is the MD5 algorithm itself. Now we implement the\r
+ * HMAC wrapper on it.\r
+ * \r
+ * Some of these functions are exported directly, because they are\r
+ * useful elsewhere (SOCKS5 CHAP authentication uses HMAC-MD5).\r
+ */\r
+\r
+void *hmacmd5_make_context(void)\r
+{\r
+    return snewn(3, struct MD5Context);\r
+}\r
+\r
+void hmacmd5_free_context(void *handle)\r
+{\r
+    sfree(handle);\r
+}\r
+\r
+void hmacmd5_key(void *handle, void const *keyv, int len)\r
+{\r
+    struct MD5Context *keys = (struct MD5Context *)handle;\r
+    unsigned char foo[64];\r
+    unsigned char const *key = (unsigned char const *)keyv;\r
+    int i;\r
+\r
+    memset(foo, 0x36, 64);\r
+    for (i = 0; i < len && i < 64; i++)\r
+       foo[i] ^= key[i];\r
+    MD5Init(&keys[0]);\r
+    MD5Update(&keys[0], foo, 64);\r
+\r
+    memset(foo, 0x5C, 64);\r
+    for (i = 0; i < len && i < 64; i++)\r
+       foo[i] ^= key[i];\r
+    MD5Init(&keys[1]);\r
+    MD5Update(&keys[1], foo, 64);\r
+\r
+    memset(foo, 0, 64);                       /* burn the evidence */\r
+}\r
+\r
+static void hmacmd5_key_16(void *handle, unsigned char *key)\r
+{\r
+    hmacmd5_key(handle, key, 16);\r
+}\r
+\r
+static void hmacmd5_start(void *handle)\r
+{\r
+    struct MD5Context *keys = (struct MD5Context *)handle;\r
+\r
+    keys[2] = keys[0];               /* structure copy */\r
+}\r
+\r
+static void hmacmd5_bytes(void *handle, unsigned char const *blk, int len)\r
+{\r
+    struct MD5Context *keys = (struct MD5Context *)handle;\r
+    MD5Update(&keys[2], blk, len);\r
+}\r
+\r
+static void hmacmd5_genresult(void *handle, unsigned char *hmac)\r
+{\r
+    struct MD5Context *keys = (struct MD5Context *)handle;\r
+    struct MD5Context s;\r
+    unsigned char intermediate[16];\r
+\r
+    s = keys[2];                      /* structure copy */\r
+    MD5Final(intermediate, &s);\r
+    s = keys[1];                      /* structure copy */\r
+    MD5Update(&s, intermediate, 16);\r
+    MD5Final(hmac, &s);\r
+}\r
+\r
+static int hmacmd5_verresult(void *handle, unsigned char const *hmac)\r
+{\r
+    unsigned char correct[16];\r
+    hmacmd5_genresult(handle, correct);\r
+    return !memcmp(correct, hmac, 16);\r
+}\r
+\r
+static void hmacmd5_do_hmac_internal(void *handle,\r
+                                    unsigned char const *blk, int len,\r
+                                    unsigned char const *blk2, int len2,\r
+                                    unsigned char *hmac)\r
+{\r
+    hmacmd5_start(handle);\r
+    hmacmd5_bytes(handle, blk, len);\r
+    if (blk2) hmacmd5_bytes(handle, blk2, len2);\r
+    hmacmd5_genresult(handle, hmac);\r
+}\r
+\r
+void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,\r
+                    unsigned char *hmac)\r
+{\r
+    hmacmd5_do_hmac_internal(handle, blk, len, NULL, 0, hmac);\r
+}\r
+\r
+static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len,\r
+                               unsigned long seq, unsigned char *hmac)\r
+{\r
+    unsigned char seqbuf[16];\r
+\r
+    seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF);\r
+    seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF);\r
+    seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF);\r
+    seqbuf[3] = (unsigned char) ((seq) & 0xFF);\r
+\r
+    hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac);\r
+}\r
+\r
+static void hmacmd5_generate(void *handle, unsigned char *blk, int len,\r
+                            unsigned long seq)\r
+{\r
+    hmacmd5_do_hmac_ssh(handle, blk, len, seq, blk + len);\r
+}\r
+\r
+static int hmacmd5_verify(void *handle, unsigned char *blk, int len,\r
+                         unsigned long seq)\r
+{\r
+    unsigned char correct[16];\r
+    hmacmd5_do_hmac_ssh(handle, blk, len, seq, correct);\r
+    return !memcmp(correct, blk + len, 16);\r
+}\r
+\r
+const struct ssh_mac ssh_hmac_md5 = {\r
+    hmacmd5_make_context, hmacmd5_free_context, hmacmd5_key_16,\r
+    hmacmd5_generate, hmacmd5_verify,\r
+    hmacmd5_start, hmacmd5_bytes, hmacmd5_genresult, hmacmd5_verresult,\r
+    "hmac-md5",\r
+    16,\r
+    "HMAC-MD5"\r
+};\r
diff --git a/putty/SSHNOGSS.C b/putty/SSHNOGSS.C
new file mode 100644 (file)
index 0000000..27ec760
--- /dev/null
@@ -0,0 +1,19 @@
+#include "putty.h"\r
+#ifndef NO_GSSAPI\r
+\r
+/* For platforms not supporting GSSAPI */\r
+\r
+struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)\r
+{\r
+    struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist *);\r
+    list->libraries = NULL;\r
+    list->nlibraries = 0;\r
+    return list;\r
+}\r
+\r
+void ssh_gss_cleanup(struct ssh_gss_liblist *list)\r
+{\r
+    sfree(list);\r
+}\r
+\r
+#endif /* NO_GSSAPI */\r
diff --git a/putty/SSHPRIME.C b/putty/SSHPRIME.C
new file mode 100644 (file)
index 0000000..e283b52
--- /dev/null
@@ -0,0 +1,1398 @@
+/*\r
+ * Prime generation.\r
+ */\r
+\r
+#include <assert.h>\r
+#include "ssh.h"\r
+\r
+/*\r
+ * This prime generation algorithm is pretty much cribbed from\r
+ * OpenSSL. The algorithm is:\r
+ * \r
+ *  - invent a B-bit random number and ensure the top and bottom\r
+ *    bits are set (so it's definitely B-bit, and it's definitely\r
+ *    odd)\r
+ * \r
+ *  - see if it's coprime to all primes below 2^16; increment it by\r
+ *    two until it is (this shouldn't take long in general)\r
+ * \r
+ *  - perform the Miller-Rabin primality test enough times to\r
+ *    ensure the probability of it being composite is 2^-80 or\r
+ *    less\r
+ * \r
+ *  - go back to square one if any M-R test fails.\r
+ */\r
+\r
+/*\r
+ * The Miller-Rabin primality test is an extension to the Fermat\r
+ * test. The Fermat test just checks that a^(p-1) == 1 mod p; this\r
+ * is vulnerable to Carmichael numbers. Miller-Rabin considers how\r
+ * that 1 is derived as well.\r
+ * \r
+ * Lemma: if a^2 == 1 (mod p), and p is prime, then either a == 1\r
+ * or a == -1 (mod p).\r
+ * \r
+ *   Proof: p divides a^2-1, i.e. p divides (a+1)(a-1). Hence,\r
+ *   since p is prime, either p divides (a+1) or p divides (a-1).\r
+ *   But this is the same as saying that either a is congruent to\r
+ *   -1 mod p or a is congruent to +1 mod p. []\r
+ * \r
+ *   Comment: This fails when p is not prime. Consider p=mn, so\r
+ *   that mn divides (a+1)(a-1). Now we could have m dividing (a+1)\r
+ *   and n dividing (a-1), without the whole of mn dividing either.\r
+ *   For example, consider a=10 and p=99. 99 = 9 * 11; 9 divides\r
+ *   10-1 and 11 divides 10+1, so a^2 is congruent to 1 mod p\r
+ *   without a having to be congruent to either 1 or -1.\r
+ * \r
+ * So the Miller-Rabin test, as well as considering a^(p-1),\r
+ * considers a^((p-1)/2), a^((p-1)/4), and so on as far as it can\r
+ * go. In other words. we write p-1 as q * 2^k, with k as large as\r
+ * possible (i.e. q must be odd), and we consider the powers\r
+ * \r
+ *       a^(q*2^0)      a^(q*2^1)          ...  a^(q*2^(k-1))  a^(q*2^k)\r
+ * i.e.  a^((n-1)/2^k)  a^((n-1)/2^(k-1))  ...  a^((n-1)/2)    a^(n-1)\r
+ * \r
+ * If p is to be prime, the last of these must be 1. Therefore, by\r
+ * the above lemma, the one before it must be either 1 or -1. And\r
+ * _if_ it's 1, then the one before that must be either 1 or -1,\r
+ * and so on ... In other words, we expect to see a trailing chain\r
+ * of 1s preceded by a -1. (If we're unlucky, our trailing chain of\r
+ * 1s will be as long as the list so we'll never get to see what\r
+ * lies before it. This doesn't count as a test failure because it\r
+ * hasn't _proved_ that p is not prime.)\r
+ * \r
+ * For example, consider a=2 and p=1729. 1729 is a Carmichael\r
+ * number: although it's not prime, it satisfies a^(p-1) == 1 mod p\r
+ * for any a coprime to it. So the Fermat test wouldn't have a\r
+ * problem with it at all, unless we happened to stumble on an a\r
+ * which had a common factor.\r
+ * \r
+ * So. 1729 - 1 equals 27 * 2^6. So we look at\r
+ * \r
+ *     2^27 mod 1729 == 645\r
+ *    2^108 mod 1729 == 1065\r
+ *    2^216 mod 1729 == 1\r
+ *    2^432 mod 1729 == 1\r
+ *    2^864 mod 1729 == 1\r
+ *   2^1728 mod 1729 == 1\r
+ * \r
+ * We do have a trailing string of 1s, so the Fermat test would\r
+ * have been happy. But this trailing string of 1s is preceded by\r
+ * 1065; whereas if 1729 were prime, we'd expect to see it preceded\r
+ * by -1 (i.e. 1728.). Guards! Seize this impostor.\r
+ * \r
+ * (If we were unlucky, we might have tried a=16 instead of a=2;\r
+ * now 16^27 mod 1729 == 1, so we would have seen a long string of\r
+ * 1s and wouldn't have seen the thing _before_ the 1s. So, just\r
+ * like the Fermat test, for a given p there may well exist values\r
+ * of a which fail to show up its compositeness. So we try several,\r
+ * just like the Fermat test. The difference is that Miller-Rabin\r
+ * is not _in general_ fooled by Carmichael numbers.)\r
+ * \r
+ * Put simply, then, the Miller-Rabin test requires us to:\r
+ * \r
+ *  1. write p-1 as q * 2^k, with q odd\r
+ *  2. compute z = (a^q) mod p.\r
+ *  3. report success if z == 1 or z == -1.\r
+ *  4. square z at most k-1 times, and report success if it becomes\r
+ *     -1 at any point.\r
+ *  5. report failure otherwise.\r
+ * \r
+ * (We expect z to become -1 after at most k-1 squarings, because\r
+ * if it became -1 after k squarings then a^(p-1) would fail to be\r
+ * 1. And we don't need to investigate what happens after we see a\r
+ * -1, because we _know_ that -1 squared is 1 modulo anything at\r
+ * all, so after we've seen a -1 we can be sure of seeing nothing\r
+ * but 1s.)\r
+ */\r
+\r
+/*\r
+ * The first few odd primes.\r
+ *\r
+ * import sys\r
+ * def sieve(n):\r
+ *     z = []\r
+ *     list = []\r
+ *     for i in range(n): z.append(1)\r
+ *     for i in range(2,n):\r
+ *         if z[i]:\r
+ *             list.append(i)\r
+ *             for j in range(i,n,i): z[j] = 0\r
+ *     return list\r
+ * list = sieve(65535)\r
+ * for i in list[1:]: sys.stdout.write("%d," % i)\r
+ */\r
+static const unsigned short primes[] = {\r
+    3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,\r
+    71, 73, 79, 83, 89, 97, 101,\r
+    103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,\r
+    179, 181, 191, 193,\r
+    197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271,\r
+    277, 281, 283, 293,\r
+    307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,\r
+    389, 397, 401, 409,\r
+    419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,\r
+    499, 503, 509, 521,\r
+    523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613,\r
+    617, 619, 631, 641,\r
+    643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733,\r
+    739, 743, 751, 757,\r
+    761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857,\r
+    859, 863, 877, 881,\r
+    883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,\r
+    991, 997, 1009,\r
+    1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087,\r
+    1091, 1093,\r
+    1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187,\r
+    1193, 1201,\r
+    1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289,\r
+    1291, 1297,\r
+    1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409,\r
+    1423, 1427,\r
+    1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489,\r
+    1493, 1499,\r
+    1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597,\r
+    1601, 1607,\r
+    1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697,\r
+    1699, 1709,\r
+    1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801,\r
+    1811, 1823,\r
+    1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913,\r
+    1931, 1933,\r
+    1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027,\r
+    2029, 2039,\r
+    2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131,\r
+    2137, 2141,\r
+    2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251,\r
+    2267, 2269,\r
+    2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351,\r
+    2357, 2371,\r
+    2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447,\r
+    2459, 2467,\r
+    2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591,\r
+    2593, 2609,\r
+    2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689,\r
+    2693, 2699,\r
+    2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789,\r
+    2791, 2797,\r
+    2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897,\r
+    2903, 2909,\r
+    2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019,\r
+    3023, 3037,\r
+    3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163,\r
+    3167, 3169,\r
+    3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259,\r
+    3271, 3299,\r
+    3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371,\r
+    3373, 3389,\r
+    3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499,\r
+    3511, 3517,\r
+    3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593,\r
+    3607, 3613,\r
+    3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701,\r
+    3709, 3719,\r
+    3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823,\r
+    3833, 3847,\r
+    3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929,\r
+    3931, 3943,\r
+    3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051,\r
+    4057, 4073,\r
+    4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159,\r
+    4177, 4201,\r
+    4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273,\r
+    4283, 4289,\r
+    4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421,\r
+    4423, 4441,\r
+    4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523,\r
+    4547, 4549,\r
+    4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651,\r
+    4657, 4663,\r
+    4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787,\r
+    4789, 4793,\r
+    4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919,\r
+    4931, 4933,\r
+    4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009,\r
+    5011, 5021,\r
+    5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119,\r
+    5147, 5153,\r
+    5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273,\r
+    5279, 5281,\r
+    5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407,\r
+    5413, 5417,\r
+    5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503,\r
+    5507, 5519,\r
+    5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641,\r
+    5647, 5651,\r
+    5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741,\r
+    5743, 5749,\r
+    5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851,\r
+    5857, 5861,\r
+    5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987,\r
+    6007, 6011,\r
+    6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113,\r
+    6121, 6131,\r
+    6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229,\r
+    6247, 6257,\r
+    6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337,\r
+    6343, 6353,\r
+    6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469,\r
+    6473, 6481,\r
+    6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599,\r
+    6607, 6619,\r
+    6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719,\r
+    6733, 6737,\r
+    6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841,\r
+    6857, 6863,\r
+    6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967,\r
+    6971, 6977,\r
+    6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079,\r
+    7103, 7109,\r
+    7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219,\r
+    7229, 7237,\r
+    7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351,\r
+    7369, 7393,\r
+    7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507,\r
+    7517, 7523,\r
+    7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591,\r
+    7603, 7607,\r
+    7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717,\r
+    7723, 7727,\r
+    7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867,\r
+    7873, 7877,\r
+    7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993,\r
+    8009, 8011,\r
+    8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117,\r
+    8123, 8147,\r
+    8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243,\r
+    8263, 8269,\r
+    8273, 8287, 8291, 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377,\r
+    8387, 8389,\r
+    8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527,\r
+    8537, 8539,\r
+    8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647,\r
+    8663, 8669,\r
+    8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747,\r
+    8753, 8761,\r
+    8779, 8783, 8803, 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 8863,\r
+    8867, 8887,\r
+    8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007,\r
+    9011, 9013,\r
+    9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137,\r
+    9151, 9157,\r
+    9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239, 9241, 9257,\r
+    9277, 9281,\r
+    9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391,\r
+    9397, 9403,\r
+    9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479,\r
+    9491, 9497,\r
+    9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629,\r
+    9631, 9643,\r
+    9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 9739, 9743, 9749,\r
+    9767, 9769,\r
+    9781, 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859,\r
+    9871, 9883,\r
+    9887, 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007,\r
+    10009, 10037,\r
+    10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111,\r
+    10133, 10139,\r
+    10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223,\r
+    10243, 10247,\r
+    10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321,\r
+    10331, 10333,\r
+    10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453,\r
+    10457, 10459,\r
+    10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567,\r
+    10589, 10597,\r
+    10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667,\r
+    10687, 10691,\r
+    10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789,\r
+    10799, 10831,\r
+    10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889, 10891, 10903,\r
+    10909, 10937,\r
+    10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047,\r
+    11057, 11059,\r
+    11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149,\r
+    11159, 11161,\r
+    11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261,\r
+    11273, 11279,\r
+    11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383,\r
+    11393, 11399,\r
+    11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491,\r
+    11497, 11503,\r
+    11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621,\r
+    11633, 11657,\r
+    11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777,\r
+    11779, 11783,\r
+    11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863,\r
+    11867, 11887,\r
+    11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959,\r
+    11969, 11971,\r
+    11981, 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071, 12073,\r
+    12097, 12101,\r
+    12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197,\r
+    12203, 12211,\r
+    12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289,\r
+    12301, 12323,\r
+    12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413,\r
+    12421, 12433,\r
+    12437, 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511,\r
+    12517, 12527,\r
+    12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611,\r
+    12613, 12619,\r
+    12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713,\r
+    12721, 12739,\r
+    12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829,\r
+    12841, 12853,\r
+    12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941, 12953,\r
+    12959, 12967,\r
+    12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043,\r
+    13049, 13063,\r
+    13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163,\r
+    13171, 13177,\r
+    13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291,\r
+    13297, 13309,\r
+    13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411,\r
+    13417, 13421,\r
+    13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 13513, 13523,\r
+    13537, 13553,\r
+    13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669,\r
+    13679, 13681,\r
+    13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751,\r
+    13757, 13759,\r
+    13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873,\r
+    13877, 13879,\r
+    13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967,\r
+    13997, 13999,\r
+    14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087,\r
+    14107, 14143,\r
+    14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249,\r
+    14251, 14281,\r
+    14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369, 14387, 14389,\r
+    14401, 14407,\r
+    14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489,\r
+    14503, 14519,\r
+    14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593,\r
+    14621, 14627,\r
+    14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 14713, 14717,\r
+    14723, 14731,\r
+    14737, 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797,\r
+    14813, 14821,\r
+    14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 14891, 14897,\r
+    14923, 14929,\r
+    14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053,\r
+    15061, 15073,\r
+    15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149,\r
+    15161, 15173,\r
+    15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269,\r
+    15271, 15277,\r
+    15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359,\r
+    15361, 15373,\r
+    15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461,\r
+    15467, 15473,\r
+    15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583,\r
+    15601, 15607,\r
+    15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679,\r
+    15683, 15727,\r
+    15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787, 15791,\r
+    15797, 15803,\r
+    15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907,\r
+    15913, 15919,\r
+    15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007, 16033, 16057,\r
+    16061, 16063,\r
+    16067, 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139,\r
+    16141, 16183,\r
+    16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267,\r
+    16273, 16301,\r
+    16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417,\r
+    16421, 16427,\r
+    16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529,\r
+    16547, 16553,\r
+    16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651,\r
+    16657, 16661,\r
+    16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763,\r
+    16787, 16811,\r
+    16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903,\r
+    16921, 16927,\r
+    16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 17011, 17021,\r
+    17027, 17029,\r
+    17033, 17041, 17047, 17053, 17077, 17093, 17099, 17107, 17117, 17123,\r
+    17137, 17159,\r
+    17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239, 17257,\r
+    17291, 17293,\r
+    17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383,\r
+    17387, 17389,\r
+    17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477,\r
+    17483, 17489,\r
+    17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581,\r
+    17597, 17599,\r
+    17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713,\r
+    17729, 17737,\r
+    17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839,\r
+    17851, 17863,\r
+    17881, 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957,\r
+    17959, 17971,\r
+    17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059,\r
+    18061, 18077,\r
+    18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, 18149, 18169,\r
+    18181, 18191,\r
+    18199, 18211, 18217, 18223, 18229, 18233, 18251, 18253, 18257, 18269,\r
+    18287, 18289,\r
+    18301, 18307, 18311, 18313, 18329, 18341, 18353, 18367, 18371, 18379,\r
+    18397, 18401,\r
+    18413, 18427, 18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493,\r
+    18503, 18517,\r
+    18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637,\r
+    18661, 18671,\r
+    18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, 18757, 18773,\r
+    18787, 18793,\r
+    18797, 18803, 18839, 18859, 18869, 18899, 18911, 18913, 18917, 18919,\r
+    18947, 18959,\r
+    18973, 18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073,\r
+    19079, 19081,\r
+    19087, 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211,\r
+    19213, 19219,\r
+    19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319,\r
+    19333, 19373,\r
+    19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427, 19429,\r
+    19433, 19441,\r
+    19447, 19457, 19463, 19469, 19471, 19477, 19483, 19489, 19501, 19507,\r
+    19531, 19541,\r
+    19543, 19553, 19559, 19571, 19577, 19583, 19597, 19603, 19609, 19661,\r
+    19681, 19687,\r
+    19697, 19699, 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763,\r
+    19777, 19793,\r
+    19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891,\r
+    19913, 19919,\r
+    19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, 19993, 19997,\r
+    20011, 20021,\r
+    20023, 20029, 20047, 20051, 20063, 20071, 20089, 20101, 20107, 20113,\r
+    20117, 20123,\r
+    20129, 20143, 20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219,\r
+    20231, 20233,\r
+    20249, 20261, 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347,\r
+    20353, 20357,\r
+    20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443,\r
+    20477, 20479,\r
+    20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563, 20593,\r
+    20599, 20611,\r
+    20627, 20639, 20641, 20663, 20681, 20693, 20707, 20717, 20719, 20731,\r
+    20743, 20747,\r
+    20749, 20753, 20759, 20771, 20773, 20789, 20807, 20809, 20849, 20857,\r
+    20873, 20879,\r
+    20887, 20897, 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963,\r
+    20981, 20983,\r
+    21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067,\r
+    21089, 21101,\r
+    21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, 21179, 21187,\r
+    21191, 21193,\r
+    21211, 21221, 21227, 21247, 21269, 21277, 21283, 21313, 21317, 21319,\r
+    21323, 21341,\r
+    21347, 21377, 21379, 21383, 21391, 21397, 21401, 21407, 21419, 21433,\r
+    21467, 21481,\r
+    21487, 21491, 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557,\r
+    21559, 21563,\r
+    21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647,\r
+    21649, 21661,\r
+    21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, 21757, 21767,\r
+    21773, 21787,\r
+    21799, 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871,\r
+    21881, 21893,\r
+    21911, 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003, 22013,\r
+    22027, 22031,\r
+    22037, 22039, 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109,\r
+    22111, 22123,\r
+    22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229,\r
+    22247, 22259,\r
+    22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307, 22343, 22349,\r
+    22367, 22369,\r
+    22381, 22391, 22397, 22409, 22433, 22441, 22447, 22453, 22469, 22481,\r
+    22483, 22501,\r
+    22511, 22531, 22541, 22543, 22549, 22567, 22571, 22573, 22613, 22619,\r
+    22621, 22637,\r
+    22639, 22643, 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717,\r
+    22721, 22727,\r
+    22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817,\r
+    22853, 22859,\r
+    22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963,\r
+    22973, 22993,\r
+    23003, 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057,\r
+    23059, 23063,\r
+    23071, 23081, 23087, 23099, 23117, 23131, 23143, 23159, 23167, 23173,\r
+    23189, 23197,\r
+    23201, 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297,\r
+    23311, 23321,\r
+    23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447,\r
+    23459, 23473,\r
+    23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, 23563, 23567,\r
+    23581, 23593,\r
+    23599, 23603, 23609, 23623, 23627, 23629, 23633, 23663, 23669, 23671,\r
+    23677, 23687,\r
+    23689, 23719, 23741, 23743, 23747, 23753, 23761, 23767, 23773, 23789,\r
+    23801, 23813,\r
+    23819, 23827, 23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893,\r
+    23899, 23909,\r
+    23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007,\r
+    24019, 24023,\r
+    24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103,\r
+    24107, 24109,\r
+    24113, 24121, 24133, 24137, 24151, 24169, 24179, 24181, 24197, 24203,\r
+    24223, 24229,\r
+    24239, 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373,\r
+    24379, 24391,\r
+    24407, 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499,\r
+    24509, 24517,\r
+    24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659,\r
+    24671, 24677,\r
+    24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767, 24781, 24793,\r
+    24799, 24809,\r
+    24821, 24841, 24847, 24851, 24859, 24877, 24889, 24907, 24917, 24919,\r
+    24923, 24943,\r
+    24953, 24967, 24971, 24977, 24979, 24989, 25013, 25031, 25033, 25037,\r
+    25057, 25073,\r
+    25087, 25097, 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169,\r
+    25171, 25183,\r
+    25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303,\r
+    25307, 25309,\r
+    25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, 25409, 25411,\r
+    25423, 25439,\r
+    25447, 25453, 25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561,\r
+    25577, 25579,\r
+    25583, 25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657,\r
+    25667, 25673,\r
+    25679, 25693, 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771,\r
+    25793, 25799,\r
+    25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913,\r
+    25919, 25931,\r
+    25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003, 26017,\r
+    26021, 26029,\r
+    26041, 26053, 26083, 26099, 26107, 26111, 26113, 26119, 26141, 26153,\r
+    26161, 26171,\r
+    26177, 26183, 26189, 26203, 26209, 26227, 26237, 26249, 26251, 26261,\r
+    26263, 26267,\r
+    26293, 26297, 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387,\r
+    26393, 26399,\r
+    26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497,\r
+    26501, 26513,\r
+    26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633, 26641, 26647,\r
+    26669, 26681,\r
+    26683, 26687, 26693, 26699, 26701, 26711, 26713, 26717, 26723, 26729,\r
+    26731, 26737,\r
+    26759, 26777, 26783, 26801, 26813, 26821, 26833, 26839, 26849, 26861,\r
+    26863, 26879,\r
+    26881, 26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959,\r
+    26981, 26987,\r
+    26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077,\r
+    27091, 27103,\r
+    27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, 27239, 27241,\r
+    27253, 27259,\r
+    27271, 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367, 27397,\r
+    27407, 27409,\r
+    27427, 27431, 27437, 27449, 27457, 27479, 27481, 27487, 27509, 27527,\r
+    27529, 27539,\r
+    27541, 27551, 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673,\r
+    27689, 27691,\r
+    27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767,\r
+    27773, 27779,\r
+    27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, 27847, 27851,\r
+    27883, 27893,\r
+    27901, 27917, 27919, 27941, 27943, 27947, 27953, 27961, 27967, 27983,\r
+    27997, 28001,\r
+    28019, 28027, 28031, 28051, 28057, 28069, 28081, 28087, 28097, 28099,\r
+    28109, 28111,\r
+    28123, 28151, 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277,\r
+    28279, 28283,\r
+    28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403,\r
+    28409, 28411,\r
+    28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517,\r
+    28537, 28541,\r
+    28547, 28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607,\r
+    28619, 28621,\r
+    28627, 28631, 28643, 28649, 28657, 28661, 28663, 28669, 28687, 28697,\r
+    28703, 28711,\r
+    28723, 28729, 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813,\r
+    28817, 28837,\r
+    28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933,\r
+    28949, 28961,\r
+    28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059, 29063, 29077,\r
+    29101, 29123,\r
+    29129, 29131, 29137, 29147, 29153, 29167, 29173, 29179, 29191, 29201,\r
+    29207, 29209,\r
+    29221, 29231, 29243, 29251, 29269, 29287, 29297, 29303, 29311, 29327,\r
+    29333, 29339,\r
+    29347, 29363, 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429,\r
+    29437, 29443,\r
+    29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573,\r
+    29581, 29587,\r
+    29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671, 29683, 29717,\r
+    29723, 29741,\r
+    29753, 29759, 29761, 29789, 29803, 29819, 29833, 29837, 29851, 29863,\r
+    29867, 29873,\r
+    29879, 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011,\r
+    30013, 30029,\r
+    30047, 30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119,\r
+    30133, 30137,\r
+    30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241,\r
+    30253, 30259,\r
+    30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, 30347, 30367,\r
+    30389, 30391,\r
+    30403, 30427, 30431, 30449, 30467, 30469, 30491, 30493, 30497, 30509,\r
+    30517, 30529,\r
+    30539, 30553, 30557, 30559, 30577, 30593, 30631, 30637, 30643, 30649,\r
+    30661, 30671,\r
+    30677, 30689, 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773,\r
+    30781, 30803,\r
+    30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871,\r
+    30881, 30893,\r
+    30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, 31013, 31019,\r
+    31033, 31039,\r
+    31051, 31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147,\r
+    31151, 31153,\r
+    31159, 31177, 31181, 31183, 31189, 31193, 31219, 31223, 31231, 31237,\r
+    31247, 31249,\r
+    31253, 31259, 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333,\r
+    31337, 31357,\r
+    31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511,\r
+    31513, 31517,\r
+    31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607, 31627,\r
+    31643, 31649,\r
+    31657, 31663, 31667, 31687, 31699, 31721, 31723, 31727, 31729, 31741,\r
+    31751, 31769,\r
+    31771, 31793, 31799, 31817, 31847, 31849, 31859, 31873, 31883, 31891,\r
+    31907, 31957,\r
+    31963, 31973, 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057,\r
+    32059, 32063,\r
+    32069, 32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159,\r
+    32173, 32183,\r
+    32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, 32261, 32297,\r
+    32299, 32303,\r
+    32309, 32321, 32323, 32327, 32341, 32353, 32359, 32363, 32369, 32371,\r
+    32377, 32381,\r
+    32401, 32411, 32413, 32423, 32429, 32441, 32443, 32467, 32479, 32491,\r
+    32497, 32503,\r
+    32507, 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587,\r
+    32603, 32609,\r
+    32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717,\r
+    32719, 32749,\r
+    32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833, 32839,\r
+    32843, 32869,\r
+    32887, 32909, 32911, 32917, 32933, 32939, 32941, 32957, 32969, 32971,\r
+    32983, 32987,\r
+    32993, 32999, 33013, 33023, 33029, 33037, 33049, 33053, 33071, 33073,\r
+    33083, 33091,\r
+    33107, 33113, 33119, 33149, 33151, 33161, 33179, 33181, 33191, 33199,\r
+    33203, 33211,\r
+    33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343,\r
+    33347, 33349,\r
+    33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457, 33461,\r
+    33469, 33479,\r
+    33487, 33493, 33503, 33521, 33529, 33533, 33547, 33563, 33569, 33577,\r
+    33581, 33587,\r
+    33589, 33599, 33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641,\r
+    33647, 33679,\r
+    33703, 33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773,\r
+    33791, 33797,\r
+    33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893,\r
+    33911, 33923,\r
+    33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039,\r
+    34057, 34061,\r
+    34123, 34127, 34129, 34141, 34147, 34157, 34159, 34171, 34183, 34211,\r
+    34213, 34217,\r
+    34231, 34253, 34259, 34261, 34267, 34273, 34283, 34297, 34301, 34303,\r
+    34313, 34319,\r
+    34327, 34337, 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429,\r
+    34439, 34457,\r
+    34469, 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537,\r
+    34543, 34549,\r
+    34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, 34651, 34667,\r
+    34673, 34679,\r
+    34687, 34693, 34703, 34721, 34729, 34739, 34747, 34757, 34759, 34763,\r
+    34781, 34807,\r
+    34819, 34841, 34843, 34847, 34849, 34871, 34877, 34883, 34897, 34913,\r
+    34919, 34939,\r
+    34949, 34961, 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069,\r
+    35081, 35083,\r
+    35089, 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159,\r
+    35171, 35201,\r
+    35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311, 35317,\r
+    35323, 35327,\r
+    35339, 35353, 35363, 35381, 35393, 35401, 35407, 35419, 35423, 35437,\r
+    35447, 35449,\r
+    35461, 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537, 35543,\r
+    35569, 35573,\r
+    35591, 35593, 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747,\r
+    35753, 35759,\r
+    35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863,\r
+    35869, 35879,\r
+    35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977, 35983,\r
+    35993, 35999,\r
+    36007, 36011, 36013, 36017, 36037, 36061, 36067, 36073, 36083, 36097,\r
+    36107, 36109,\r
+    36131, 36137, 36151, 36161, 36187, 36191, 36209, 36217, 36229, 36241,\r
+    36251, 36263,\r
+    36269, 36277, 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353,\r
+    36373, 36383,\r
+    36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497,\r
+    36523, 36527,\r
+    36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599, 36607,\r
+    36629, 36637,\r
+    36643, 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721,\r
+    36739, 36749,\r
+    36761, 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821, 36833,\r
+    36847, 36857,\r
+    36871, 36877, 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931,\r
+    36943, 36947,\r
+    36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057,\r
+    37061, 37087,\r
+    37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, 37199, 37201,\r
+    37217, 37223,\r
+    37243, 37253, 37273, 37277, 37307, 37309, 37313, 37321, 37337, 37339,\r
+    37357, 37361,\r
+    37363, 37369, 37379, 37397, 37409, 37423, 37441, 37447, 37463, 37483,\r
+    37489, 37493,\r
+    37501, 37507, 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567,\r
+    37571, 37573,\r
+    37579, 37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663,\r
+    37691, 37693,\r
+    37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847,\r
+    37853, 37861,\r
+    37871, 37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987,\r
+    37991, 37993,\r
+    37997, 38011, 38039, 38047, 38053, 38069, 38083, 38113, 38119, 38149,\r
+    38153, 38167,\r
+    38177, 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261,\r
+    38273, 38281,\r
+    38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371,\r
+    38377, 38393,\r
+    38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, 38557, 38561,\r
+    38567, 38569,\r
+    38593, 38603, 38609, 38611, 38629, 38639, 38651, 38653, 38669, 38671,\r
+    38677, 38693,\r
+    38699, 38707, 38711, 38713, 38723, 38729, 38737, 38747, 38749, 38767,\r
+    38783, 38791,\r
+    38803, 38821, 38833, 38839, 38851, 38861, 38867, 38873, 38891, 38903,\r
+    38917, 38921,\r
+    38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041,\r
+    39043, 39047,\r
+    39079, 39089, 39097, 39103, 39107, 39113, 39119, 39133, 39139, 39157,\r
+    39161, 39163,\r
+    39181, 39191, 39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241,\r
+    39251, 39293,\r
+    39301, 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373,\r
+    39383, 39397,\r
+    39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511,\r
+    39521, 39541,\r
+    39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667,\r
+    39671, 39679,\r
+    39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791,\r
+    39799, 39821,\r
+    39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877, 39883,\r
+    39887, 39901,\r
+    39929, 39937, 39953, 39971, 39979, 39983, 39989, 40009, 40013, 40031,\r
+    40037, 40039,\r
+    40063, 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153,\r
+    40163, 40169,\r
+    40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283,\r
+    40289, 40343,\r
+    40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, 40459, 40471,\r
+    40483, 40487,\r
+    40493, 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583,\r
+    40591, 40597,\r
+    40609, 40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751,\r
+    40759, 40763,\r
+    40771, 40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849,\r
+    40853, 40867,\r
+    40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973,\r
+    40993, 41011,\r
+    41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117,\r
+    41131, 41141,\r
+    41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203, 41213,\r
+    41221, 41227,\r
+    41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341,\r
+    41351, 41357,\r
+    41381, 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479,\r
+    41491, 41507,\r
+    41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603,\r
+    41609, 41611,\r
+    41617, 41621, 41627, 41641, 41647, 41651, 41659, 41669, 41681, 41687,\r
+    41719, 41729,\r
+    41737, 41759, 41761, 41771, 41777, 41801, 41809, 41813, 41843, 41849,\r
+    41851, 41863,\r
+    41879, 41887, 41893, 41897, 41903, 41911, 41927, 41941, 41947, 41953,\r
+    41957, 41959,\r
+    41969, 41981, 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061,\r
+    42071, 42073,\r
+    42083, 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187,\r
+    42193, 42197,\r
+    42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, 42293, 42299,\r
+    42307, 42323,\r
+    42331, 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407,\r
+    42409, 42433,\r
+    42437, 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487, 42491,\r
+    42499, 42509,\r
+    42533, 42557, 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649,\r
+    42667, 42677,\r
+    42683, 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743,\r
+    42751, 42767,\r
+    42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, 42853, 42859,\r
+    42863, 42899,\r
+    42901, 42923, 42929, 42937, 42943, 42953, 42961, 42967, 42979, 42989,\r
+    43003, 43013,\r
+    43019, 43037, 43049, 43051, 43063, 43067, 43093, 43103, 43117, 43133,\r
+    43151, 43159,\r
+    43177, 43189, 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291,\r
+    43313, 43319,\r
+    43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451,\r
+    43457, 43481,\r
+    43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597,\r
+    43607, 43609,\r
+    43613, 43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717,\r
+    43721, 43753,\r
+    43759, 43777, 43781, 43783, 43787, 43789, 43793, 43801, 43853, 43867,\r
+    43889, 43891,\r
+    43913, 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991,\r
+    43997, 44017,\r
+    44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101,\r
+    44111, 44119,\r
+    44123, 44129, 44131, 44159, 44171, 44179, 44189, 44201, 44203, 44207,\r
+    44221, 44249,\r
+    44257, 44263, 44267, 44269, 44273, 44279, 44281, 44293, 44351, 44357,\r
+    44371, 44381,\r
+    44383, 44389, 44417, 44449, 44453, 44483, 44491, 44497, 44501, 44507,\r
+    44519, 44531,\r
+    44533, 44537, 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623,\r
+    44633, 44641,\r
+    44647, 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741,\r
+    44753, 44771,\r
+    44773, 44777, 44789, 44797, 44809, 44819, 44839, 44843, 44851, 44867,\r
+    44879, 44887,\r
+    44893, 44909, 44917, 44927, 44939, 44953, 44959, 44963, 44971, 44983,\r
+    44987, 45007,\r
+    45013, 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137,\r
+    45139, 45161,\r
+    45179, 45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289,\r
+    45293, 45307,\r
+    45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389, 45403,\r
+    45413, 45427,\r
+    45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, 45541, 45553,\r
+    45557, 45569,\r
+    45587, 45589, 45599, 45613, 45631, 45641, 45659, 45667, 45673, 45677,\r
+    45691, 45697,\r
+    45707, 45737, 45751, 45757, 45763, 45767, 45779, 45817, 45821, 45823,\r
+    45827, 45833,\r
+    45841, 45853, 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959,\r
+    45971, 45979,\r
+    45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099,\r
+    46103, 46133,\r
+    46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, 46219, 46229,\r
+    46237, 46261,\r
+    46271, 46273, 46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351,\r
+    46381, 46399,\r
+    46411, 46439, 46441, 46447, 46451, 46457, 46471, 46477, 46489, 46499,\r
+    46507, 46511,\r
+    46523, 46549, 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633,\r
+    46639, 46643,\r
+    46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747,\r
+    46751, 46757,\r
+    46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853, 46861,\r
+    46867, 46877,\r
+    46889, 46901, 46919, 46933, 46957, 46993, 46997, 47017, 47041, 47051,\r
+    47057, 47059,\r
+    47087, 47093, 47111, 47119, 47123, 47129, 47137, 47143, 47147, 47149,\r
+    47161, 47189,\r
+    47207, 47221, 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303,\r
+    47309, 47317,\r
+    47339, 47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419,\r
+    47431, 47441,\r
+    47459, 47491, 47497, 47501, 47507, 47513, 47521, 47527, 47533, 47543,\r
+    47563, 47569,\r
+    47581, 47591, 47599, 47609, 47623, 47629, 47639, 47653, 47657, 47659,\r
+    47681, 47699,\r
+    47701, 47711, 47713, 47717, 47737, 47741, 47743, 47777, 47779, 47791,\r
+    47797, 47807,\r
+    47809, 47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917,\r
+    47933, 47939,\r
+    47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049,\r
+    48073, 48079,\r
+    48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, 48187, 48193,\r
+    48197, 48221,\r
+    48239, 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337, 48341,\r
+    48353, 48371,\r
+    48383, 48397, 48407, 48409, 48413, 48437, 48449, 48463, 48473, 48479,\r
+    48481, 48487,\r
+    48491, 48497, 48523, 48527, 48533, 48539, 48541, 48563, 48571, 48589,\r
+    48593, 48611,\r
+    48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733,\r
+    48751, 48757,\r
+    48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, 48821, 48823,\r
+    48847, 48857,\r
+    48859, 48869, 48871, 48883, 48889, 48907, 48947, 48953, 48973, 48989,\r
+    48991, 49003,\r
+    49009, 49019, 49031, 49033, 49037, 49043, 49057, 49069, 49081, 49103,\r
+    49109, 49117,\r
+    49121, 49123, 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201,\r
+    49207, 49211,\r
+    49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339,\r
+    49363, 49367,\r
+    49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459,\r
+    49463, 49477,\r
+    49481, 49499, 49523, 49529, 49531, 49537, 49547, 49549, 49559, 49597,\r
+    49603, 49613,\r
+    49627, 49633, 49639, 49663, 49667, 49669, 49681, 49697, 49711, 49727,\r
+    49739, 49741,\r
+    49747, 49757, 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831,\r
+    49843, 49853,\r
+    49871, 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957,\r
+    49991, 49993,\r
+    49999, 50021, 50023, 50033, 50047, 50051, 50053, 50069, 50077, 50087,\r
+    50093, 50101,\r
+    50111, 50119, 50123, 50129, 50131, 50147, 50153, 50159, 50177, 50207,\r
+    50221, 50227,\r
+    50231, 50261, 50263, 50273, 50287, 50291, 50311, 50321, 50329, 50333,\r
+    50341, 50359,\r
+    50363, 50377, 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461,\r
+    50497, 50503,\r
+    50513, 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593,\r
+    50599, 50627,\r
+    50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767, 50773,\r
+    50777, 50789,\r
+    50821, 50833, 50839, 50849, 50857, 50867, 50873, 50891, 50893, 50909,\r
+    50923, 50929,\r
+    50951, 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047,\r
+    51059, 51061,\r
+    51071, 51109, 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197,\r
+    51199, 51203,\r
+    51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307, 51329,\r
+    51341, 51343,\r
+    51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, 51427, 51431,\r
+    51437, 51439,\r
+    51449, 51461, 51473, 51479, 51481, 51487, 51503, 51511, 51517, 51521,\r
+    51539, 51551,\r
+    51563, 51577, 51581, 51593, 51599, 51607, 51613, 51631, 51637, 51647,\r
+    51659, 51673,\r
+    51679, 51683, 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787,\r
+    51797, 51803,\r
+    51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899,\r
+    51907, 51913,\r
+    51929, 51941, 51949, 51971, 51973, 51977, 51991, 52009, 52021, 52027,\r
+    52051, 52057,\r
+    52067, 52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177,\r
+    52181, 52183,\r
+    52189, 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289, 52291,\r
+    52301, 52313,\r
+    52321, 52361, 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457,\r
+    52489, 52501,\r
+    52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579,\r
+    52583, 52609,\r
+    52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709, 52711, 52721,\r
+    52727, 52733,\r
+    52747, 52757, 52769, 52783, 52807, 52813, 52817, 52837, 52859, 52861,\r
+    52879, 52883,\r
+    52889, 52901, 52903, 52919, 52937, 52951, 52957, 52963, 52967, 52973,\r
+    52981, 52999,\r
+    53003, 53017, 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101,\r
+    53113, 53117,\r
+    53129, 53147, 53149, 53161, 53171, 53173, 53189, 53197, 53201, 53231,\r
+    53233, 53239,\r
+    53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, 53353, 53359,\r
+    53377, 53381,\r
+    53401, 53407, 53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507,\r
+    53527, 53549,\r
+    53551, 53569, 53591, 53593, 53597, 53609, 53611, 53617, 53623, 53629,\r
+    53633, 53639,\r
+    53653, 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773,\r
+    53777, 53783,\r
+    53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891,\r
+    53897, 53899,\r
+    53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, 54001, 54011,\r
+    54013, 54037,\r
+    54049, 54059, 54083, 54091, 54101, 54121, 54133, 54139, 54151, 54163,\r
+    54167, 54181,\r
+    54193, 54217, 54251, 54269, 54277, 54287, 54293, 54311, 54319, 54323,\r
+    54331, 54347,\r
+    54361, 54367, 54371, 54377, 54401, 54403, 54409, 54413, 54419, 54421,\r
+    54437, 54443,\r
+    54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541,\r
+    54547, 54559,\r
+    54563, 54577, 54581, 54583, 54601, 54617, 54623, 54629, 54631, 54647,\r
+    54667, 54673,\r
+    54679, 54709, 54713, 54721, 54727, 54751, 54767, 54773, 54779, 54787,\r
+    54799, 54829,\r
+    54833, 54851, 54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949,\r
+    54959, 54973,\r
+    54979, 54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073,\r
+    55079, 55103,\r
+    55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217,\r
+    55219, 55229,\r
+    55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343,\r
+    55351, 55373,\r
+    55381, 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501, 55511,\r
+    55529, 55541,\r
+    55547, 55579, 55589, 55603, 55609, 55619, 55621, 55631, 55633, 55639,\r
+    55661, 55663,\r
+    55667, 55673, 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763,\r
+    55787, 55793,\r
+    55799, 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849,\r
+    55871, 55889,\r
+    55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, 55967, 55987,\r
+    55997, 56003,\r
+    56009, 56039, 56041, 56053, 56081, 56087, 56093, 56099, 56101, 56113,\r
+    56123, 56131,\r
+    56149, 56167, 56171, 56179, 56197, 56207, 56209, 56237, 56239, 56249,\r
+    56263, 56267,\r
+    56269, 56299, 56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401,\r
+    56417, 56431,\r
+    56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503,\r
+    56509, 56519,\r
+    56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629,\r
+    56633, 56659,\r
+    56663, 56671, 56681, 56687, 56701, 56711, 56713, 56731, 56737, 56747,\r
+    56767, 56773,\r
+    56779, 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857, 56873,\r
+    56891, 56893,\r
+    56897, 56909, 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963,\r
+    56983, 56989,\r
+    56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097,\r
+    57107, 57119,\r
+    57131, 57139, 57143, 57149, 57163, 57173, 57179, 57191, 57193, 57203,\r
+    57221, 57223,\r
+    57241, 57251, 57259, 57269, 57271, 57283, 57287, 57301, 57329, 57331,\r
+    57347, 57349,\r
+    57367, 57373, 57383, 57389, 57397, 57413, 57427, 57457, 57467, 57487,\r
+    57493, 57503,\r
+    57527, 57529, 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641,\r
+    57649, 57653,\r
+    57667, 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737,\r
+    57751, 57773,\r
+    57781, 57787, 57791, 57793, 57803, 57809, 57829, 57839, 57847, 57853,\r
+    57859, 57881,\r
+    57899, 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013,\r
+    58027, 58031,\r
+    58043, 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129,\r
+    58147, 58151,\r
+    58153, 58169, 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229,\r
+    58231, 58237,\r
+    58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379,\r
+    58391, 58393,\r
+    58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, 58477, 58481,\r
+    58511, 58537,\r
+    58543, 58549, 58567, 58573, 58579, 58601, 58603, 58613, 58631, 58657,\r
+    58661, 58679,\r
+    58687, 58693, 58699, 58711, 58727, 58733, 58741, 58757, 58763, 58771,\r
+    58787, 58789,\r
+    58831, 58889, 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943,\r
+    58963, 58967,\r
+    58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051, 59053,\r
+    59063, 59069,\r
+    59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159,\r
+    59167, 59183,\r
+    59197, 59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273,\r
+    59281, 59333,\r
+    59341, 59351, 59357, 59359, 59369, 59377, 59387, 59393, 59399, 59407,\r
+    59417, 59419,\r
+    59441, 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513,\r
+    59539, 59557,\r
+    59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659,\r
+    59663, 59669,\r
+    59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747, 59753, 59771,\r
+    59779, 59791,\r
+    59797, 59809, 59833, 59863, 59879, 59887, 59921, 59929, 59951, 59957,\r
+    59971, 59981,\r
+    59999, 60013, 60017, 60029, 60037, 60041, 60077, 60083, 60089, 60091,\r
+    60101, 60103,\r
+    60107, 60127, 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217,\r
+    60223, 60251,\r
+    60257, 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353,\r
+    60373, 60383,\r
+    60397, 60413, 60427, 60443, 60449, 60457, 60493, 60497, 60509, 60521,\r
+    60527, 60539,\r
+    60589, 60601, 60607, 60611, 60617, 60623, 60631, 60637, 60647, 60649,\r
+    60659, 60661,\r
+    60679, 60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763,\r
+    60773, 60779,\r
+    60793, 60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913,\r
+    60917, 60919,\r
+    60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043,\r
+    61051, 61057,\r
+    61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211, 61223,\r
+    61231, 61253,\r
+    61261, 61283, 61291, 61297, 61331, 61333, 61339, 61343, 61357, 61363,\r
+    61379, 61381,\r
+    61403, 61409, 61417, 61441, 61463, 61469, 61471, 61483, 61487, 61493,\r
+    61507, 61511,\r
+    61519, 61543, 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613,\r
+    61627, 61631,\r
+    61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717,\r
+    61723, 61729,\r
+    61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, 61871, 61879,\r
+    61909, 61927,\r
+    61933, 61949, 61961, 61967, 61979, 61981, 61987, 61991, 62003, 62011,\r
+    62017, 62039,\r
+    62047, 62053, 62057, 62071, 62081, 62099, 62119, 62129, 62131, 62137,\r
+    62141, 62143,\r
+    62171, 62189, 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297,\r
+    62299, 62303,\r
+    62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459,\r
+    62467, 62473,\r
+    62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581,\r
+    62591, 62597,\r
+    62603, 62617, 62627, 62633, 62639, 62653, 62659, 62683, 62687, 62701,\r
+    62723, 62731,\r
+    62743, 62753, 62761, 62773, 62791, 62801, 62819, 62827, 62851, 62861,\r
+    62869, 62873,\r
+    62897, 62903, 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983,\r
+    62987, 62989,\r
+    63029, 63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127,\r
+    63131, 63149,\r
+    63179, 63197, 63199, 63211, 63241, 63247, 63277, 63281, 63299, 63311,\r
+    63313, 63317,\r
+    63331, 63337, 63347, 63353, 63361, 63367, 63377, 63389, 63391, 63397,\r
+    63409, 63419,\r
+    63421, 63439, 63443, 63463, 63467, 63473, 63487, 63493, 63499, 63521,\r
+    63527, 63533,\r
+    63541, 63559, 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617,\r
+    63629, 63647,\r
+    63649, 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719,\r
+    63727, 63737,\r
+    63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, 63823, 63839,\r
+    63841, 63853,\r
+    63857, 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007,\r
+    64013, 64019,\r
+    64033, 64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151, 64153,\r
+    64157, 64171,\r
+    64187, 64189, 64217, 64223, 64231, 64237, 64271, 64279, 64283, 64301,\r
+    64303, 64319,\r
+    64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453,\r
+    64483, 64489,\r
+    64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, 64609, 64613,\r
+    64621, 64627,\r
+    64633, 64661, 64663, 64667, 64679, 64693, 64709, 64717, 64747, 64763,\r
+    64781, 64783,\r
+    64793, 64811, 64817, 64849, 64853, 64871, 64877, 64879, 64891, 64901,\r
+    64919, 64921,\r
+    64927, 64937, 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033,\r
+    65053, 65063,\r
+    65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147,\r
+    65167, 65171,\r
+    65173, 65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287,\r
+    65293, 65309,\r
+    65323, 65327, 65353, 65357, 65371, 65381, 65393, 65407, 65413, 65419,\r
+    65423, 65437,\r
+    65447, 65449, 65479, 65497, 65519, 65521,\r
+};\r
+\r
+#define NPRIMES (sizeof(primes) / sizeof(*primes))\r
+\r
+/*\r
+ * Generate a prime. We can deal with various extra properties of\r
+ * the prime:\r
+ * \r
+ *  - to speed up use in RSA, we can arrange to select a prime with\r
+ *    the property (prime % modulus) != residue.\r
+ * \r
+ *  - for use in DSA, we can arrange to select a prime which is one\r
+ *    more than a multiple of a dirty great bignum. In this case\r
+ *    `bits' gives the size of the factor by which we _multiply_\r
+ *    that bignum, rather than the size of the whole number.\r
+ */\r
+Bignum primegen(int bits, int modulus, int residue, Bignum factor,\r
+               int phase, progfn_t pfn, void *pfnparam)\r
+{\r
+    int i, k, v, byte, bitsleft, check, checks;\r
+    unsigned long delta;\r
+    unsigned long moduli[NPRIMES + 1];\r
+    unsigned long residues[NPRIMES + 1];\r
+    unsigned long multipliers[NPRIMES + 1];\r
+    Bignum p, pm1, q, wqp, wqp2;\r
+    int progress = 0;\r
+\r
+    byte = 0;\r
+    bitsleft = 0;\r
+\r
+  STARTOVER:\r
+\r
+    pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);\r
+\r
+    /*\r
+     * Generate a k-bit random number with top and bottom bits set.\r
+     * Alternatively, if `factor' is nonzero, generate a k-bit\r
+     * random number with the top bit set and the bottom bit clear,\r
+     * multiply it by `factor', and add one.\r
+     */\r
+    p = bn_power_2(bits - 1);\r
+    for (i = 0; i < bits; i++) {\r
+       if (i == 0 || i == bits - 1)\r
+           v = (i != 0 || !factor) ? 1 : 0;\r
+       else {\r
+           if (bitsleft <= 0)\r
+               bitsleft = 8, byte = random_byte();\r
+           v = byte & 1;\r
+           byte >>= 1;\r
+           bitsleft--;\r
+       }\r
+       bignum_set_bit(p, i, v);\r
+    }\r
+    if (factor) {\r
+       Bignum tmp = p;\r
+       p = bigmul(tmp, factor);\r
+       freebn(tmp);\r
+       assert(bignum_bit(p, 0) == 0);\r
+       bignum_set_bit(p, 0, 1);\r
+    }\r
+\r
+    /*\r
+     * Ensure this random number is coprime to the first few\r
+     * primes, by repeatedly adding either 2 or 2*factor to it\r
+     * until it is.\r
+     */\r
+    for (i = 0; i < NPRIMES; i++) {\r
+       moduli[i] = primes[i];\r
+       residues[i] = bignum_mod_short(p, primes[i]);\r
+       if (factor)\r
+           multipliers[i] = bignum_mod_short(factor, primes[i]);\r
+       else\r
+           multipliers[i] = 1;\r
+    }\r
+    moduli[NPRIMES] = modulus;\r
+    residues[NPRIMES] = (bignum_mod_short(p, (unsigned short) modulus)\r
+                        + modulus - residue);\r
+    if (factor)\r
+       multipliers[NPRIMES] = bignum_mod_short(factor, modulus);\r
+    else\r
+       multipliers[NPRIMES] = 1;\r
+    delta = 0;\r
+    while (1) {\r
+       for (i = 0; i < (sizeof(moduli) / sizeof(*moduli)); i++)\r
+           if (!((residues[i] + delta * multipliers[i]) % moduli[i]))\r
+               break;\r
+       if (i < (sizeof(moduli) / sizeof(*moduli))) {   /* we broke */\r
+           delta += 2;\r
+           if (delta > 65536) {\r
+               freebn(p);\r
+               goto STARTOVER;\r
+           }\r
+           continue;\r
+       }\r
+       break;\r
+    }\r
+    q = p;\r
+    if (factor) {\r
+       Bignum tmp;\r
+       tmp = bignum_from_long(delta);\r
+       p = bigmuladd(tmp, factor, q);\r
+       freebn(tmp);\r
+    } else {\r
+       p = bignum_add_long(q, delta);\r
+    }\r
+    freebn(q);\r
+\r
+    /*\r
+     * Now apply the Miller-Rabin primality test a few times. First\r
+     * work out how many checks are needed.\r
+     */\r
+    checks = 27;\r
+    if (bits >= 150)\r
+       checks = 18;\r
+    if (bits >= 200)\r
+       checks = 15;\r
+    if (bits >= 250)\r
+       checks = 12;\r
+    if (bits >= 300)\r
+       checks = 9;\r
+    if (bits >= 350)\r
+       checks = 8;\r
+    if (bits >= 400)\r
+       checks = 7;\r
+    if (bits >= 450)\r
+       checks = 6;\r
+    if (bits >= 550)\r
+       checks = 5;\r
+    if (bits >= 650)\r
+       checks = 4;\r
+    if (bits >= 850)\r
+       checks = 3;\r
+    if (bits >= 1300)\r
+       checks = 2;\r
+\r
+    /*\r
+     * Next, write p-1 as q*2^k.\r
+     */\r
+    for (k = 0; bignum_bit(p, k) == !k; k++)\r
+       continue;       /* find first 1 bit in p-1 */\r
+    q = bignum_rshift(p, k);\r
+    /* And store p-1 itself, which we'll need. */\r
+    pm1 = copybn(p);\r
+    decbn(pm1);\r
+\r
+    /*\r
+     * Now, for each check ...\r
+     */\r
+    for (check = 0; check < checks; check++) {\r
+       Bignum w;\r
+\r
+       /*\r
+        * Invent a random number between 1 and p-1 inclusive.\r
+        */\r
+       while (1) {\r
+           w = bn_power_2(bits - 1);\r
+           for (i = 0; i < bits; i++) {\r
+               if (bitsleft <= 0)\r
+                   bitsleft = 8, byte = random_byte();\r
+               v = byte & 1;\r
+               byte >>= 1;\r
+               bitsleft--;\r
+               bignum_set_bit(w, i, v);\r
+           }\r
+           bn_restore_invariant(w);\r
+           if (bignum_cmp(w, p) >= 0 || bignum_cmp(w, Zero) == 0) {\r
+               freebn(w);\r
+               continue;\r
+           }\r
+           break;\r
+       }\r
+\r
+       pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);\r
+\r
+       /*\r
+        * Compute w^q mod p.\r
+        */\r
+       wqp = modpow(w, q, p);\r
+       freebn(w);\r
+\r
+       /*\r
+        * See if this is 1, or if it is -1, or if it becomes -1\r
+        * when squared at most k-1 times.\r
+        */\r
+       if (bignum_cmp(wqp, One) == 0 || bignum_cmp(wqp, pm1) == 0) {\r
+           freebn(wqp);\r
+           continue;\r
+       }\r
+       for (i = 0; i < k - 1; i++) {\r
+           wqp2 = modmul(wqp, wqp, p);\r
+           freebn(wqp);\r
+           wqp = wqp2;\r
+           if (bignum_cmp(wqp, pm1) == 0)\r
+               break;\r
+       }\r
+       if (i < k - 1) {\r
+           freebn(wqp);\r
+           continue;\r
+       }\r
+\r
+       /*\r
+        * It didn't. Therefore, w is a witness for the\r
+        * compositeness of p.\r
+        */\r
+       freebn(wqp);\r
+       freebn(p);\r
+       freebn(pm1);\r
+       freebn(q);\r
+       goto STARTOVER;\r
+    }\r
+\r
+    /*\r
+     * We have a prime!\r
+     */\r
+    freebn(q);\r
+    freebn(pm1);\r
+    return p;\r
+}\r
diff --git a/putty/SSHPUBK.C b/putty/SSHPUBK.C
new file mode 100644 (file)
index 0000000..7b5a690
--- /dev/null
@@ -0,0 +1,1217 @@
+/*\r
+ * Generic SSH public-key handling operations. In particular,\r
+ * reading of SSH public-key files, and also the generic `sign'\r
+ * operation for SSH-2 (which checks the type of the key and\r
+ * dispatches to the appropriate key-type specific function).\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "misc.h"\r
+\r
+#define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n"\r
+\r
+#define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\\r
+                          (x)-'a'<26 ? (x)-'a'+26 :\\r
+                          (x)-'0'<10 ? (x)-'0'+52 :\\r
+                          (x)=='+' ? 62 : \\r
+                          (x)=='/' ? 63 : 0 )\r
+\r
+static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,\r
+                          char **commentptr, char *passphrase,\r
+                          const char **error)\r
+{\r
+    unsigned char buf[16384];\r
+    unsigned char keybuf[16];\r
+    int len;\r
+    int i, j, ciphertype;\r
+    int ret = 0;\r
+    struct MD5Context md5c;\r
+    char *comment;\r
+\r
+    *error = NULL;\r
+\r
+    /* Slurp the whole file (minus the header) into a buffer. */\r
+    len = fread(buf, 1, sizeof(buf), fp);\r
+    fclose(fp);\r
+    if (len < 0 || len == sizeof(buf)) {\r
+       *error = "error reading file";\r
+       goto end;                      /* file too big or not read */\r
+    }\r
+\r
+    i = 0;\r
+    *error = "file format error";\r
+\r
+    /*\r
+     * A zero byte. (The signature includes a terminating NUL.)\r
+     */\r
+    if (len - i < 1 || buf[i] != 0)\r
+       goto end;\r
+    i++;\r
+\r
+    /* One byte giving encryption type, and one reserved uint32. */\r
+    if (len - i < 1)\r
+       goto end;\r
+    ciphertype = buf[i];\r
+    if (ciphertype != 0 && ciphertype != SSH_CIPHER_3DES)\r
+       goto end;\r
+    i++;\r
+    if (len - i < 4)\r
+       goto end;                      /* reserved field not present */\r
+    if (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0\r
+       || buf[i + 3] != 0) goto end;  /* reserved field nonzero, panic! */\r
+    i += 4;\r
+\r
+    /* Now the serious stuff. An ordinary SSH-1 public key. */\r
+    i += makekey(buf + i, len, key, NULL, 1);\r
+    if (i < 0)\r
+       goto end;                      /* overran */\r
+\r
+    /* Next, the comment field. */\r
+    j = GET_32BIT(buf + i);\r
+    i += 4;\r
+    if (len - i < j)\r
+       goto end;\r
+    comment = snewn(j + 1, char);\r
+    if (comment) {\r
+       memcpy(comment, buf + i, j);\r
+       comment[j] = '\0';\r
+    }\r
+    i += j;\r
+    if (commentptr)\r
+       *commentptr = dupstr(comment);\r
+    if (key)\r
+       key->comment = comment;\r
+    else\r
+       sfree(comment);\r
+\r
+    if (pub_only) {\r
+       ret = 1;\r
+       goto end;\r
+    }\r
+\r
+    if (!key) {\r
+       ret = ciphertype != 0;\r
+       *error = NULL;\r
+       goto end;\r
+    }\r
+\r
+    /*\r
+     * Decrypt remainder of buffer.\r
+     */\r
+    if (ciphertype) {\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Final(keybuf, &md5c);\r
+       des3_decrypt_pubkey(keybuf, buf + i, (len - i + 7) & ~7);\r
+       memset(keybuf, 0, sizeof(keybuf));      /* burn the evidence */\r
+    }\r
+\r
+    /*\r
+     * We are now in the secret part of the key. The first four\r
+     * bytes should be of the form a, b, a, b.\r
+     */\r
+    if (len - i < 4)\r
+       goto end;\r
+    if (buf[i] != buf[i + 2] || buf[i + 1] != buf[i + 3]) {\r
+       *error = "wrong passphrase";\r
+       ret = -1;\r
+       goto end;\r
+    }\r
+    i += 4;\r
+\r
+    /*\r
+     * After that, we have one further bignum which is our\r
+     * decryption exponent, and then the three auxiliary values\r
+     * (iqmp, q, p).\r
+     */\r
+    j = makeprivate(buf + i, len - i, key);\r
+    if (j < 0) goto end;\r
+    i += j;\r
+    j = ssh1_read_bignum(buf + i, len - i, &key->iqmp);\r
+    if (j < 0) goto end;\r
+    i += j;\r
+    j = ssh1_read_bignum(buf + i, len - i, &key->q);\r
+    if (j < 0) goto end;\r
+    i += j;\r
+    j = ssh1_read_bignum(buf + i, len - i, &key->p);\r
+    if (j < 0) goto end;\r
+    i += j;\r
+\r
+    if (!rsa_verify(key)) {\r
+       *error = "rsa_verify failed";\r
+       freersakey(key);\r
+       ret = 0;\r
+    } else\r
+       ret = 1;\r
+\r
+  end:\r
+    memset(buf, 0, sizeof(buf));       /* burn the evidence */\r
+    return ret;\r
+}\r
+\r
+int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase,\r
+              const char **errorstr)\r
+{\r
+    FILE *fp;\r
+    char buf[64];\r
+    int ret = 0;\r
+    const char *error = NULL;\r
+\r
+    fp = f_open(*filename, "rb", FALSE);\r
+    if (!fp) {\r
+       error = "can't open file";\r
+       goto end;\r
+    }\r
+\r
+    /*\r
+     * Read the first line of the file and see if it's a v1 private\r
+     * key file.\r
+     */\r
+    if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {\r
+       /*\r
+        * This routine will take care of calling fclose() for us.\r
+        */\r
+       ret = loadrsakey_main(fp, key, FALSE, NULL, passphrase, &error);\r
+       fp = NULL;\r
+       goto end;\r
+    }\r
+\r
+    /*\r
+     * Otherwise, we have nothing. Return empty-handed.\r
+     */\r
+    error = "not an SSH-1 RSA file";\r
+\r
+  end:\r
+    if (fp)\r
+       fclose(fp);\r
+    if ((ret != 1) && errorstr)\r
+       *errorstr = error;\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * See whether an RSA key is encrypted. Return its comment field as\r
+ * well.\r
+ */\r
+int rsakey_encrypted(const Filename *filename, char **comment)\r
+{\r
+    FILE *fp;\r
+    char buf[64];\r
+\r
+    fp = f_open(*filename, "rb", FALSE);\r
+    if (!fp)\r
+       return 0;                      /* doesn't even exist */\r
+\r
+    /*\r
+     * Read the first line of the file and see if it's a v1 private\r
+     * key file.\r
+     */\r
+    if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {\r
+       const char *dummy;\r
+       /*\r
+        * This routine will take care of calling fclose() for us.\r
+        */\r
+       return loadrsakey_main(fp, NULL, FALSE, comment, NULL, &dummy);\r
+    }\r
+    fclose(fp);\r
+    return 0;                         /* wasn't the right kind of file */\r
+}\r
+\r
+/*\r
+ * Return a malloc'ed chunk of memory containing the public blob of\r
+ * an RSA key, as given in the agent protocol (modulus bits,\r
+ * exponent, modulus).\r
+ */\r
+int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,\r
+                  char **commentptr, const char **errorstr)\r
+{\r
+    FILE *fp;\r
+    char buf[64];\r
+    struct RSAKey key;\r
+    int ret;\r
+    const char *error = NULL;\r
+\r
+    /* Default return if we fail. */\r
+    *blob = NULL;\r
+    *bloblen = 0;\r
+    ret = 0;\r
+\r
+    fp = f_open(*filename, "rb", FALSE);\r
+    if (!fp) {\r
+       error = "can't open file";\r
+       goto end;\r
+    }\r
+\r
+    /*\r
+     * Read the first line of the file and see if it's a v1 private\r
+     * key file.\r
+     */\r
+    if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) {\r
+       memset(&key, 0, sizeof(key));\r
+       if (loadrsakey_main(fp, &key, TRUE, commentptr, NULL, &error)) {\r
+           *blob = rsa_public_blob(&key, bloblen);\r
+           freersakey(&key);\r
+           ret = 1;\r
+           fp = NULL;\r
+       }\r
+    } else {\r
+       error = "not an SSH-1 RSA file";\r
+    }\r
+\r
+  end:\r
+    if (fp)\r
+       fclose(fp);\r
+    if ((ret != 1) && errorstr)\r
+       *errorstr = error;\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Save an RSA key file. Return nonzero on success.\r
+ */\r
+int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase)\r
+{\r
+    unsigned char buf[16384];\r
+    unsigned char keybuf[16];\r
+    struct MD5Context md5c;\r
+    unsigned char *p, *estart;\r
+    FILE *fp;\r
+\r
+    /*\r
+     * Write the initial signature.\r
+     */\r
+    p = buf;\r
+    memcpy(p, rsa_signature, sizeof(rsa_signature));\r
+    p += sizeof(rsa_signature);\r
+\r
+    /*\r
+     * One byte giving encryption type, and one reserved (zero)\r
+     * uint32.\r
+     */\r
+    *p++ = (passphrase ? SSH_CIPHER_3DES : 0);\r
+    PUT_32BIT(p, 0);\r
+    p += 4;\r
+\r
+    /*\r
+     * An ordinary SSH-1 public key consists of: a uint32\r
+     * containing the bit count, then two bignums containing the\r
+     * modulus and exponent respectively.\r
+     */\r
+    PUT_32BIT(p, bignum_bitcount(key->modulus));\r
+    p += 4;\r
+    p += ssh1_write_bignum(p, key->modulus);\r
+    p += ssh1_write_bignum(p, key->exponent);\r
+\r
+    /*\r
+     * A string containing the comment field.\r
+     */\r
+    if (key->comment) {\r
+       PUT_32BIT(p, strlen(key->comment));\r
+       p += 4;\r
+       memcpy(p, key->comment, strlen(key->comment));\r
+       p += strlen(key->comment);\r
+    } else {\r
+       PUT_32BIT(p, 0);\r
+       p += 4;\r
+    }\r
+\r
+    /*\r
+     * The encrypted portion starts here.\r
+     */\r
+    estart = p;\r
+\r
+    /*\r
+     * Two bytes, then the same two bytes repeated.\r
+     */\r
+    *p++ = random_byte();\r
+    *p++ = random_byte();\r
+    p[0] = p[-2];\r
+    p[1] = p[-1];\r
+    p += 2;\r
+\r
+    /*\r
+     * Four more bignums: the decryption exponent, then iqmp, then\r
+     * q, then p.\r
+     */\r
+    p += ssh1_write_bignum(p, key->private_exponent);\r
+    p += ssh1_write_bignum(p, key->iqmp);\r
+    p += ssh1_write_bignum(p, key->q);\r
+    p += ssh1_write_bignum(p, key->p);\r
+\r
+    /*\r
+     * Now write zeros until the encrypted portion is a multiple of\r
+     * 8 bytes.\r
+     */\r
+    while ((p - estart) % 8)\r
+       *p++ = '\0';\r
+\r
+    /*\r
+     * Now encrypt the encrypted portion.\r
+     */\r
+    if (passphrase) {\r
+       MD5Init(&md5c);\r
+       MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));\r
+       MD5Final(keybuf, &md5c);\r
+       des3_encrypt_pubkey(keybuf, estart, p - estart);\r
+       memset(keybuf, 0, sizeof(keybuf));      /* burn the evidence */\r
+    }\r
+\r
+    /*\r
+     * Done. Write the result to the file.\r
+     */\r
+    fp = f_open(*filename, "wb", TRUE);\r
+    if (fp) {\r
+       int ret = (fwrite(buf, 1, p - buf, fp) == (size_t) (p - buf));\r
+        if (fclose(fp))\r
+            ret = 0;\r
+       return ret;\r
+    } else\r
+       return 0;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * SSH-2 private key load/store functions.\r
+ */\r
+\r
+/*\r
+ * PuTTY's own format for SSH-2 keys is as follows:\r
+ *\r
+ * The file is text. Lines are terminated by CRLF, although CR-only\r
+ * and LF-only are tolerated on input.\r
+ *\r
+ * The first line says "PuTTY-User-Key-File-2: " plus the name of the\r
+ * algorithm ("ssh-dss", "ssh-rsa" etc).\r
+ *\r
+ * The next line says "Encryption: " plus an encryption type.\r
+ * Currently the only supported encryption types are "aes256-cbc"\r
+ * and "none".\r
+ *\r
+ * The next line says "Comment: " plus the comment string.\r
+ *\r
+ * Next there is a line saying "Public-Lines: " plus a number N.\r
+ * The following N lines contain a base64 encoding of the public\r
+ * part of the key. This is encoded as the standard SSH-2 public key\r
+ * blob (with no initial length): so for RSA, for example, it will\r
+ * read\r
+ *\r
+ *    string "ssh-rsa"\r
+ *    mpint  exponent\r
+ *    mpint  modulus\r
+ *\r
+ * Next, there is a line saying "Private-Lines: " plus a number N,\r
+ * and then N lines containing the (potentially encrypted) private\r
+ * part of the key. For the key type "ssh-rsa", this will be\r
+ * composed of\r
+ *\r
+ *    mpint  private_exponent\r
+ *    mpint  p                  (the larger of the two primes)\r
+ *    mpint  q                  (the smaller prime)\r
+ *    mpint  iqmp               (the inverse of q modulo p)\r
+ *    data   padding            (to reach a multiple of the cipher block size)\r
+ *\r
+ * And for "ssh-dss", it will be composed of\r
+ *\r
+ *    mpint  x                  (the private key parameter)\r
+ *  [ string hash   20-byte hash of mpints p || q || g   only in old format ]\r
+ * \r
+ * Finally, there is a line saying "Private-MAC: " plus a hex\r
+ * representation of a HMAC-SHA-1 of:\r
+ *\r
+ *    string  name of algorithm ("ssh-dss", "ssh-rsa")\r
+ *    string  encryption type\r
+ *    string  comment\r
+ *    string  public-blob\r
+ *    string  private-plaintext (the plaintext version of the\r
+ *                               private part, including the final\r
+ *                               padding)\r
+ * \r
+ * The key to the MAC is itself a SHA-1 hash of:\r
+ * \r
+ *    data    "putty-private-key-file-mac-key"\r
+ *    data    passphrase\r
+ *\r
+ * (An empty passphrase is used for unencrypted keys.)\r
+ *\r
+ * If the key is encrypted, the encryption key is derived from the\r
+ * passphrase by means of a succession of SHA-1 hashes. Each hash\r
+ * is the hash of:\r
+ *\r
+ *    uint32  sequence-number\r
+ *    data    passphrase\r
+ *\r
+ * where the sequence-number increases from zero. As many of these\r
+ * hashes are used as necessary.\r
+ *\r
+ * For backwards compatibility with snapshots between 0.51 and\r
+ * 0.52, we also support the older key file format, which begins\r
+ * with "PuTTY-User-Key-File-1" (version number differs). In this\r
+ * format the Private-MAC: field only covers the private-plaintext\r
+ * field and nothing else (and without the 4-byte string length on\r
+ * the front too). Moreover, the Private-MAC: field can be replaced\r
+ * with a Private-Hash: field which is a plain SHA-1 hash instead of\r
+ * an HMAC (this was generated for unencrypted keys).\r
+ */\r
+\r
+static int read_header(FILE * fp, char *header)\r
+{\r
+    int len = 39;\r
+    int c;\r
+\r
+    while (len > 0) {\r
+       c = fgetc(fp);\r
+       if (c == '\n' || c == '\r' || c == EOF)\r
+           return 0;                  /* failure */\r
+       if (c == ':') {\r
+           c = fgetc(fp);\r
+           if (c != ' ')\r
+               return 0;\r
+           *header = '\0';\r
+           return 1;                  /* success! */\r
+       }\r
+       if (len == 0)\r
+           return 0;                  /* failure */\r
+       *header++ = c;\r
+       len--;\r
+    }\r
+    return 0;                         /* failure */\r
+}\r
+\r
+static char *read_body(FILE * fp)\r
+{\r
+    char *text;\r
+    int len;\r
+    int size;\r
+    int c;\r
+\r
+    size = 128;\r
+    text = snewn(size, char);\r
+    len = 0;\r
+    text[len] = '\0';\r
+\r
+    while (1) {\r
+       c = fgetc(fp);\r
+       if (c == '\r' || c == '\n' || c == EOF) {\r
+           if (c != EOF) {\r
+               c = fgetc(fp);\r
+               if (c != '\r' && c != '\n')\r
+                   ungetc(c, fp);\r
+           }\r
+           return text;\r
+       }\r
+       if (len + 1 >= size) {\r
+           size += 128;\r
+           text = sresize(text, size, char);\r
+       }\r
+       text[len++] = c;\r
+       text[len] = '\0';\r
+    }\r
+}\r
+\r
+int base64_decode_atom(char *atom, unsigned char *out)\r
+{\r
+    int vals[4];\r
+    int i, v, len;\r
+    unsigned word;\r
+    char c;\r
+\r
+    for (i = 0; i < 4; i++) {\r
+       c = atom[i];\r
+       if (c >= 'A' && c <= 'Z')\r
+           v = c - 'A';\r
+       else if (c >= 'a' && c <= 'z')\r
+           v = c - 'a' + 26;\r
+       else if (c >= '0' && c <= '9')\r
+           v = c - '0' + 52;\r
+       else if (c == '+')\r
+           v = 62;\r
+       else if (c == '/')\r
+           v = 63;\r
+       else if (c == '=')\r
+           v = -1;\r
+       else\r
+           return 0;                  /* invalid atom */\r
+       vals[i] = v;\r
+    }\r
+\r
+    if (vals[0] == -1 || vals[1] == -1)\r
+       return 0;\r
+    if (vals[2] == -1 && vals[3] != -1)\r
+       return 0;\r
+\r
+    if (vals[3] != -1)\r
+       len = 3;\r
+    else if (vals[2] != -1)\r
+       len = 2;\r
+    else\r
+       len = 1;\r
+\r
+    word = ((vals[0] << 18) |\r
+           (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));\r
+    out[0] = (word >> 16) & 0xFF;\r
+    if (len > 1)\r
+       out[1] = (word >> 8) & 0xFF;\r
+    if (len > 2)\r
+       out[2] = word & 0xFF;\r
+    return len;\r
+}\r
+\r
+static unsigned char *read_blob(FILE * fp, int nlines, int *bloblen)\r
+{\r
+    unsigned char *blob;\r
+    char *line;\r
+    int linelen, len;\r
+    int i, j, k;\r
+\r
+    /* We expect at most 64 base64 characters, ie 48 real bytes, per line. */\r
+    blob = snewn(48 * nlines, unsigned char);\r
+    len = 0;\r
+    for (i = 0; i < nlines; i++) {\r
+       line = read_body(fp);\r
+       if (!line) {\r
+           sfree(blob);\r
+           return NULL;\r
+       }\r
+       linelen = strlen(line);\r
+       if (linelen % 4 != 0 || linelen > 64) {\r
+           sfree(blob);\r
+           sfree(line);\r
+           return NULL;\r
+       }\r
+       for (j = 0; j < linelen; j += 4) {\r
+           k = base64_decode_atom(line + j, blob + len);\r
+           if (!k) {\r
+               sfree(line);\r
+               sfree(blob);\r
+               return NULL;\r
+           }\r
+           len += k;\r
+       }\r
+       sfree(line);\r
+    }\r
+    *bloblen = len;\r
+    return blob;\r
+}\r
+\r
+/*\r
+ * Magic error return value for when the passphrase is wrong.\r
+ */\r
+struct ssh2_userkey ssh2_wrong_passphrase = {\r
+    NULL, NULL, NULL\r
+};\r
+\r
+const struct ssh_signkey *find_pubkey_alg(const char *name)\r
+{\r
+    if (!strcmp(name, "ssh-rsa"))\r
+       return &ssh_rsa;\r
+    else if (!strcmp(name, "ssh-dss"))\r
+       return &ssh_dss;\r
+    else\r
+       return NULL;\r
+}\r
+\r
+struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,\r
+                                      char *passphrase, const char **errorstr)\r
+{\r
+    FILE *fp;\r
+    char header[40], *b, *encryption, *comment, *mac;\r
+    const struct ssh_signkey *alg;\r
+    struct ssh2_userkey *ret;\r
+    int cipher, cipherblk;\r
+    unsigned char *public_blob, *private_blob;\r
+    int public_blob_len, private_blob_len;\r
+    int i, is_mac, old_fmt;\r
+    int passlen = passphrase ? strlen(passphrase) : 0;\r
+    const char *error = NULL;\r
+\r
+    ret = NULL;                               /* return NULL for most errors */\r
+    encryption = comment = mac = NULL;\r
+    public_blob = private_blob = NULL;\r
+\r
+    fp = f_open(*filename, "rb", FALSE);\r
+    if (!fp) {\r
+       error = "can't open file";\r
+       goto error;\r
+    }\r
+\r
+    /* Read the first header line which contains the key type. */\r
+    if (!read_header(fp, header))\r
+       goto error;\r
+    if (0 == strcmp(header, "PuTTY-User-Key-File-2")) {\r
+       old_fmt = 0;\r
+    } else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) {\r
+       /* this is an old key file; warn and then continue */\r
+       old_keyfile_warning();\r
+       old_fmt = 1;\r
+    } else {\r
+       error = "not a PuTTY SSH-2 private key";\r
+       goto error;\r
+    }\r
+    error = "file format error";\r
+    if ((b = read_body(fp)) == NULL)\r
+       goto error;\r
+    /* Select key algorithm structure. */\r
+    alg = find_pubkey_alg(b);\r
+    if (!alg) {\r
+       sfree(b);\r
+       goto error;\r
+    }\r
+    sfree(b);\r
+\r
+    /* Read the Encryption header line. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Encryption"))\r
+       goto error;\r
+    if ((encryption = read_body(fp)) == NULL)\r
+       goto error;\r
+    if (!strcmp(encryption, "aes256-cbc")) {\r
+       cipher = 1;\r
+       cipherblk = 16;\r
+    } else if (!strcmp(encryption, "none")) {\r
+       cipher = 0;\r
+       cipherblk = 1;\r
+    } else {\r
+       sfree(encryption);\r
+       goto error;\r
+    }\r
+\r
+    /* Read the Comment header line. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))\r
+       goto error;\r
+    if ((comment = read_body(fp)) == NULL)\r
+       goto error;\r
+\r
+    /* Read the Public-Lines header line and the public blob. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))\r
+       goto error;\r
+    if ((b = read_body(fp)) == NULL)\r
+       goto error;\r
+    i = atoi(b);\r
+    sfree(b);\r
+    if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)\r
+       goto error;\r
+\r
+    /* Read the Private-Lines header line and the Private blob. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Private-Lines"))\r
+       goto error;\r
+    if ((b = read_body(fp)) == NULL)\r
+       goto error;\r
+    i = atoi(b);\r
+    sfree(b);\r
+    if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)\r
+       goto error;\r
+\r
+    /* Read the Private-MAC or Private-Hash header line. */\r
+    if (!read_header(fp, header))\r
+       goto error;\r
+    if (0 == strcmp(header, "Private-MAC")) {\r
+       if ((mac = read_body(fp)) == NULL)\r
+           goto error;\r
+       is_mac = 1;\r
+    } else if (0 == strcmp(header, "Private-Hash") && old_fmt) {\r
+       if ((mac = read_body(fp)) == NULL)\r
+           goto error;\r
+       is_mac = 0;\r
+    } else\r
+       goto error;\r
+\r
+    fclose(fp);\r
+    fp = NULL;\r
+\r
+    /*\r
+     * Decrypt the private blob.\r
+     */\r
+    if (cipher) {\r
+       unsigned char key[40];\r
+       SHA_State s;\r
+\r
+       if (!passphrase)\r
+           goto error;\r
+       if (private_blob_len % cipherblk)\r
+           goto error;\r
+\r
+       SHA_Init(&s);\r
+       SHA_Bytes(&s, "\0\0\0\0", 4);\r
+       SHA_Bytes(&s, passphrase, passlen);\r
+       SHA_Final(&s, key + 0);\r
+       SHA_Init(&s);\r
+       SHA_Bytes(&s, "\0\0\0\1", 4);\r
+       SHA_Bytes(&s, passphrase, passlen);\r
+       SHA_Final(&s, key + 20);\r
+       aes256_decrypt_pubkey(key, private_blob, private_blob_len);\r
+    }\r
+\r
+    /*\r
+     * Verify the MAC.\r
+     */\r
+    {\r
+       char realmac[41];\r
+       unsigned char binary[20];\r
+       unsigned char *macdata;\r
+       int maclen;\r
+       int free_macdata;\r
+\r
+       if (old_fmt) {\r
+           /* MAC (or hash) only covers the private blob. */\r
+           macdata = private_blob;\r
+           maclen = private_blob_len;\r
+           free_macdata = 0;\r
+       } else {\r
+           unsigned char *p;\r
+           int namelen = strlen(alg->name);\r
+           int enclen = strlen(encryption);\r
+           int commlen = strlen(comment);\r
+           maclen = (4 + namelen +\r
+                     4 + enclen +\r
+                     4 + commlen +\r
+                     4 + public_blob_len +\r
+                     4 + private_blob_len);\r
+           macdata = snewn(maclen, unsigned char);\r
+           p = macdata;\r
+#define DO_STR(s,len) PUT_32BIT(p,(len));memcpy(p+4,(s),(len));p+=4+(len)\r
+           DO_STR(alg->name, namelen);\r
+           DO_STR(encryption, enclen);\r
+           DO_STR(comment, commlen);\r
+           DO_STR(public_blob, public_blob_len);\r
+           DO_STR(private_blob, private_blob_len);\r
+\r
+           free_macdata = 1;\r
+       }\r
+\r
+       if (is_mac) {\r
+           SHA_State s;\r
+           unsigned char mackey[20];\r
+           char header[] = "putty-private-key-file-mac-key";\r
+\r
+           SHA_Init(&s);\r
+           SHA_Bytes(&s, header, sizeof(header)-1);\r
+           if (cipher && passphrase)\r
+               SHA_Bytes(&s, passphrase, passlen);\r
+           SHA_Final(&s, mackey);\r
+\r
+           hmac_sha1_simple(mackey, 20, macdata, maclen, binary);\r
+\r
+           memset(mackey, 0, sizeof(mackey));\r
+           memset(&s, 0, sizeof(s));\r
+       } else {\r
+           SHA_Simple(macdata, maclen, binary);\r
+       }\r
+\r
+       if (free_macdata) {\r
+           memset(macdata, 0, maclen);\r
+           sfree(macdata);\r
+       }\r
+\r
+       for (i = 0; i < 20; i++)\r
+           sprintf(realmac + 2 * i, "%02x", binary[i]);\r
+\r
+       if (strcmp(mac, realmac)) {\r
+           /* An incorrect MAC is an unconditional Error if the key is\r
+            * unencrypted. Otherwise, it means Wrong Passphrase. */\r
+           if (cipher) {\r
+               error = "wrong passphrase";\r
+               ret = SSH2_WRONG_PASSPHRASE;\r
+           } else {\r
+               error = "MAC failed";\r
+               ret = NULL;\r
+           }\r
+           goto error;\r
+       }\r
+    }\r
+    sfree(mac);\r
+\r
+    /*\r
+     * Create and return the key.\r
+     */\r
+    ret = snew(struct ssh2_userkey);\r
+    ret->alg = alg;\r
+    ret->comment = comment;\r
+    ret->data = alg->createkey(public_blob, public_blob_len,\r
+                              private_blob, private_blob_len);\r
+    if (!ret->data) {\r
+       sfree(ret->comment);\r
+       sfree(ret);\r
+       ret = NULL;\r
+       error = "createkey failed";\r
+       goto error;\r
+    }\r
+    sfree(public_blob);\r
+    sfree(private_blob);\r
+    sfree(encryption);\r
+    if (errorstr)\r
+       *errorstr = NULL;\r
+    return ret;\r
+\r
+    /*\r
+     * Error processing.\r
+     */\r
+  error:\r
+    if (fp)\r
+       fclose(fp);\r
+    if (comment)\r
+       sfree(comment);\r
+    if (encryption)\r
+       sfree(encryption);\r
+    if (mac)\r
+       sfree(mac);\r
+    if (public_blob)\r
+       sfree(public_blob);\r
+    if (private_blob)\r
+       sfree(private_blob);\r
+    if (errorstr)\r
+       *errorstr = error;\r
+    return ret;\r
+}\r
+\r
+unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,\r
+                                   int *pub_blob_len, char **commentptr,\r
+                                   const char **errorstr)\r
+{\r
+    FILE *fp;\r
+    char header[40], *b;\r
+    const struct ssh_signkey *alg;\r
+    unsigned char *public_blob;\r
+    int public_blob_len;\r
+    int i;\r
+    const char *error = NULL;\r
+    char *comment;\r
+\r
+    public_blob = NULL;\r
+\r
+    fp = f_open(*filename, "rb", FALSE);\r
+    if (!fp) {\r
+       error = "can't open file";\r
+       goto error;\r
+    }\r
+\r
+    /* Read the first header line which contains the key type. */\r
+    if (!read_header(fp, header)\r
+       || (0 != strcmp(header, "PuTTY-User-Key-File-2") &&\r
+           0 != strcmp(header, "PuTTY-User-Key-File-1"))) {\r
+       error = "not a PuTTY SSH-2 private key";\r
+       goto error;\r
+    }\r
+    error = "file format error";\r
+    if ((b = read_body(fp)) == NULL)\r
+       goto error;\r
+    /* Select key algorithm structure. */\r
+    alg = find_pubkey_alg(b);\r
+    if (!alg) {\r
+       sfree(b);\r
+       goto error;\r
+    }\r
+    sfree(b);\r
+\r
+    /* Read the Encryption header line. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Encryption"))\r
+       goto error;\r
+    if ((b = read_body(fp)) == NULL)\r
+       goto error;\r
+    sfree(b);                         /* we don't care */\r
+\r
+    /* Read the Comment header line. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))\r
+       goto error;\r
+    if ((comment = read_body(fp)) == NULL)\r
+       goto error;\r
+\r
+    if (commentptr)\r
+       *commentptr = comment;\r
+    else\r
+       sfree(comment);\r
+\r
+    /* Read the Public-Lines header line and the public blob. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))\r
+       goto error;\r
+    if ((b = read_body(fp)) == NULL)\r
+       goto error;\r
+    i = atoi(b);\r
+    sfree(b);\r
+    if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)\r
+       goto error;\r
+\r
+    fclose(fp);\r
+    if (pub_blob_len)\r
+       *pub_blob_len = public_blob_len;\r
+    if (algorithm)\r
+       *algorithm = alg->name;\r
+    return public_blob;\r
+\r
+    /*\r
+     * Error processing.\r
+     */\r
+  error:\r
+    if (fp)\r
+       fclose(fp);\r
+    if (public_blob)\r
+       sfree(public_blob);\r
+    if (errorstr)\r
+       *errorstr = error;\r
+    return NULL;\r
+}\r
+\r
+int ssh2_userkey_encrypted(const Filename *filename, char **commentptr)\r
+{\r
+    FILE *fp;\r
+    char header[40], *b, *comment;\r
+    int ret;\r
+\r
+    if (commentptr)\r
+       *commentptr = NULL;\r
+\r
+    fp = f_open(*filename, "rb", FALSE);\r
+    if (!fp)\r
+       return 0;\r
+    if (!read_header(fp, header)\r
+       || (0 != strcmp(header, "PuTTY-User-Key-File-2") &&\r
+           0 != strcmp(header, "PuTTY-User-Key-File-1"))) {\r
+       fclose(fp);\r
+       return 0;\r
+    }\r
+    if ((b = read_body(fp)) == NULL) {\r
+       fclose(fp);\r
+       return 0;\r
+    }\r
+    sfree(b);                         /* we don't care about key type here */\r
+    /* Read the Encryption header line. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Encryption")) {\r
+       fclose(fp);\r
+       return 0;\r
+    }\r
+    if ((b = read_body(fp)) == NULL) {\r
+       fclose(fp);\r
+       return 0;\r
+    }\r
+\r
+    /* Read the Comment header line. */\r
+    if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) {\r
+       fclose(fp);\r
+       sfree(b);\r
+       return 1;\r
+    }\r
+    if ((comment = read_body(fp)) == NULL) {\r
+       fclose(fp);\r
+       sfree(b);\r
+       return 1;\r
+    }\r
+\r
+    if (commentptr)\r
+       *commentptr = comment;\r
+\r
+    fclose(fp);\r
+    if (!strcmp(b, "aes256-cbc"))\r
+       ret = 1;\r
+    else\r
+       ret = 0;\r
+    sfree(b);\r
+    return ret;\r
+}\r
+\r
+int base64_lines(int datalen)\r
+{\r
+    /* When encoding, we use 64 chars/line, which equals 48 real chars. */\r
+    return (datalen + 47) / 48;\r
+}\r
+\r
+void base64_encode(FILE * fp, unsigned char *data, int datalen, int cpl)\r
+{\r
+    int linelen = 0;\r
+    char out[4];\r
+    int n, i;\r
+\r
+    while (datalen > 0) {\r
+       n = (datalen < 3 ? datalen : 3);\r
+       base64_encode_atom(data, n, out);\r
+       data += n;\r
+       datalen -= n;\r
+       for (i = 0; i < 4; i++) {\r
+           if (linelen >= cpl) {\r
+               linelen = 0;\r
+               fputc('\n', fp);\r
+           }\r
+           fputc(out[i], fp);\r
+           linelen++;\r
+       }\r
+    }\r
+    fputc('\n', fp);\r
+}\r
+\r
+int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,\r
+                     char *passphrase)\r
+{\r
+    FILE *fp;\r
+    unsigned char *pub_blob, *priv_blob, *priv_blob_encrypted;\r
+    int pub_blob_len, priv_blob_len, priv_encrypted_len;\r
+    int passlen;\r
+    int cipherblk;\r
+    int i;\r
+    char *cipherstr;\r
+    unsigned char priv_mac[20];\r
+\r
+    /*\r
+     * Fetch the key component blobs.\r
+     */\r
+    pub_blob = key->alg->public_blob(key->data, &pub_blob_len);\r
+    priv_blob = key->alg->private_blob(key->data, &priv_blob_len);\r
+    if (!pub_blob || !priv_blob) {\r
+       sfree(pub_blob);\r
+       sfree(priv_blob);\r
+       return 0;\r
+    }\r
+\r
+    /*\r
+     * Determine encryption details, and encrypt the private blob.\r
+     */\r
+    if (passphrase) {\r
+       cipherstr = "aes256-cbc";\r
+       cipherblk = 16;\r
+    } else {\r
+       cipherstr = "none";\r
+       cipherblk = 1;\r
+    }\r
+    priv_encrypted_len = priv_blob_len + cipherblk - 1;\r
+    priv_encrypted_len -= priv_encrypted_len % cipherblk;\r
+    priv_blob_encrypted = snewn(priv_encrypted_len, unsigned char);\r
+    memset(priv_blob_encrypted, 0, priv_encrypted_len);\r
+    memcpy(priv_blob_encrypted, priv_blob, priv_blob_len);\r
+    /* Create padding based on the SHA hash of the unpadded blob. This prevents\r
+     * too easy a known-plaintext attack on the last block. */\r
+    SHA_Simple(priv_blob, priv_blob_len, priv_mac);\r
+    assert(priv_encrypted_len - priv_blob_len < 20);\r
+    memcpy(priv_blob_encrypted + priv_blob_len, priv_mac,\r
+          priv_encrypted_len - priv_blob_len);\r
+\r
+    /* Now create the MAC. */\r
+    {\r
+       unsigned char *macdata;\r
+       int maclen;\r
+       unsigned char *p;\r
+       int namelen = strlen(key->alg->name);\r
+       int enclen = strlen(cipherstr);\r
+       int commlen = strlen(key->comment);\r
+       SHA_State s;\r
+       unsigned char mackey[20];\r
+       char header[] = "putty-private-key-file-mac-key";\r
+\r
+       maclen = (4 + namelen +\r
+                 4 + enclen +\r
+                 4 + commlen +\r
+                 4 + pub_blob_len +\r
+                 4 + priv_encrypted_len);\r
+       macdata = snewn(maclen, unsigned char);\r
+       p = macdata;\r
+#define DO_STR(s,len) PUT_32BIT(p,(len));memcpy(p+4,(s),(len));p+=4+(len)\r
+       DO_STR(key->alg->name, namelen);\r
+       DO_STR(cipherstr, enclen);\r
+       DO_STR(key->comment, commlen);\r
+       DO_STR(pub_blob, pub_blob_len);\r
+       DO_STR(priv_blob_encrypted, priv_encrypted_len);\r
+\r
+       SHA_Init(&s);\r
+       SHA_Bytes(&s, header, sizeof(header)-1);\r
+       if (passphrase)\r
+           SHA_Bytes(&s, passphrase, strlen(passphrase));\r
+       SHA_Final(&s, mackey);\r
+       hmac_sha1_simple(mackey, 20, macdata, maclen, priv_mac);\r
+       memset(macdata, 0, maclen);\r
+       sfree(macdata);\r
+       memset(mackey, 0, sizeof(mackey));\r
+       memset(&s, 0, sizeof(s));\r
+    }\r
+\r
+    if (passphrase) {\r
+       unsigned char key[40];\r
+       SHA_State s;\r
+\r
+       passlen = strlen(passphrase);\r
+\r
+       SHA_Init(&s);\r
+       SHA_Bytes(&s, "\0\0\0\0", 4);\r
+       SHA_Bytes(&s, passphrase, passlen);\r
+       SHA_Final(&s, key + 0);\r
+       SHA_Init(&s);\r
+       SHA_Bytes(&s, "\0\0\0\1", 4);\r
+       SHA_Bytes(&s, passphrase, passlen);\r
+       SHA_Final(&s, key + 20);\r
+       aes256_encrypt_pubkey(key, priv_blob_encrypted,\r
+                             priv_encrypted_len);\r
+\r
+       memset(key, 0, sizeof(key));\r
+       memset(&s, 0, sizeof(s));\r
+    }\r
+\r
+    fp = f_open(*filename, "w", TRUE);\r
+    if (!fp)\r
+       return 0;\r
+    fprintf(fp, "PuTTY-User-Key-File-2: %s\n", key->alg->name);\r
+    fprintf(fp, "Encryption: %s\n", cipherstr);\r
+    fprintf(fp, "Comment: %s\n", key->comment);\r
+    fprintf(fp, "Public-Lines: %d\n", base64_lines(pub_blob_len));\r
+    base64_encode(fp, pub_blob, pub_blob_len, 64);\r
+    fprintf(fp, "Private-Lines: %d\n", base64_lines(priv_encrypted_len));\r
+    base64_encode(fp, priv_blob_encrypted, priv_encrypted_len, 64);\r
+    fprintf(fp, "Private-MAC: ");\r
+    for (i = 0; i < 20; i++)\r
+       fprintf(fp, "%02x", priv_mac[i]);\r
+    fprintf(fp, "\n");\r
+    fclose(fp);\r
+\r
+    sfree(pub_blob);\r
+    memset(priv_blob, 0, priv_blob_len);\r
+    sfree(priv_blob);\r
+    sfree(priv_blob_encrypted);\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * A function to determine the type of a private key file. Returns\r
+ * 0 on failure, 1 or 2 on success.\r
+ */\r
+int key_type(const Filename *filename)\r
+{\r
+    FILE *fp;\r
+    char buf[32];\r
+    const char putty2_sig[] = "PuTTY-User-Key-File-";\r
+    const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT";\r
+    const char openssh_sig[] = "-----BEGIN ";\r
+    int i;\r
+\r
+    fp = f_open(*filename, "r", FALSE);\r
+    if (!fp)\r
+       return SSH_KEYTYPE_UNOPENABLE;\r
+    i = fread(buf, 1, sizeof(buf), fp);\r
+    fclose(fp);\r
+    if (i < 0)\r
+       return SSH_KEYTYPE_UNOPENABLE;\r
+    if (i < 32)\r
+       return SSH_KEYTYPE_UNKNOWN;\r
+    if (!memcmp(buf, rsa_signature, sizeof(rsa_signature)-1))\r
+       return SSH_KEYTYPE_SSH1;\r
+    if (!memcmp(buf, putty2_sig, sizeof(putty2_sig)-1))\r
+       return SSH_KEYTYPE_SSH2;\r
+    if (!memcmp(buf, openssh_sig, sizeof(openssh_sig)-1))\r
+       return SSH_KEYTYPE_OPENSSH;\r
+    if (!memcmp(buf, sshcom_sig, sizeof(sshcom_sig)-1))\r
+       return SSH_KEYTYPE_SSHCOM;\r
+    return SSH_KEYTYPE_UNKNOWN;               /* unrecognised or EOF */\r
+}\r
+\r
+/*\r
+ * Convert the type word to a string, for `wrong type' error\r
+ * messages.\r
+ */\r
+char *key_type_to_str(int type)\r
+{\r
+    switch (type) {\r
+      case SSH_KEYTYPE_UNOPENABLE: return "unable to open file"; break;\r
+      case SSH_KEYTYPE_UNKNOWN: return "not a private key"; break;\r
+      case SSH_KEYTYPE_SSH1: return "SSH-1 private key"; break;\r
+      case SSH_KEYTYPE_SSH2: return "PuTTY SSH-2 private key"; break;\r
+      case SSH_KEYTYPE_OPENSSH: return "OpenSSH SSH-2 private key"; break;\r
+      case SSH_KEYTYPE_SSHCOM: return "ssh.com SSH-2 private key"; break;\r
+      default: return "INTERNAL ERROR"; break;\r
+    }\r
+}\r
diff --git a/putty/SSHRAND.C b/putty/SSHRAND.C
new file mode 100644 (file)
index 0000000..91d9b37
--- /dev/null
@@ -0,0 +1,251 @@
+/*\r
+ * cryptographic random number generator for PuTTY's ssh client\r
+ */\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include <assert.h>\r
+\r
+/* Collect environmental noise every 5 minutes */\r
+#define NOISE_REGULAR_INTERVAL (5*60*TICKSPERSEC)\r
+\r
+void noise_get_heavy(void (*func) (void *, int));\r
+void noise_get_light(void (*func) (void *, int));\r
+\r
+/*\r
+ * `pool' itself is a pool of random data which we actually use: we\r
+ * return bytes from `pool', at position `poolpos', until `poolpos'\r
+ * reaches the end of the pool. At this point we generate more\r
+ * random data, by adding noise, stirring well, and resetting\r
+ * `poolpos' to point to just past the beginning of the pool (not\r
+ * _the_ beginning, since otherwise we'd give away the whole\r
+ * contents of our pool, and attackers would just have to guess the\r
+ * next lot of noise).\r
+ *\r
+ * `incomingb' buffers acquired noise data, until it gets full, at\r
+ * which point the acquired noise is SHA'ed into `incoming' and\r
+ * `incomingb' is cleared. The noise in `incoming' is used as part\r
+ * of the noise for each stirring of the pool, in addition to local\r
+ * time, process listings, and other such stuff.\r
+ */\r
+\r
+#define HASHINPUT 64                  /* 64 bytes SHA input */\r
+#define HASHSIZE 20                   /* 160 bits SHA output */\r
+#define POOLSIZE 1200                 /* size of random pool */\r
+\r
+struct RandPool {\r
+    unsigned char pool[POOLSIZE];\r
+    int poolpos;\r
+\r
+    unsigned char incoming[HASHSIZE];\r
+\r
+    unsigned char incomingb[HASHINPUT];\r
+    int incomingpos;\r
+\r
+    int stir_pending;\r
+};\r
+\r
+static struct RandPool pool;\r
+int random_active = 0;\r
+long next_noise_collection;\r
+\r
+static void random_stir(void)\r
+{\r
+    word32 block[HASHINPUT / sizeof(word32)];\r
+    word32 digest[HASHSIZE / sizeof(word32)];\r
+    int i, j, k;\r
+\r
+    /*\r
+     * noise_get_light will call random_add_noise, which may call\r
+     * back to here. Prevent recursive stirs.\r
+     */\r
+    if (pool.stir_pending)\r
+       return;\r
+    pool.stir_pending = TRUE;\r
+\r
+    noise_get_light(random_add_noise);\r
+\r
+    SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb);\r
+    pool.incomingpos = 0;\r
+\r
+    /*\r
+     * Chunks of this code are blatantly endianness-dependent, but\r
+     * as it's all random bits anyway, WHO CARES?\r
+     */\r
+    memcpy(digest, pool.incoming, sizeof(digest));\r
+\r
+    /*\r
+     * Make two passes over the pool.\r
+     */\r
+    for (i = 0; i < 2; i++) {\r
+\r
+       /*\r
+        * We operate SHA in CFB mode, repeatedly adding the same\r
+        * block of data to the digest. But we're also fiddling\r
+        * with the digest-so-far, so this shouldn't be Bad or\r
+        * anything.\r
+        */\r
+       memcpy(block, pool.pool, sizeof(block));\r
+\r
+       /*\r
+        * Each pass processes the pool backwards in blocks of\r
+        * HASHSIZE, just so that in general we get the output of\r
+        * SHA before the corresponding input, in the hope that\r
+        * things will be that much less predictable that way\r
+        * round, when we subsequently return bytes ...\r
+        */\r
+       for (j = POOLSIZE; (j -= HASHSIZE) >= 0;) {\r
+           /*\r
+            * XOR the bit of the pool we're processing into the\r
+            * digest.\r
+            */\r
+\r
+           for (k = 0; k < sizeof(digest) / sizeof(*digest); k++)\r
+               digest[k] ^= ((word32 *) (pool.pool + j))[k];\r
+\r
+           /*\r
+            * Munge our unrevealed first block of the pool into\r
+            * it.\r
+            */\r
+           SHATransform(digest, block);\r
+\r
+           /*\r
+            * Stick the result back into the pool.\r
+            */\r
+\r
+           for (k = 0; k < sizeof(digest) / sizeof(*digest); k++)\r
+               ((word32 *) (pool.pool + j))[k] = digest[k];\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Might as well save this value back into `incoming', just so\r
+     * there'll be some extra bizarreness there.\r
+     */\r
+    SHATransform(digest, block);\r
+    memcpy(pool.incoming, digest, sizeof(digest));\r
+\r
+    pool.poolpos = sizeof(pool.incoming);\r
+\r
+    pool.stir_pending = FALSE;\r
+}\r
+\r
+void random_add_noise(void *noise, int length)\r
+{\r
+    unsigned char *p = noise;\r
+    int i;\r
+\r
+    if (!random_active)\r
+       return;\r
+\r
+    /*\r
+     * This function processes HASHINPUT bytes into only HASHSIZE\r
+     * bytes, so _if_ we were getting incredibly high entropy\r
+     * sources then we would be throwing away valuable stuff.\r
+     */\r
+    while (length >= (HASHINPUT - pool.incomingpos)) {\r
+       memcpy(pool.incomingb + pool.incomingpos, p,\r
+              HASHINPUT - pool.incomingpos);\r
+       p += HASHINPUT - pool.incomingpos;\r
+       length -= HASHINPUT - pool.incomingpos;\r
+       SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb);\r
+       for (i = 0; i < HASHSIZE; i++) {\r
+           pool.pool[pool.poolpos++] ^= pool.incomingb[i];\r
+           if (pool.poolpos >= POOLSIZE)\r
+               pool.poolpos = 0;\r
+       }\r
+       if (pool.poolpos < HASHSIZE)\r
+           random_stir();\r
+\r
+       pool.incomingpos = 0;\r
+    }\r
+\r
+    memcpy(pool.incomingb + pool.incomingpos, p, length);\r
+    pool.incomingpos += length;\r
+}\r
+\r
+void random_add_heavynoise(void *noise, int length)\r
+{\r
+    unsigned char *p = noise;\r
+    int i;\r
+\r
+    while (length >= POOLSIZE) {\r
+       for (i = 0; i < POOLSIZE; i++)\r
+           pool.pool[i] ^= *p++;\r
+       random_stir();\r
+       length -= POOLSIZE;\r
+    }\r
+\r
+    for (i = 0; i < length; i++)\r
+       pool.pool[i] ^= *p++;\r
+    random_stir();\r
+}\r
+\r
+static void random_add_heavynoise_bitbybit(void *noise, int length)\r
+{\r
+    unsigned char *p = noise;\r
+    int i;\r
+\r
+    while (length >= POOLSIZE - pool.poolpos) {\r
+       for (i = 0; i < POOLSIZE - pool.poolpos; i++)\r
+           pool.pool[pool.poolpos + i] ^= *p++;\r
+       random_stir();\r
+       length -= POOLSIZE - pool.poolpos;\r
+       pool.poolpos = 0;\r
+    }\r
+\r
+    for (i = 0; i < length; i++)\r
+       pool.pool[i] ^= *p++;\r
+    pool.poolpos = i;\r
+}\r
+\r
+static void random_timer(void *ctx, long now)\r
+{\r
+    if (random_active > 0 && now - next_noise_collection >= 0) {\r
+       noise_regular();\r
+       next_noise_collection =\r
+           schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool);\r
+    }\r
+}\r
+\r
+void random_ref(void)\r
+{\r
+    if (!random_active) {\r
+       memset(&pool, 0, sizeof(pool));    /* just to start with */\r
+\r
+       noise_get_heavy(random_add_heavynoise_bitbybit);\r
+       random_stir();\r
+\r
+       next_noise_collection =\r
+           schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool);\r
+    }\r
+\r
+    random_active++;\r
+}\r
+\r
+void random_unref(void)\r
+{\r
+    random_active--;\r
+    assert(random_active >= 0);\r
+    if (random_active) return;\r
+\r
+    expire_timer_context(&pool);\r
+}\r
+\r
+int random_byte(void)\r
+{\r
+    if (pool.poolpos >= POOLSIZE)\r
+       random_stir();\r
+\r
+    return pool.pool[pool.poolpos++];\r
+}\r
+\r
+void random_get_savedata(void **data, int *len)\r
+{\r
+    void *buf = snewn(POOLSIZE / 2, char);\r
+    random_stir();\r
+    memcpy(buf, pool.pool + pool.poolpos, POOLSIZE / 2);\r
+    *len = POOLSIZE / 2;\r
+    *data = buf;\r
+    random_stir();\r
+}\r
diff --git a/putty/SSHRSA.C b/putty/SSHRSA.C
new file mode 100644 (file)
index 0000000..ea6440b
--- /dev/null
@@ -0,0 +1,1086 @@
+/*\r
+ * RSA implementation for PuTTY.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <assert.h>\r
+\r
+#include "ssh.h"\r
+#include "misc.h"\r
+\r
+int makekey(unsigned char *data, int len, struct RSAKey *result,\r
+           unsigned char **keystr, int order)\r
+{\r
+    unsigned char *p = data;\r
+    int i, n;\r
+\r
+    if (len < 4)\r
+       return -1;\r
+\r
+    if (result) {\r
+       result->bits = 0;\r
+       for (i = 0; i < 4; i++)\r
+           result->bits = (result->bits << 8) + *p++;\r
+    } else\r
+       p += 4;\r
+\r
+    len -= 4;\r
+\r
+    /*\r
+     * order=0 means exponent then modulus (the keys sent by the\r
+     * server). order=1 means modulus then exponent (the keys\r
+     * stored in a keyfile).\r
+     */\r
+\r
+    if (order == 0) {\r
+       n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL);\r
+       if (n < 0) return -1;\r
+       p += n;\r
+       len -= n;\r
+    }\r
+\r
+    n = ssh1_read_bignum(p, len, result ? &result->modulus : NULL);\r
+    if (n < 0 || (result && bignum_bitcount(result->modulus) == 0)) return -1;\r
+    if (result)\r
+       result->bytes = n - 2;\r
+    if (keystr)\r
+       *keystr = p + 2;\r
+    p += n;\r
+    len -= n;\r
+\r
+    if (order == 1) {\r
+       n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL);\r
+       if (n < 0) return -1;\r
+       p += n;\r
+       len -= n;\r
+    }\r
+    return p - data;\r
+}\r
+\r
+int makeprivate(unsigned char *data, int len, struct RSAKey *result)\r
+{\r
+    return ssh1_read_bignum(data, len, &result->private_exponent);\r
+}\r
+\r
+int rsaencrypt(unsigned char *data, int length, struct RSAKey *key)\r
+{\r
+    Bignum b1, b2;\r
+    int i;\r
+    unsigned char *p;\r
+\r
+    if (key->bytes < length + 4)\r
+       return 0;                      /* RSA key too short! */\r
+\r
+    memmove(data + key->bytes - length, data, length);\r
+    data[0] = 0;\r
+    data[1] = 2;\r
+\r
+    for (i = 2; i < key->bytes - length - 1; i++) {\r
+       do {\r
+           data[i] = random_byte();\r
+       } while (data[i] == 0);\r
+    }\r
+    data[key->bytes - length - 1] = 0;\r
+\r
+    b1 = bignum_from_bytes(data, key->bytes);\r
+\r
+    b2 = modpow(b1, key->exponent, key->modulus);\r
+\r
+    p = data;\r
+    for (i = key->bytes; i--;) {\r
+       *p++ = bignum_byte(b2, i);\r
+    }\r
+\r
+    freebn(b1);\r
+    freebn(b2);\r
+\r
+    return 1;\r
+}\r
+\r
+static void sha512_mpint(SHA512_State * s, Bignum b)\r
+{\r
+    unsigned char lenbuf[4];\r
+    int len;\r
+    len = (bignum_bitcount(b) + 8) / 8;\r
+    PUT_32BIT(lenbuf, len);\r
+    SHA512_Bytes(s, lenbuf, 4);\r
+    while (len-- > 0) {\r
+       lenbuf[0] = bignum_byte(b, len);\r
+       SHA512_Bytes(s, lenbuf, 1);\r
+    }\r
+    memset(lenbuf, 0, sizeof(lenbuf));\r
+}\r
+\r
+/*\r
+ * Compute (base ^ exp) % mod, provided mod == p * q, with p,q\r
+ * distinct primes, and iqmp is the multiplicative inverse of q mod p.\r
+ * Uses Chinese Remainder Theorem to speed computation up over the\r
+ * obvious implementation of a single big modpow.\r
+ */\r
+Bignum crt_modpow(Bignum base, Bignum exp, Bignum mod,\r
+                  Bignum p, Bignum q, Bignum iqmp)\r
+{\r
+    Bignum pm1, qm1, pexp, qexp, presult, qresult, diff, multiplier, ret0, ret;\r
+\r
+    /*\r
+     * Reduce the exponent mod phi(p) and phi(q), to save time when\r
+     * exponentiating mod p and mod q respectively. Of course, since p\r
+     * and q are prime, phi(p) == p-1 and similarly for q.\r
+     */\r
+    pm1 = copybn(p);\r
+    decbn(pm1);\r
+    qm1 = copybn(q);\r
+    decbn(qm1);\r
+    pexp = bigmod(exp, pm1);\r
+    qexp = bigmod(exp, qm1);\r
+\r
+    /*\r
+     * Do the two modpows.\r
+     */\r
+    presult = modpow(base, pexp, p);\r
+    qresult = modpow(base, qexp, q);\r
+\r
+    /*\r
+     * Recombine the results. We want a value which is congruent to\r
+     * qresult mod q, and to presult mod p.\r
+     *\r
+     * We know that iqmp * q is congruent to 1 * mod p (by definition\r
+     * of iqmp) and to 0 mod q (obviously). So we start with qresult\r
+     * (which is congruent to qresult mod both primes), and add on\r
+     * (presult-qresult) * (iqmp * q) which adjusts it to be congruent\r
+     * to presult mod p without affecting its value mod q.\r
+     */\r
+    if (bignum_cmp(presult, qresult) < 0) {\r
+        /*\r
+         * Can't subtract presult from qresult without first adding on\r
+         * p.\r
+         */\r
+        Bignum tmp = presult;\r
+        presult = bigadd(presult, p);\r
+        freebn(tmp);\r
+    }\r
+    diff = bigsub(presult, qresult);\r
+    multiplier = bigmul(iqmp, q);\r
+    ret0 = bigmuladd(multiplier, diff, qresult);\r
+\r
+    /*\r
+     * Finally, reduce the result mod n.\r
+     */\r
+    ret = bigmod(ret0, mod);\r
+\r
+    /*\r
+     * Free all the intermediate results before returning.\r
+     */\r
+    freebn(pm1);\r
+    freebn(qm1);\r
+    freebn(pexp);\r
+    freebn(qexp);\r
+    freebn(presult);\r
+    freebn(qresult);\r
+    freebn(diff);\r
+    freebn(multiplier);\r
+    freebn(ret0);\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * This function is a wrapper on modpow(). It has the same effect as\r
+ * modpow(), but employs RSA blinding to protect against timing\r
+ * attacks and also uses the Chinese Remainder Theorem (implemented\r
+ * above, in crt_modpow()) to speed up the main operation.\r
+ */\r
+static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key)\r
+{\r
+    Bignum random, random_encrypted, random_inverse;\r
+    Bignum input_blinded, ret_blinded;\r
+    Bignum ret;\r
+\r
+    SHA512_State ss;\r
+    unsigned char digest512[64];\r
+    int digestused = lenof(digest512);\r
+    int hashseq = 0;\r
+\r
+    /*\r
+     * Start by inventing a random number chosen uniformly from the\r
+     * range 2..modulus-1. (We do this by preparing a random number\r
+     * of the right length and retrying if it's greater than the\r
+     * modulus, to prevent any potential Bleichenbacher-like\r
+     * attacks making use of the uneven distribution within the\r
+     * range that would arise from just reducing our number mod n.\r
+     * There are timing implications to the potential retries, of\r
+     * course, but all they tell you is the modulus, which you\r
+     * already knew.)\r
+     * \r
+     * To preserve determinism and avoid Pageant needing to share\r
+     * the random number pool, we actually generate this `random'\r
+     * number by hashing stuff with the private key.\r
+     */\r
+    while (1) {\r
+       int bits, byte, bitsleft, v;\r
+       random = copybn(key->modulus);\r
+       /*\r
+        * Find the topmost set bit. (This function will return its\r
+        * index plus one.) Then we'll set all bits from that one\r
+        * downwards randomly.\r
+        */\r
+       bits = bignum_bitcount(random);\r
+       byte = 0;\r
+       bitsleft = 0;\r
+       while (bits--) {\r
+           if (bitsleft <= 0) {\r
+               bitsleft = 8;\r
+               /*\r
+                * Conceptually the following few lines are equivalent to\r
+                *    byte = random_byte();\r
+                */\r
+               if (digestused >= lenof(digest512)) {\r
+                   unsigned char seqbuf[4];\r
+                   PUT_32BIT(seqbuf, hashseq);\r
+                   SHA512_Init(&ss);\r
+                   SHA512_Bytes(&ss, "RSA deterministic blinding", 26);\r
+                   SHA512_Bytes(&ss, seqbuf, sizeof(seqbuf));\r
+                   sha512_mpint(&ss, key->private_exponent);\r
+                   SHA512_Final(&ss, digest512);\r
+                   hashseq++;\r
+\r
+                   /*\r
+                    * Now hash that digest plus the signature\r
+                    * input.\r
+                    */\r
+                   SHA512_Init(&ss);\r
+                   SHA512_Bytes(&ss, digest512, sizeof(digest512));\r
+                   sha512_mpint(&ss, input);\r
+                   SHA512_Final(&ss, digest512);\r
+\r
+                   digestused = 0;\r
+               }\r
+               byte = digest512[digestused++];\r
+           }\r
+           v = byte & 1;\r
+           byte >>= 1;\r
+           bitsleft--;\r
+           bignum_set_bit(random, bits, v);\r
+       }\r
+\r
+       /*\r
+        * Now check that this number is strictly greater than\r
+        * zero, and strictly less than modulus.\r
+        */\r
+       if (bignum_cmp(random, Zero) <= 0 ||\r
+           bignum_cmp(random, key->modulus) >= 0) {\r
+           freebn(random);\r
+           continue;\r
+       } else {\r
+           break;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * RSA blinding relies on the fact that (xy)^d mod n is equal\r
+     * to (x^d mod n) * (y^d mod n) mod n. We invent a random pair\r
+     * y and y^d; then we multiply x by y, raise to the power d mod\r
+     * n as usual, and divide by y^d to recover x^d. Thus an\r
+     * attacker can't correlate the timing of the modpow with the\r
+     * input, because they don't know anything about the number\r
+     * that was input to the actual modpow.\r
+     * \r
+     * The clever bit is that we don't have to do a huge modpow to\r
+     * get y and y^d; we will use the number we just invented as\r
+     * _y^d_, and use the _public_ exponent to compute (y^d)^e = y\r
+     * from it, which is much faster to do.\r
+     */\r
+    random_encrypted = crt_modpow(random, key->exponent,\r
+                                  key->modulus, key->p, key->q, key->iqmp);\r
+    random_inverse = modinv(random, key->modulus);\r
+    input_blinded = modmul(input, random_encrypted, key->modulus);\r
+    ret_blinded = crt_modpow(input_blinded, key->private_exponent,\r
+                             key->modulus, key->p, key->q, key->iqmp);\r
+    ret = modmul(ret_blinded, random_inverse, key->modulus);\r
+\r
+    freebn(ret_blinded);\r
+    freebn(input_blinded);\r
+    freebn(random_inverse);\r
+    freebn(random_encrypted);\r
+    freebn(random);\r
+\r
+    return ret;\r
+}\r
+\r
+Bignum rsadecrypt(Bignum input, struct RSAKey *key)\r
+{\r
+    return rsa_privkey_op(input, key);\r
+}\r
+\r
+int rsastr_len(struct RSAKey *key)\r
+{\r
+    Bignum md, ex;\r
+    int mdlen, exlen;\r
+\r
+    md = key->modulus;\r
+    ex = key->exponent;\r
+    mdlen = (bignum_bitcount(md) + 15) / 16;\r
+    exlen = (bignum_bitcount(ex) + 15) / 16;\r
+    return 4 * (mdlen + exlen) + 20;\r
+}\r
+\r
+void rsastr_fmt(char *str, struct RSAKey *key)\r
+{\r
+    Bignum md, ex;\r
+    int len = 0, i, nibbles;\r
+    static const char hex[] = "0123456789abcdef";\r
+\r
+    md = key->modulus;\r
+    ex = key->exponent;\r
+\r
+    len += sprintf(str + len, "0x");\r
+\r
+    nibbles = (3 + bignum_bitcount(ex)) / 4;\r
+    if (nibbles < 1)\r
+       nibbles = 1;\r
+    for (i = nibbles; i--;)\r
+       str[len++] = hex[(bignum_byte(ex, i / 2) >> (4 * (i % 2))) & 0xF];\r
+\r
+    len += sprintf(str + len, ",0x");\r
+\r
+    nibbles = (3 + bignum_bitcount(md)) / 4;\r
+    if (nibbles < 1)\r
+       nibbles = 1;\r
+    for (i = nibbles; i--;)\r
+       str[len++] = hex[(bignum_byte(md, i / 2) >> (4 * (i % 2))) & 0xF];\r
+\r
+    str[len] = '\0';\r
+}\r
+\r
+/*\r
+ * Generate a fingerprint string for the key. Compatible with the\r
+ * OpenSSH fingerprint code.\r
+ */\r
+void rsa_fingerprint(char *str, int len, struct RSAKey *key)\r
+{\r
+    struct MD5Context md5c;\r
+    unsigned char digest[16];\r
+    char buffer[16 * 3 + 40];\r
+    int numlen, slen, i;\r
+\r
+    MD5Init(&md5c);\r
+    numlen = ssh1_bignum_length(key->modulus) - 2;\r
+    for (i = numlen; i--;) {\r
+       unsigned char c = bignum_byte(key->modulus, i);\r
+       MD5Update(&md5c, &c, 1);\r
+    }\r
+    numlen = ssh1_bignum_length(key->exponent) - 2;\r
+    for (i = numlen; i--;) {\r
+       unsigned char c = bignum_byte(key->exponent, i);\r
+       MD5Update(&md5c, &c, 1);\r
+    }\r
+    MD5Final(digest, &md5c);\r
+\r
+    sprintf(buffer, "%d ", bignum_bitcount(key->modulus));\r
+    for (i = 0; i < 16; i++)\r
+       sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",\r
+               digest[i]);\r
+    strncpy(str, buffer, len);\r
+    str[len - 1] = '\0';\r
+    slen = strlen(str);\r
+    if (key->comment && slen < len - 1) {\r
+       str[slen] = ' ';\r
+       strncpy(str + slen + 1, key->comment, len - slen - 1);\r
+       str[len - 1] = '\0';\r
+    }\r
+}\r
+\r
+/*\r
+ * Verify that the public data in an RSA key matches the private\r
+ * data. We also check the private data itself: we ensure that p >\r
+ * q and that iqmp really is the inverse of q mod p.\r
+ */\r
+int rsa_verify(struct RSAKey *key)\r
+{\r
+    Bignum n, ed, pm1, qm1;\r
+    int cmp;\r
+\r
+    /* n must equal pq. */\r
+    n = bigmul(key->p, key->q);\r
+    cmp = bignum_cmp(n, key->modulus);\r
+    freebn(n);\r
+    if (cmp != 0)\r
+       return 0;\r
+\r
+    /* e * d must be congruent to 1, modulo (p-1) and modulo (q-1). */\r
+    pm1 = copybn(key->p);\r
+    decbn(pm1);\r
+    ed = modmul(key->exponent, key->private_exponent, pm1);\r
+    cmp = bignum_cmp(ed, One);\r
+    sfree(ed);\r
+    if (cmp != 0)\r
+       return 0;\r
+\r
+    qm1 = copybn(key->q);\r
+    decbn(qm1);\r
+    ed = modmul(key->exponent, key->private_exponent, qm1);\r
+    cmp = bignum_cmp(ed, One);\r
+    sfree(ed);\r
+    if (cmp != 0)\r
+       return 0;\r
+\r
+    /*\r
+     * Ensure p > q.\r
+     *\r
+     * I have seen key blobs in the wild which were generated with\r
+     * p < q, so instead of rejecting the key in this case we\r
+     * should instead flip them round into the canonical order of\r
+     * p > q. This also involves regenerating iqmp.\r
+     */\r
+    if (bignum_cmp(key->p, key->q) <= 0) {\r
+       Bignum tmp = key->p;\r
+       key->p = key->q;\r
+       key->q = tmp;\r
+\r
+       freebn(key->iqmp);\r
+       key->iqmp = modinv(key->q, key->p);\r
+    }\r
+\r
+    /*\r
+     * Ensure iqmp * q is congruent to 1, modulo p.\r
+     */\r
+    n = modmul(key->iqmp, key->q, key->p);\r
+    cmp = bignum_cmp(n, One);\r
+    sfree(n);\r
+    if (cmp != 0)\r
+       return 0;\r
+\r
+    return 1;\r
+}\r
+\r
+/* Public key blob as used by Pageant: exponent before modulus. */\r
+unsigned char *rsa_public_blob(struct RSAKey *key, int *len)\r
+{\r
+    int length, pos;\r
+    unsigned char *ret;\r
+\r
+    length = (ssh1_bignum_length(key->modulus) +\r
+             ssh1_bignum_length(key->exponent) + 4);\r
+    ret = snewn(length, unsigned char);\r
+\r
+    PUT_32BIT(ret, bignum_bitcount(key->modulus));\r
+    pos = 4;\r
+    pos += ssh1_write_bignum(ret + pos, key->exponent);\r
+    pos += ssh1_write_bignum(ret + pos, key->modulus);\r
+\r
+    *len = length;\r
+    return ret;\r
+}\r
+\r
+/* Given a public blob, determine its length. */\r
+int rsa_public_blob_len(void *data, int maxlen)\r
+{\r
+    unsigned char *p = (unsigned char *)data;\r
+    int n;\r
+\r
+    if (maxlen < 4)\r
+       return -1;\r
+    p += 4;                           /* length word */\r
+    maxlen -= 4;\r
+\r
+    n = ssh1_read_bignum(p, maxlen, NULL);    /* exponent */\r
+    if (n < 0)\r
+       return -1;\r
+    p += n;\r
+\r
+    n = ssh1_read_bignum(p, maxlen, NULL);    /* modulus */\r
+    if (n < 0)\r
+       return -1;\r
+    p += n;\r
+\r
+    return p - (unsigned char *)data;\r
+}\r
+\r
+void freersakey(struct RSAKey *key)\r
+{\r
+    if (key->modulus)\r
+       freebn(key->modulus);\r
+    if (key->exponent)\r
+       freebn(key->exponent);\r
+    if (key->private_exponent)\r
+       freebn(key->private_exponent);\r
+    if (key->p)\r
+       freebn(key->p);\r
+    if (key->q)\r
+       freebn(key->q);\r
+    if (key->iqmp)\r
+       freebn(key->iqmp);\r
+    if (key->comment)\r
+       sfree(key->comment);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Implementation of the ssh-rsa signing key type. \r
+ */\r
+\r
+static void getstring(char **data, int *datalen, char **p, int *length)\r
+{\r
+    *p = NULL;\r
+    if (*datalen < 4)\r
+       return;\r
+    *length = GET_32BIT(*data);\r
+    *datalen -= 4;\r
+    *data += 4;\r
+    if (*datalen < *length)\r
+       return;\r
+    *p = *data;\r
+    *data += *length;\r
+    *datalen -= *length;\r
+}\r
+static Bignum getmp(char **data, int *datalen)\r
+{\r
+    char *p;\r
+    int length;\r
+    Bignum b;\r
+\r
+    getstring(data, datalen, &p, &length);\r
+    if (!p)\r
+       return NULL;\r
+    b = bignum_from_bytes((unsigned char *)p, length);\r
+    return b;\r
+}\r
+\r
+static void *rsa2_newkey(char *data, int len)\r
+{\r
+    char *p;\r
+    int slen;\r
+    struct RSAKey *rsa;\r
+\r
+    rsa = snew(struct RSAKey);\r
+    if (!rsa)\r
+       return NULL;\r
+    getstring(&data, &len, &p, &slen);\r
+\r
+    if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) {\r
+       sfree(rsa);\r
+       return NULL;\r
+    }\r
+    rsa->exponent = getmp(&data, &len);\r
+    rsa->modulus = getmp(&data, &len);\r
+    rsa->private_exponent = NULL;\r
+    rsa->p = rsa->q = rsa->iqmp = NULL;\r
+    rsa->comment = NULL;\r
+\r
+    return rsa;\r
+}\r
+\r
+static void rsa2_freekey(void *key)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    freersakey(rsa);\r
+    sfree(rsa);\r
+}\r
+\r
+static char *rsa2_fmtkey(void *key)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    char *p;\r
+    int len;\r
+\r
+    len = rsastr_len(rsa);\r
+    p = snewn(len, char);\r
+    rsastr_fmt(p, rsa);\r
+    return p;\r
+}\r
+\r
+static unsigned char *rsa2_public_blob(void *key, int *len)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    int elen, mlen, bloblen;\r
+    int i;\r
+    unsigned char *blob, *p;\r
+\r
+    elen = (bignum_bitcount(rsa->exponent) + 8) / 8;\r
+    mlen = (bignum_bitcount(rsa->modulus) + 8) / 8;\r
+\r
+    /*\r
+     * string "ssh-rsa", mpint exp, mpint mod. Total 19+elen+mlen.\r
+     * (three length fields, 12+7=19).\r
+     */\r
+    bloblen = 19 + elen + mlen;\r
+    blob = snewn(bloblen, unsigned char);\r
+    p = blob;\r
+    PUT_32BIT(p, 7);\r
+    p += 4;\r
+    memcpy(p, "ssh-rsa", 7);\r
+    p += 7;\r
+    PUT_32BIT(p, elen);\r
+    p += 4;\r
+    for (i = elen; i--;)\r
+       *p++ = bignum_byte(rsa->exponent, i);\r
+    PUT_32BIT(p, mlen);\r
+    p += 4;\r
+    for (i = mlen; i--;)\r
+       *p++ = bignum_byte(rsa->modulus, i);\r
+    assert(p == blob + bloblen);\r
+    *len = bloblen;\r
+    return blob;\r
+}\r
+\r
+static unsigned char *rsa2_private_blob(void *key, int *len)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    int dlen, plen, qlen, ulen, bloblen;\r
+    int i;\r
+    unsigned char *blob, *p;\r
+\r
+    dlen = (bignum_bitcount(rsa->private_exponent) + 8) / 8;\r
+    plen = (bignum_bitcount(rsa->p) + 8) / 8;\r
+    qlen = (bignum_bitcount(rsa->q) + 8) / 8;\r
+    ulen = (bignum_bitcount(rsa->iqmp) + 8) / 8;\r
+\r
+    /*\r
+     * mpint private_exp, mpint p, mpint q, mpint iqmp. Total 16 +\r
+     * sum of lengths.\r
+     */\r
+    bloblen = 16 + dlen + plen + qlen + ulen;\r
+    blob = snewn(bloblen, unsigned char);\r
+    p = blob;\r
+    PUT_32BIT(p, dlen);\r
+    p += 4;\r
+    for (i = dlen; i--;)\r
+       *p++ = bignum_byte(rsa->private_exponent, i);\r
+    PUT_32BIT(p, plen);\r
+    p += 4;\r
+    for (i = plen; i--;)\r
+       *p++ = bignum_byte(rsa->p, i);\r
+    PUT_32BIT(p, qlen);\r
+    p += 4;\r
+    for (i = qlen; i--;)\r
+       *p++ = bignum_byte(rsa->q, i);\r
+    PUT_32BIT(p, ulen);\r
+    p += 4;\r
+    for (i = ulen; i--;)\r
+       *p++ = bignum_byte(rsa->iqmp, i);\r
+    assert(p == blob + bloblen);\r
+    *len = bloblen;\r
+    return blob;\r
+}\r
+\r
+static void *rsa2_createkey(unsigned char *pub_blob, int pub_len,\r
+                           unsigned char *priv_blob, int priv_len)\r
+{\r
+    struct RSAKey *rsa;\r
+    char *pb = (char *) priv_blob;\r
+\r
+    rsa = rsa2_newkey((char *) pub_blob, pub_len);\r
+    rsa->private_exponent = getmp(&pb, &priv_len);\r
+    rsa->p = getmp(&pb, &priv_len);\r
+    rsa->q = getmp(&pb, &priv_len);\r
+    rsa->iqmp = getmp(&pb, &priv_len);\r
+\r
+    if (!rsa_verify(rsa)) {\r
+       rsa2_freekey(rsa);\r
+       return NULL;\r
+    }\r
+\r
+    return rsa;\r
+}\r
+\r
+static void *rsa2_openssh_createkey(unsigned char **blob, int *len)\r
+{\r
+    char **b = (char **) blob;\r
+    struct RSAKey *rsa;\r
+\r
+    rsa = snew(struct RSAKey);\r
+    if (!rsa)\r
+       return NULL;\r
+    rsa->comment = NULL;\r
+\r
+    rsa->modulus = getmp(b, len);\r
+    rsa->exponent = getmp(b, len);\r
+    rsa->private_exponent = getmp(b, len);\r
+    rsa->iqmp = getmp(b, len);\r
+    rsa->p = getmp(b, len);\r
+    rsa->q = getmp(b, len);\r
+\r
+    if (!rsa->modulus || !rsa->exponent || !rsa->private_exponent ||\r
+       !rsa->iqmp || !rsa->p || !rsa->q) {\r
+       sfree(rsa->modulus);\r
+       sfree(rsa->exponent);\r
+       sfree(rsa->private_exponent);\r
+       sfree(rsa->iqmp);\r
+       sfree(rsa->p);\r
+       sfree(rsa->q);\r
+       sfree(rsa);\r
+       return NULL;\r
+    }\r
+\r
+    return rsa;\r
+}\r
+\r
+static int rsa2_openssh_fmtkey(void *key, unsigned char *blob, int len)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    int bloblen, i;\r
+\r
+    bloblen =\r
+       ssh2_bignum_length(rsa->modulus) +\r
+       ssh2_bignum_length(rsa->exponent) +\r
+       ssh2_bignum_length(rsa->private_exponent) +\r
+       ssh2_bignum_length(rsa->iqmp) +\r
+       ssh2_bignum_length(rsa->p) + ssh2_bignum_length(rsa->q);\r
+\r
+    if (bloblen > len)\r
+       return bloblen;\r
+\r
+    bloblen = 0;\r
+#define ENC(x) \\r
+    PUT_32BIT(blob+bloblen, ssh2_bignum_length((x))-4); bloblen += 4; \\r
+    for (i = ssh2_bignum_length((x))-4; i-- ;) blob[bloblen++]=bignum_byte((x),i);\r
+    ENC(rsa->modulus);\r
+    ENC(rsa->exponent);\r
+    ENC(rsa->private_exponent);\r
+    ENC(rsa->iqmp);\r
+    ENC(rsa->p);\r
+    ENC(rsa->q);\r
+\r
+    return bloblen;\r
+}\r
+\r
+static int rsa2_pubkey_bits(void *blob, int len)\r
+{\r
+    struct RSAKey *rsa;\r
+    int ret;\r
+\r
+    rsa = rsa2_newkey((char *) blob, len);\r
+    ret = bignum_bitcount(rsa->modulus);\r
+    rsa2_freekey(rsa);\r
+\r
+    return ret;\r
+}\r
+\r
+static char *rsa2_fingerprint(void *key)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    struct MD5Context md5c;\r
+    unsigned char digest[16], lenbuf[4];\r
+    char buffer[16 * 3 + 40];\r
+    char *ret;\r
+    int numlen, i;\r
+\r
+    MD5Init(&md5c);\r
+    MD5Update(&md5c, (unsigned char *)"\0\0\0\7ssh-rsa", 11);\r
+\r
+#define ADD_BIGNUM(bignum) \\r
+    numlen = (bignum_bitcount(bignum)+8)/8; \\r
+    PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \\r
+    for (i = numlen; i-- ;) { \\r
+        unsigned char c = bignum_byte(bignum, i); \\r
+        MD5Update(&md5c, &c, 1); \\r
+    }\r
+    ADD_BIGNUM(rsa->exponent);\r
+    ADD_BIGNUM(rsa->modulus);\r
+#undef ADD_BIGNUM\r
+\r
+    MD5Final(digest, &md5c);\r
+\r
+    sprintf(buffer, "ssh-rsa %d ", bignum_bitcount(rsa->modulus));\r
+    for (i = 0; i < 16; i++)\r
+       sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",\r
+               digest[i]);\r
+    ret = snewn(strlen(buffer) + 1, char);\r
+    if (ret)\r
+       strcpy(ret, buffer);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * This is the magic ASN.1/DER prefix that goes in the decoded\r
+ * signature, between the string of FFs and the actual SHA hash\r
+ * value. The meaning of it is:\r
+ * \r
+ * 00 -- this marks the end of the FFs; not part of the ASN.1 bit itself\r
+ * \r
+ * 30 21 -- a constructed SEQUENCE of length 0x21\r
+ *    30 09 -- a constructed sub-SEQUENCE of length 9\r
+ *       06 05 -- an object identifier, length 5\r
+ *          2B 0E 03 02 1A -- object id { 1 3 14 3 2 26 }\r
+ *                            (the 1,3 comes from 0x2B = 43 = 40*1+3)\r
+ *       05 00 -- NULL\r
+ *    04 14 -- a primitive OCTET STRING of length 0x14\r
+ *       [0x14 bytes of hash data follows]\r
+ * \r
+ * The object id in the middle there is listed as `id-sha1' in\r
+ * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1d2.asn (the\r
+ * ASN module for PKCS #1) and its expanded form is as follows:\r
+ * \r
+ * id-sha1                OBJECT IDENTIFIER ::= {\r
+ *    iso(1) identified-organization(3) oiw(14) secsig(3)\r
+ *    algorithms(2) 26 }\r
+ */\r
+static const unsigned char asn1_weird_stuff[] = {\r
+    0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B,\r
+    0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14,\r
+};\r
+\r
+#define ASN1_LEN ( (int) sizeof(asn1_weird_stuff) )\r
+\r
+static int rsa2_verifysig(void *key, char *sig, int siglen,\r
+                         char *data, int datalen)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    Bignum in, out;\r
+    char *p;\r
+    int slen;\r
+    int bytes, i, j, ret;\r
+    unsigned char hash[20];\r
+\r
+    getstring(&sig, &siglen, &p, &slen);\r
+    if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) {\r
+       return 0;\r
+    }\r
+    in = getmp(&sig, &siglen);\r
+    out = modpow(in, rsa->exponent, rsa->modulus);\r
+    freebn(in);\r
+\r
+    ret = 1;\r
+\r
+    bytes = (bignum_bitcount(rsa->modulus)+7) / 8;\r
+    /* Top (partial) byte should be zero. */\r
+    if (bignum_byte(out, bytes - 1) != 0)\r
+       ret = 0;\r
+    /* First whole byte should be 1. */\r
+    if (bignum_byte(out, bytes - 2) != 1)\r
+       ret = 0;\r
+    /* Most of the rest should be FF. */\r
+    for (i = bytes - 3; i >= 20 + ASN1_LEN; i--) {\r
+       if (bignum_byte(out, i) != 0xFF)\r
+           ret = 0;\r
+    }\r
+    /* Then we expect to see the asn1_weird_stuff. */\r
+    for (i = 20 + ASN1_LEN - 1, j = 0; i >= 20; i--, j++) {\r
+       if (bignum_byte(out, i) != asn1_weird_stuff[j])\r
+           ret = 0;\r
+    }\r
+    /* Finally, we expect to see the SHA-1 hash of the signed data. */\r
+    SHA_Simple(data, datalen, hash);\r
+    for (i = 19, j = 0; i >= 0; i--, j++) {\r
+       if (bignum_byte(out, i) != hash[j])\r
+           ret = 0;\r
+    }\r
+    freebn(out);\r
+\r
+    return ret;\r
+}\r
+\r
+static unsigned char *rsa2_sign(void *key, char *data, int datalen,\r
+                               int *siglen)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    unsigned char *bytes;\r
+    int nbytes;\r
+    unsigned char hash[20];\r
+    Bignum in, out;\r
+    int i, j;\r
+\r
+    SHA_Simple(data, datalen, hash);\r
+\r
+    nbytes = (bignum_bitcount(rsa->modulus) - 1) / 8;\r
+    assert(1 <= nbytes - 20 - ASN1_LEN);\r
+    bytes = snewn(nbytes, unsigned char);\r
+\r
+    bytes[0] = 1;\r
+    for (i = 1; i < nbytes - 20 - ASN1_LEN; i++)\r
+       bytes[i] = 0xFF;\r
+    for (i = nbytes - 20 - ASN1_LEN, j = 0; i < nbytes - 20; i++, j++)\r
+       bytes[i] = asn1_weird_stuff[j];\r
+    for (i = nbytes - 20, j = 0; i < nbytes; i++, j++)\r
+       bytes[i] = hash[j];\r
+\r
+    in = bignum_from_bytes(bytes, nbytes);\r
+    sfree(bytes);\r
+\r
+    out = rsa_privkey_op(in, rsa);\r
+    freebn(in);\r
+\r
+    nbytes = (bignum_bitcount(out) + 7) / 8;\r
+    bytes = snewn(4 + 7 + 4 + nbytes, unsigned char);\r
+    PUT_32BIT(bytes, 7);\r
+    memcpy(bytes + 4, "ssh-rsa", 7);\r
+    PUT_32BIT(bytes + 4 + 7, nbytes);\r
+    for (i = 0; i < nbytes; i++)\r
+       bytes[4 + 7 + 4 + i] = bignum_byte(out, nbytes - 1 - i);\r
+    freebn(out);\r
+\r
+    *siglen = 4 + 7 + 4 + nbytes;\r
+    return bytes;\r
+}\r
+\r
+const struct ssh_signkey ssh_rsa = {\r
+    rsa2_newkey,\r
+    rsa2_freekey,\r
+    rsa2_fmtkey,\r
+    rsa2_public_blob,\r
+    rsa2_private_blob,\r
+    rsa2_createkey,\r
+    rsa2_openssh_createkey,\r
+    rsa2_openssh_fmtkey,\r
+    rsa2_pubkey_bits,\r
+    rsa2_fingerprint,\r
+    rsa2_verifysig,\r
+    rsa2_sign,\r
+    "ssh-rsa",\r
+    "rsa2"\r
+};\r
+\r
+void *ssh_rsakex_newkey(char *data, int len)\r
+{\r
+    return rsa2_newkey(data, len);\r
+}\r
+\r
+void ssh_rsakex_freekey(void *key)\r
+{\r
+    rsa2_freekey(key);\r
+}\r
+\r
+int ssh_rsakex_klen(void *key)\r
+{\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+\r
+    return bignum_bitcount(rsa->modulus);\r
+}\r
+\r
+static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,\r
+                     void *vdata, int datalen)\r
+{\r
+    unsigned char *data = (unsigned char *)vdata;\r
+    unsigned count = 0;\r
+\r
+    while (datalen > 0) {\r
+        int i, max = (datalen > h->hlen ? h->hlen : datalen);\r
+        void *s;\r
+        unsigned char counter[4], hash[SSH2_KEX_MAX_HASH_LEN];\r
+\r
+       assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN);\r
+        PUT_32BIT(counter, count);\r
+        s = h->init();\r
+        h->bytes(s, seed, seedlen);\r
+        h->bytes(s, counter, 4);\r
+        h->final(s, hash);\r
+        count++;\r
+\r
+        for (i = 0; i < max; i++)\r
+            data[i] ^= hash[i];\r
+\r
+        data += max;\r
+        datalen -= max;\r
+    }\r
+}\r
+\r
+void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,\r
+                        unsigned char *out, int outlen,\r
+                        void *key)\r
+{\r
+    Bignum b1, b2;\r
+    struct RSAKey *rsa = (struct RSAKey *) key;\r
+    int k, i;\r
+    char *p;\r
+    const int HLEN = h->hlen;\r
+\r
+    /*\r
+     * Here we encrypt using RSAES-OAEP. Essentially this means:\r
+     * \r
+     *  - we have a SHA-based `mask generation function' which\r
+     *    creates a pseudo-random stream of mask data\r
+     *    deterministically from an input chunk of data.\r
+     * \r
+     *  - we have a random chunk of data called a seed.\r
+     * \r
+     *  - we use the seed to generate a mask which we XOR with our\r
+     *    plaintext.\r
+     * \r
+     *  - then we use _the masked plaintext_ to generate a mask\r
+     *    which we XOR with the seed.\r
+     * \r
+     *  - then we concatenate the masked seed and the masked\r
+     *    plaintext, and RSA-encrypt that lot.\r
+     * \r
+     * The result is that the data input to the encryption function\r
+     * is random-looking and (hopefully) contains no exploitable\r
+     * structure such as PKCS1-v1_5 does.\r
+     * \r
+     * For a precise specification, see RFC 3447, section 7.1.1.\r
+     * Some of the variable names below are derived from that, so\r
+     * it'd probably help to read it anyway.\r
+     */\r
+\r
+    /* k denotes the length in octets of the RSA modulus. */\r
+    k = (7 + bignum_bitcount(rsa->modulus)) / 8;\r
+\r
+    /* The length of the input data must be at most k - 2hLen - 2. */\r
+    assert(inlen > 0 && inlen <= k - 2*HLEN - 2);\r
+\r
+    /* The length of the output data wants to be precisely k. */\r
+    assert(outlen == k);\r
+\r
+    /*\r
+     * Now perform EME-OAEP encoding. First set up all the unmasked\r
+     * output data.\r
+     */\r
+    /* Leading byte zero. */\r
+    out[0] = 0;\r
+    /* At position 1, the seed: HLEN bytes of random data. */\r
+    for (i = 0; i < HLEN; i++)\r
+        out[i + 1] = random_byte();\r
+    /* At position 1+HLEN, the data block DB, consisting of: */\r
+    /* The hash of the label (we only support an empty label here) */\r
+    h->final(h->init(), out + HLEN + 1);\r
+    /* A bunch of zero octets */\r
+    memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));\r
+    /* A single 1 octet, followed by the input message data. */\r
+    out[outlen - inlen - 1] = 1;\r
+    memcpy(out + outlen - inlen, in, inlen);\r
+\r
+    /*\r
+     * Now use the seed data to mask the block DB.\r
+     */\r
+    oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1);\r
+\r
+    /*\r
+     * And now use the masked DB to mask the seed itself.\r
+     */\r
+    oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN);\r
+\r
+    /*\r
+     * Now `out' contains precisely the data we want to\r
+     * RSA-encrypt.\r
+     */\r
+    b1 = bignum_from_bytes(out, outlen);\r
+    b2 = modpow(b1, rsa->exponent, rsa->modulus);\r
+    p = (char *)out;\r
+    for (i = outlen; i--;) {\r
+       *p++ = bignum_byte(b2, i);\r
+    }\r
+    freebn(b1);\r
+    freebn(b2);\r
+\r
+    /*\r
+     * And we're done.\r
+     */\r
+}\r
+\r
+static const struct ssh_kex ssh_rsa_kex_sha1 = {\r
+    "rsa1024-sha1", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha1\r
+};\r
+\r
+static const struct ssh_kex ssh_rsa_kex_sha256 = {\r
+    "rsa2048-sha256", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha256\r
+};\r
+\r
+static const struct ssh_kex *const rsa_kex_list[] = {\r
+    &ssh_rsa_kex_sha256,\r
+    &ssh_rsa_kex_sha1\r
+};\r
+\r
+const struct ssh_kexes ssh_rsa_kex = {\r
+    sizeof(rsa_kex_list) / sizeof(*rsa_kex_list),\r
+    rsa_kex_list\r
+};\r
diff --git a/putty/SSHRSAG.C b/putty/SSHRSAG.C
new file mode 100644 (file)
index 0000000..b19d3c1
--- /dev/null
@@ -0,0 +1,103 @@
+/*\r
+ * RSA key generation.\r
+ */\r
+\r
+#include "ssh.h"\r
+\r
+#define RSA_EXPONENT 37                       /* we like this prime */\r
+\r
+int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,\r
+                void *pfnparam)\r
+{\r
+    Bignum pm1, qm1, phi_n;\r
+\r
+    /*\r
+     * Set up the phase limits for the progress report. We do this\r
+     * by passing minus the phase number.\r
+     *\r
+     * For prime generation: our initial filter finds things\r
+     * coprime to everything below 2^16. Computing the product of\r
+     * (p-1)/p for all prime p below 2^16 gives about 20.33; so\r
+     * among B-bit integers, one in every 20.33 will get through\r
+     * the initial filter to be a candidate prime.\r
+     *\r
+     * Meanwhile, we are searching for primes in the region of 2^B;\r
+     * since pi(x) ~ x/log(x), when x is in the region of 2^B, the\r
+     * prime density will be d/dx pi(x) ~ 1/log(B), i.e. about\r
+     * 1/0.6931B. So the chance of any given candidate being prime\r
+     * is 20.33/0.6931B, which is roughly 29.34 divided by B.\r
+     *\r
+     * So now we have this probability P, we're looking at an\r
+     * exponential distribution with parameter P: we will manage in\r
+     * one attempt with probability P, in two with probability\r
+     * P(1-P), in three with probability P(1-P)^2, etc. The\r
+     * probability that we have still not managed to find a prime\r
+     * after N attempts is (1-P)^N.\r
+     * \r
+     * We therefore inform the progress indicator of the number B\r
+     * (29.34/B), so that it knows how much to increment by each\r
+     * time. We do this in 16-bit fixed point, so 29.34 becomes\r
+     * 0x1D.57C4.\r
+     */\r
+    pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x10000);\r
+    pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / (bits / 2));\r
+    pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x10000);\r
+    pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / (bits - bits / 2));\r
+    pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x4000);\r
+    pfn(pfnparam, PROGFN_LIN_PHASE, 3, 5);\r
+    pfn(pfnparam, PROGFN_READY, 0, 0);\r
+\r
+    /*\r
+     * We don't generate e; we just use a standard one always.\r
+     */\r
+    key->exponent = bignum_from_long(RSA_EXPONENT);\r
+\r
+    /*\r
+     * Generate p and q: primes with combined length `bits', not\r
+     * congruent to 1 modulo e. (Strictly speaking, we wanted (p-1)\r
+     * and e to be coprime, and (q-1) and e to be coprime, but in\r
+     * general that's slightly more fiddly to arrange. By choosing\r
+     * a prime e, we can simplify the criterion.)\r
+     */\r
+    key->p = primegen(bits / 2, RSA_EXPONENT, 1, NULL,\r
+                     1, pfn, pfnparam);\r
+    key->q = primegen(bits - bits / 2, RSA_EXPONENT, 1, NULL,\r
+                     2, pfn, pfnparam);\r
+\r
+    /*\r
+     * Ensure p > q, by swapping them if not.\r
+     */\r
+    if (bignum_cmp(key->p, key->q) < 0) {\r
+       Bignum t = key->p;\r
+       key->p = key->q;\r
+       key->q = t;\r
+    }\r
+\r
+    /*\r
+     * Now we have p, q and e. All we need to do now is work out\r
+     * the other helpful quantities: n=pq, d=e^-1 mod (p-1)(q-1),\r
+     * and (q^-1 mod p).\r
+     */\r
+    pfn(pfnparam, PROGFN_PROGRESS, 3, 1);\r
+    key->modulus = bigmul(key->p, key->q);\r
+    pfn(pfnparam, PROGFN_PROGRESS, 3, 2);\r
+    pm1 = copybn(key->p);\r
+    decbn(pm1);\r
+    qm1 = copybn(key->q);\r
+    decbn(qm1);\r
+    phi_n = bigmul(pm1, qm1);\r
+    pfn(pfnparam, PROGFN_PROGRESS, 3, 3);\r
+    freebn(pm1);\r
+    freebn(qm1);\r
+    key->private_exponent = modinv(key->exponent, phi_n);\r
+    pfn(pfnparam, PROGFN_PROGRESS, 3, 4);\r
+    key->iqmp = modinv(key->q, key->p);\r
+    pfn(pfnparam, PROGFN_PROGRESS, 3, 5);\r
+\r
+    /*\r
+     * Clean up temporary numbers.\r
+     */\r
+    freebn(phi_n);\r
+\r
+    return 1;\r
+}\r
diff --git a/putty/SSHSH256.C b/putty/SSHSH256.C
new file mode 100644 (file)
index 0000000..538982a
--- /dev/null
@@ -0,0 +1,269 @@
+/*\r
+ * SHA-256 algorithm as described at\r
+ * \r
+ *   http://csrc.nist.gov/cryptval/shs.html\r
+ */\r
+\r
+#include "ssh.h"\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Core SHA256 algorithm: processes 16-word blocks into a message digest.\r
+ */\r
+\r
+#define ror(x,y) ( ((x) << (32-y)) | (((uint32)(x)) >> (y)) )\r
+#define shr(x,y) ( (((uint32)(x)) >> (y)) )\r
+#define Ch(x,y,z) ( ((x) & (y)) ^ (~(x) & (z)) )\r
+#define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) )\r
+#define bigsigma0(x) ( ror((x),2) ^ ror((x),13) ^ ror((x),22) )\r
+#define bigsigma1(x) ( ror((x),6) ^ ror((x),11) ^ ror((x),25) )\r
+#define smallsigma0(x) ( ror((x),7) ^ ror((x),18) ^ shr((x),3) )\r
+#define smallsigma1(x) ( ror((x),17) ^ ror((x),19) ^ shr((x),10) )\r
+\r
+void SHA256_Core_Init(SHA256_State *s) {\r
+    s->h[0] = 0x6a09e667;\r
+    s->h[1] = 0xbb67ae85;\r
+    s->h[2] = 0x3c6ef372;\r
+    s->h[3] = 0xa54ff53a;\r
+    s->h[4] = 0x510e527f;\r
+    s->h[5] = 0x9b05688c;\r
+    s->h[6] = 0x1f83d9ab;\r
+    s->h[7] = 0x5be0cd19;\r
+}\r
+\r
+void SHA256_Block(SHA256_State *s, uint32 *block) {\r
+    uint32 w[80];\r
+    uint32 a,b,c,d,e,f,g,h;\r
+    static const int k[] = {\r
+        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,\r
+        0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\r
+        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\r
+        0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\r
+        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,\r
+        0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\r
+        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,\r
+        0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\r
+        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\r
+        0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\r
+        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,\r
+        0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\r
+        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,\r
+        0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\r
+        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\r
+        0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\r
+    };\r
+\r
+    int t;\r
+\r
+    for (t = 0; t < 16; t++)\r
+        w[t] = block[t];\r
+\r
+    for (t = 16; t < 64; t++)\r
+       w[t] = smallsigma1(w[t-2]) + w[t-7] + smallsigma0(w[t-15]) + w[t-16];\r
+\r
+    a = s->h[0]; b = s->h[1]; c = s->h[2]; d = s->h[3];\r
+    e = s->h[4]; f = s->h[5]; g = s->h[6]; h = s->h[7];\r
+\r
+    for (t = 0; t < 64; t+=8) {\r
+        uint32 t1, t2;\r
+\r
+#define ROUND(j,a,b,c,d,e,f,g,h) \\r
+       t1 = h + bigsigma1(e) + Ch(e,f,g) + k[j] + w[j]; \\r
+       t2 = bigsigma0(a) + Maj(a,b,c); \\r
+        d = d + t1; h = t1 + t2;\r
+\r
+       ROUND(t+0, a,b,c,d,e,f,g,h);\r
+       ROUND(t+1, h,a,b,c,d,e,f,g);\r
+       ROUND(t+2, g,h,a,b,c,d,e,f);\r
+       ROUND(t+3, f,g,h,a,b,c,d,e);\r
+       ROUND(t+4, e,f,g,h,a,b,c,d);\r
+       ROUND(t+5, d,e,f,g,h,a,b,c);\r
+       ROUND(t+6, c,d,e,f,g,h,a,b);\r
+       ROUND(t+7, b,c,d,e,f,g,h,a);\r
+    }\r
+\r
+    s->h[0] += a; s->h[1] += b; s->h[2] += c; s->h[3] += d;\r
+    s->h[4] += e; s->h[5] += f; s->h[6] += g; s->h[7] += h;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Outer SHA256 algorithm: take an arbitrary length byte string,\r
+ * convert it into 16-word blocks with the prescribed padding at\r
+ * the end, and pass those blocks to the core SHA256 algorithm.\r
+ */\r
+\r
+#define BLKSIZE 64\r
+\r
+void SHA256_Init(SHA256_State *s) {\r
+    SHA256_Core_Init(s);\r
+    s->blkused = 0;\r
+    s->lenhi = s->lenlo = 0;\r
+}\r
+\r
+void SHA256_Bytes(SHA256_State *s, const void *p, int len) {\r
+    unsigned char *q = (unsigned char *)p;\r
+    uint32 wordblock[16];\r
+    uint32 lenw = len;\r
+    int i;\r
+\r
+    /*\r
+     * Update the length field.\r
+     */\r
+    s->lenlo += lenw;\r
+    s->lenhi += (s->lenlo < lenw);\r
+\r
+    if (s->blkused && s->blkused+len < BLKSIZE) {\r
+        /*\r
+         * Trivial case: just add to the block.\r
+         */\r
+        memcpy(s->block + s->blkused, q, len);\r
+        s->blkused += len;\r
+    } else {\r
+        /*\r
+         * We must complete and process at least one block.\r
+         */\r
+        while (s->blkused + len >= BLKSIZE) {\r
+            memcpy(s->block + s->blkused, q, BLKSIZE - s->blkused);\r
+            q += BLKSIZE - s->blkused;\r
+            len -= BLKSIZE - s->blkused;\r
+            /* Now process the block. Gather bytes big-endian into words */\r
+            for (i = 0; i < 16; i++) {\r
+                wordblock[i] =\r
+                    ( ((uint32)s->block[i*4+0]) << 24 ) |\r
+                    ( ((uint32)s->block[i*4+1]) << 16 ) |\r
+                    ( ((uint32)s->block[i*4+2]) <<  8 ) |\r
+                    ( ((uint32)s->block[i*4+3]) <<  0 );\r
+            }\r
+            SHA256_Block(s, wordblock);\r
+            s->blkused = 0;\r
+        }\r
+        memcpy(s->block, q, len);\r
+        s->blkused = len;\r
+    }\r
+}\r
+\r
+void SHA256_Final(SHA256_State *s, unsigned char *digest) {\r
+    int i;\r
+    int pad;\r
+    unsigned char c[64];\r
+    uint32 lenhi, lenlo;\r
+\r
+    if (s->blkused >= 56)\r
+        pad = 56 + 64 - s->blkused;\r
+    else\r
+        pad = 56 - s->blkused;\r
+\r
+    lenhi = (s->lenhi << 3) | (s->lenlo >> (32-3));\r
+    lenlo = (s->lenlo << 3);\r
+\r
+    memset(c, 0, pad);\r
+    c[0] = 0x80;\r
+    SHA256_Bytes(s, &c, pad);\r
+\r
+    c[0] = (lenhi >> 24) & 0xFF;\r
+    c[1] = (lenhi >> 16) & 0xFF;\r
+    c[2] = (lenhi >>  8) & 0xFF;\r
+    c[3] = (lenhi >>  0) & 0xFF;\r
+    c[4] = (lenlo >> 24) & 0xFF;\r
+    c[5] = (lenlo >> 16) & 0xFF;\r
+    c[6] = (lenlo >>  8) & 0xFF;\r
+    c[7] = (lenlo >>  0) & 0xFF;\r
+\r
+    SHA256_Bytes(s, &c, 8);\r
+\r
+    for (i = 0; i < 8; i++) {\r
+       digest[i*4+0] = (s->h[i] >> 24) & 0xFF;\r
+       digest[i*4+1] = (s->h[i] >> 16) & 0xFF;\r
+       digest[i*4+2] = (s->h[i] >>  8) & 0xFF;\r
+       digest[i*4+3] = (s->h[i] >>  0) & 0xFF;\r
+    }\r
+}\r
+\r
+void SHA256_Simple(const void *p, int len, unsigned char *output) {\r
+    SHA256_State s;\r
+\r
+    SHA256_Init(&s);\r
+    SHA256_Bytes(&s, p, len);\r
+    SHA256_Final(&s, output);\r
+}\r
+\r
+/*\r
+ * Thin abstraction for things where hashes are pluggable.\r
+ */\r
+\r
+static void *sha256_init(void)\r
+{\r
+    SHA256_State *s;\r
+\r
+    s = snew(SHA256_State);\r
+    SHA256_Init(s);\r
+    return s;\r
+}\r
+\r
+static void sha256_bytes(void *handle, void *p, int len)\r
+{\r
+    SHA256_State *s = handle;\r
+\r
+    SHA256_Bytes(s, p, len);\r
+}\r
+\r
+static void sha256_final(void *handle, unsigned char *output)\r
+{\r
+    SHA256_State *s = handle;\r
+\r
+    SHA256_Final(s, output);\r
+    sfree(s);\r
+}\r
+\r
+const struct ssh_hash ssh_sha256 = {\r
+    sha256_init, sha256_bytes, sha256_final, 32, "SHA-256"\r
+};\r
+\r
+#ifdef TEST\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+int main(void) {\r
+    unsigned char digest[32];\r
+    int i, j, errors;\r
+\r
+    struct {\r
+       const char *teststring;\r
+       unsigned char digest[32];\r
+    } tests[] = {\r
+       { "abc", {\r
+           0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,\r
+           0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,\r
+           0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,\r
+           0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad,\r
+       } },\r
+       { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", {\r
+           0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,\r
+           0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,\r
+           0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,\r
+           0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1,\r
+       } },\r
+    };\r
+\r
+    errors = 0;\r
+\r
+    for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {\r
+       SHA256_Simple(tests[i].teststring,\r
+                     strlen(tests[i].teststring), digest);\r
+       for (j = 0; j < 32; j++) {\r
+           if (digest[j] != tests[i].digest[j]) {\r
+               fprintf(stderr,\r
+                       "\"%s\" digest byte %d should be 0x%02x, is 0x%02x\n",\r
+                       tests[i].teststring, j, tests[i].digest[j], digest[j]);\r
+               errors++;\r
+           }\r
+       }\r
+    }\r
+\r
+    printf("%d errors\n", errors);\r
+\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/SSHSH512.C b/putty/SSHSH512.C
new file mode 100644 (file)
index 0000000..bdb126f
--- /dev/null
@@ -0,0 +1,358 @@
+/*\r
+ * SHA-512 algorithm as described at\r
+ * \r
+ *   http://csrc.nist.gov/cryptval/shs.html\r
+ */\r
+\r
+#include "ssh.h"\r
+\r
+#define BLKSIZE 128\r
+\r
+/*\r
+ * Arithmetic implementations. Note that AND, XOR and NOT can\r
+ * overlap destination with one source, but the others can't.\r
+ */\r
+#define add(r,x,y) ( r.lo = y.lo + x.lo, \\r
+                    r.hi = y.hi + x.hi + ((uint32)r.lo < (uint32)y.lo) )\r
+#define rorB(r,x,y) ( r.lo = ((uint32)x.hi >> ((y)-32)) | ((uint32)x.lo << (64-(y))), \\r
+                     r.hi = ((uint32)x.lo >> ((y)-32)) | ((uint32)x.hi << (64-(y))) )\r
+#define rorL(r,x,y) ( r.lo = ((uint32)x.lo >> (y)) | ((uint32)x.hi << (32-(y))), \\r
+                     r.hi = ((uint32)x.hi >> (y)) | ((uint32)x.lo << (32-(y))) )\r
+#define shrB(r,x,y) ( r.lo = (uint32)x.hi >> ((y)-32), r.hi = 0 )\r
+#define shrL(r,x,y) ( r.lo = ((uint32)x.lo >> (y)) | ((uint32)x.hi << (32-(y))), \\r
+                     r.hi = (uint32)x.hi >> (y) )\r
+#define and(r,x,y) ( r.lo = x.lo & y.lo, r.hi = x.hi & y.hi )\r
+#define xor(r,x,y) ( r.lo = x.lo ^ y.lo, r.hi = x.hi ^ y.hi )\r
+#define not(r,x) ( r.lo = ~x.lo, r.hi = ~x.hi )\r
+#define INIT(h,l) { h, l }\r
+#define BUILD(r,h,l) ( r.hi = h, r.lo = l )\r
+#define EXTRACT(h,l,r) ( h = r.hi, l = r.lo )\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Core SHA512 algorithm: processes 16-doubleword blocks into a\r
+ * message digest.\r
+ */\r
+\r
+#define Ch(r,t,x,y,z) ( not(t,x), and(r,t,z), and(t,x,y), xor(r,r,t) )\r
+#define Maj(r,t,x,y,z) ( and(r,x,y), and(t,x,z), xor(r,r,t), \\r
+                        and(t,y,z), xor(r,r,t) )\r
+#define bigsigma0(r,t,x) ( rorL(r,x,28), rorB(t,x,34), xor(r,r,t), \\r
+                          rorB(t,x,39), xor(r,r,t) )\r
+#define bigsigma1(r,t,x) ( rorL(r,x,14), rorL(t,x,18), xor(r,r,t), \\r
+                          rorB(t,x,41), xor(r,r,t) )\r
+#define smallsigma0(r,t,x) ( rorL(r,x,1), rorL(t,x,8), xor(r,r,t), \\r
+                            shrL(t,x,7), xor(r,r,t) )\r
+#define smallsigma1(r,t,x) ( rorL(r,x,19), rorB(t,x,61), xor(r,r,t), \\r
+                            shrL(t,x,6), xor(r,r,t) )\r
+\r
+static void SHA512_Core_Init(SHA512_State *s) {\r
+    static const uint64 iv[] = {\r
+       INIT(0x6a09e667, 0xf3bcc908),\r
+       INIT(0xbb67ae85, 0x84caa73b),\r
+       INIT(0x3c6ef372, 0xfe94f82b),\r
+       INIT(0xa54ff53a, 0x5f1d36f1),\r
+       INIT(0x510e527f, 0xade682d1),\r
+       INIT(0x9b05688c, 0x2b3e6c1f),\r
+       INIT(0x1f83d9ab, 0xfb41bd6b),\r
+       INIT(0x5be0cd19, 0x137e2179),\r
+    };\r
+    int i;\r
+    for (i = 0; i < 8; i++)\r
+       s->h[i] = iv[i];\r
+}\r
+\r
+static void SHA512_Block(SHA512_State *s, uint64 *block) {\r
+    uint64 w[80];\r
+    uint64 a,b,c,d,e,f,g,h;\r
+    static const uint64 k[] = {\r
+       INIT(0x428a2f98, 0xd728ae22), INIT(0x71374491, 0x23ef65cd),\r
+       INIT(0xb5c0fbcf, 0xec4d3b2f), INIT(0xe9b5dba5, 0x8189dbbc),\r
+       INIT(0x3956c25b, 0xf348b538), INIT(0x59f111f1, 0xb605d019),\r
+       INIT(0x923f82a4, 0xaf194f9b), INIT(0xab1c5ed5, 0xda6d8118),\r
+       INIT(0xd807aa98, 0xa3030242), INIT(0x12835b01, 0x45706fbe),\r
+       INIT(0x243185be, 0x4ee4b28c), INIT(0x550c7dc3, 0xd5ffb4e2),\r
+       INIT(0x72be5d74, 0xf27b896f), INIT(0x80deb1fe, 0x3b1696b1),\r
+       INIT(0x9bdc06a7, 0x25c71235), INIT(0xc19bf174, 0xcf692694),\r
+       INIT(0xe49b69c1, 0x9ef14ad2), INIT(0xefbe4786, 0x384f25e3),\r
+       INIT(0x0fc19dc6, 0x8b8cd5b5), INIT(0x240ca1cc, 0x77ac9c65),\r
+       INIT(0x2de92c6f, 0x592b0275), INIT(0x4a7484aa, 0x6ea6e483),\r
+       INIT(0x5cb0a9dc, 0xbd41fbd4), INIT(0x76f988da, 0x831153b5),\r
+       INIT(0x983e5152, 0xee66dfab), INIT(0xa831c66d, 0x2db43210),\r
+       INIT(0xb00327c8, 0x98fb213f), INIT(0xbf597fc7, 0xbeef0ee4),\r
+       INIT(0xc6e00bf3, 0x3da88fc2), INIT(0xd5a79147, 0x930aa725),\r
+       INIT(0x06ca6351, 0xe003826f), INIT(0x14292967, 0x0a0e6e70),\r
+       INIT(0x27b70a85, 0x46d22ffc), INIT(0x2e1b2138, 0x5c26c926),\r
+       INIT(0x4d2c6dfc, 0x5ac42aed), INIT(0x53380d13, 0x9d95b3df),\r
+       INIT(0x650a7354, 0x8baf63de), INIT(0x766a0abb, 0x3c77b2a8),\r
+       INIT(0x81c2c92e, 0x47edaee6), INIT(0x92722c85, 0x1482353b),\r
+       INIT(0xa2bfe8a1, 0x4cf10364), INIT(0xa81a664b, 0xbc423001),\r
+       INIT(0xc24b8b70, 0xd0f89791), INIT(0xc76c51a3, 0x0654be30),\r
+       INIT(0xd192e819, 0xd6ef5218), INIT(0xd6990624, 0x5565a910),\r
+       INIT(0xf40e3585, 0x5771202a), INIT(0x106aa070, 0x32bbd1b8),\r
+       INIT(0x19a4c116, 0xb8d2d0c8), INIT(0x1e376c08, 0x5141ab53),\r
+       INIT(0x2748774c, 0xdf8eeb99), INIT(0x34b0bcb5, 0xe19b48a8),\r
+       INIT(0x391c0cb3, 0xc5c95a63), INIT(0x4ed8aa4a, 0xe3418acb),\r
+       INIT(0x5b9cca4f, 0x7763e373), INIT(0x682e6ff3, 0xd6b2b8a3),\r
+       INIT(0x748f82ee, 0x5defb2fc), INIT(0x78a5636f, 0x43172f60),\r
+       INIT(0x84c87814, 0xa1f0ab72), INIT(0x8cc70208, 0x1a6439ec),\r
+       INIT(0x90befffa, 0x23631e28), INIT(0xa4506ceb, 0xde82bde9),\r
+       INIT(0xbef9a3f7, 0xb2c67915), INIT(0xc67178f2, 0xe372532b),\r
+       INIT(0xca273ece, 0xea26619c), INIT(0xd186b8c7, 0x21c0c207),\r
+       INIT(0xeada7dd6, 0xcde0eb1e), INIT(0xf57d4f7f, 0xee6ed178),\r
+       INIT(0x06f067aa, 0x72176fba), INIT(0x0a637dc5, 0xa2c898a6),\r
+       INIT(0x113f9804, 0xbef90dae), INIT(0x1b710b35, 0x131c471b),\r
+       INIT(0x28db77f5, 0x23047d84), INIT(0x32caab7b, 0x40c72493),\r
+       INIT(0x3c9ebe0a, 0x15c9bebc), INIT(0x431d67c4, 0x9c100d4c),\r
+       INIT(0x4cc5d4be, 0xcb3e42b6), INIT(0x597f299c, 0xfc657e2a),\r
+       INIT(0x5fcb6fab, 0x3ad6faec), INIT(0x6c44198c, 0x4a475817),\r
+    };\r
+\r
+    int t;\r
+\r
+    for (t = 0; t < 16; t++)\r
+        w[t] = block[t];\r
+\r
+    for (t = 16; t < 80; t++) {\r
+       uint64 p, q, r, tmp;\r
+       smallsigma1(p, tmp, w[t-2]);\r
+       smallsigma0(q, tmp, w[t-15]);\r
+       add(r, p, q);\r
+       add(p, r, w[t-7]);\r
+       add(w[t], p, w[t-16]);\r
+    }\r
+\r
+    a = s->h[0]; b = s->h[1]; c = s->h[2]; d = s->h[3];\r
+    e = s->h[4]; f = s->h[5]; g = s->h[6]; h = s->h[7];\r
+\r
+    for (t = 0; t < 80; t+=8) {\r
+        uint64 tmp, p, q, r;\r
+\r
+#define ROUND(j,a,b,c,d,e,f,g,h) \\r
+       bigsigma1(p, tmp, e); \\r
+       Ch(q, tmp, e, f, g); \\r
+       add(r, p, q); \\r
+       add(p, r, k[j]) ; \\r
+       add(q, p, w[j]); \\r
+       add(r, q, h); \\r
+       bigsigma0(p, tmp, a); \\r
+       Maj(tmp, q, a, b, c); \\r
+       add(q, tmp, p); \\r
+       add(p, r, d); \\r
+       d = p; \\r
+       add(h, q, r);\r
+\r
+       ROUND(t+0, a,b,c,d,e,f,g,h);\r
+       ROUND(t+1, h,a,b,c,d,e,f,g);\r
+       ROUND(t+2, g,h,a,b,c,d,e,f);\r
+       ROUND(t+3, f,g,h,a,b,c,d,e);\r
+       ROUND(t+4, e,f,g,h,a,b,c,d);\r
+       ROUND(t+5, d,e,f,g,h,a,b,c);\r
+       ROUND(t+6, c,d,e,f,g,h,a,b);\r
+       ROUND(t+7, b,c,d,e,f,g,h,a);\r
+    }\r
+\r
+    {\r
+       uint64 tmp;\r
+#define UPDATE(state, local) ( tmp = state, add(state, tmp, local) )\r
+       UPDATE(s->h[0], a); UPDATE(s->h[1], b);\r
+       UPDATE(s->h[2], c); UPDATE(s->h[3], d);\r
+       UPDATE(s->h[4], e); UPDATE(s->h[5], f);\r
+       UPDATE(s->h[6], g); UPDATE(s->h[7], h);\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Outer SHA512 algorithm: take an arbitrary length byte string,\r
+ * convert it into 16-doubleword blocks with the prescribed padding\r
+ * at the end, and pass those blocks to the core SHA512 algorithm.\r
+ */\r
+\r
+void SHA512_Init(SHA512_State *s) {\r
+    int i;\r
+    SHA512_Core_Init(s);\r
+    s->blkused = 0;\r
+    for (i = 0; i < 4; i++)\r
+       s->len[i] = 0;\r
+}\r
+\r
+void SHA512_Bytes(SHA512_State *s, const void *p, int len) {\r
+    unsigned char *q = (unsigned char *)p;\r
+    uint64 wordblock[16];\r
+    uint32 lenw = len;\r
+    int i;\r
+\r
+    /*\r
+     * Update the length field.\r
+     */\r
+    for (i = 0; i < 4; i++) {\r
+       s->len[i] += lenw;\r
+       lenw = (s->len[i] < lenw);\r
+    }\r
+\r
+    if (s->blkused && s->blkused+len < BLKSIZE) {\r
+        /*\r
+         * Trivial case: just add to the block.\r
+         */\r
+        memcpy(s->block + s->blkused, q, len);\r
+        s->blkused += len;\r
+    } else {\r
+        /*\r
+         * We must complete and process at least one block.\r
+         */\r
+        while (s->blkused + len >= BLKSIZE) {\r
+            memcpy(s->block + s->blkused, q, BLKSIZE - s->blkused);\r
+            q += BLKSIZE - s->blkused;\r
+            len -= BLKSIZE - s->blkused;\r
+            /* Now process the block. Gather bytes big-endian into words */\r
+            for (i = 0; i < 16; i++) {\r
+               uint32 h, l;\r
+                h = ( ((uint32)s->block[i*8+0]) << 24 ) |\r
+                    ( ((uint32)s->block[i*8+1]) << 16 ) |\r
+                    ( ((uint32)s->block[i*8+2]) <<  8 ) |\r
+                    ( ((uint32)s->block[i*8+3]) <<  0 );\r
+                l = ( ((uint32)s->block[i*8+4]) << 24 ) |\r
+                    ( ((uint32)s->block[i*8+5]) << 16 ) |\r
+                    ( ((uint32)s->block[i*8+6]) <<  8 ) |\r
+                    ( ((uint32)s->block[i*8+7]) <<  0 );\r
+               BUILD(wordblock[i], h, l);\r
+            }\r
+            SHA512_Block(s, wordblock);\r
+            s->blkused = 0;\r
+        }\r
+        memcpy(s->block, q, len);\r
+        s->blkused = len;\r
+    }\r
+}\r
+\r
+void SHA512_Final(SHA512_State *s, unsigned char *digest) {\r
+    int i;\r
+    int pad;\r
+    unsigned char c[BLKSIZE];\r
+    uint32 len[4];\r
+\r
+    if (s->blkused >= BLKSIZE-16)\r
+        pad = (BLKSIZE-16) + BLKSIZE - s->blkused;\r
+    else\r
+        pad = (BLKSIZE-16) - s->blkused;\r
+\r
+    for (i = 4; i-- ;) {\r
+       uint32 lenhi = s->len[i];\r
+       uint32 lenlo = i > 0 ? s->len[i-1] : 0;\r
+       len[i] = (lenhi << 3) | (lenlo >> (32-3));\r
+    }\r
+\r
+    memset(c, 0, pad);\r
+    c[0] = 0x80;\r
+    SHA512_Bytes(s, &c, pad);\r
+\r
+    for (i = 0; i < 4; i++) {\r
+       c[i*4+0] = (len[3-i] >> 24) & 0xFF;\r
+       c[i*4+1] = (len[3-i] >> 16) & 0xFF;\r
+       c[i*4+2] = (len[3-i] >>  8) & 0xFF;\r
+       c[i*4+3] = (len[3-i] >>  0) & 0xFF;\r
+    }\r
+\r
+    SHA512_Bytes(s, &c, 16);\r
+\r
+    for (i = 0; i < 8; i++) {\r
+       uint32 h, l;\r
+       EXTRACT(h, l, s->h[i]);\r
+       digest[i*8+0] = (h >> 24) & 0xFF;\r
+       digest[i*8+1] = (h >> 16) & 0xFF;\r
+       digest[i*8+2] = (h >>  8) & 0xFF;\r
+       digest[i*8+3] = (h >>  0) & 0xFF;\r
+       digest[i*8+4] = (l >> 24) & 0xFF;\r
+       digest[i*8+5] = (l >> 16) & 0xFF;\r
+       digest[i*8+6] = (l >>  8) & 0xFF;\r
+       digest[i*8+7] = (l >>  0) & 0xFF;\r
+    }\r
+}\r
+\r
+void SHA512_Simple(const void *p, int len, unsigned char *output) {\r
+    SHA512_State s;\r
+\r
+    SHA512_Init(&s);\r
+    SHA512_Bytes(&s, p, len);\r
+    SHA512_Final(&s, output);\r
+}\r
+\r
+#ifdef TEST\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+int main(void) {\r
+    unsigned char digest[64];\r
+    int i, j, errors;\r
+\r
+    struct {\r
+       const char *teststring;\r
+       unsigned char digest512[64];\r
+    } tests[] = {\r
+       { "abc", {\r
+           0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,\r
+            0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,\r
+            0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,\r
+            0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,\r
+            0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,\r
+            0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,\r
+            0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,\r
+            0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f,\r
+       } },\r
+       { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"\r
+       "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", {\r
+           0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,\r
+            0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,\r
+            0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,\r
+            0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,\r
+            0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,\r
+            0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,\r
+            0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,\r
+            0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09,\r
+       } },\r
+       { NULL, {\r
+           0xe7, 0x18, 0x48, 0x3d, 0x0c, 0xe7, 0x69, 0x64,\r
+           0x4e, 0x2e, 0x42, 0xc7, 0xbc, 0x15, 0xb4, 0x63,\r
+           0x8e, 0x1f, 0x98, 0xb1, 0x3b, 0x20, 0x44, 0x28,\r
+           0x56, 0x32, 0xa8, 0x03, 0xaf, 0xa9, 0x73, 0xeb,\r
+           0xde, 0x0f, 0xf2, 0x44, 0x87, 0x7e, 0xa6, 0x0a,\r
+           0x4c, 0xb0, 0x43, 0x2c, 0xe5, 0x77, 0xc3, 0x1b,\r
+           0xeb, 0x00, 0x9c, 0x5c, 0x2c, 0x49, 0xaa, 0x2e,\r
+           0x4e, 0xad, 0xb2, 0x17, 0xad, 0x8c, 0xc0, 0x9b, \r
+       } },\r
+    };\r
+\r
+    errors = 0;\r
+\r
+    for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {\r
+       if (tests[i].teststring) {\r
+           SHA512_Simple(tests[i].teststring,\r
+                         strlen(tests[i].teststring), digest);\r
+       } else {\r
+           SHA512_State s;\r
+           int n;\r
+           SHA512_Init(&s);\r
+           for (n = 0; n < 1000000 / 40; n++)\r
+               SHA512_Bytes(&s, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",\r
+                            40);\r
+           SHA512_Final(&s, digest);\r
+       }\r
+       for (j = 0; j < 64; j++) {\r
+           if (digest[j] != tests[i].digest512[j]) {\r
+               fprintf(stderr,\r
+                       "\"%s\" digest512 byte %d should be 0x%02x, is 0x%02x\n",\r
+                       tests[i].teststring, j, tests[i].digest512[j],\r
+                       digest[j]);\r
+               errors++;\r
+           }\r
+       }\r
+\r
+    }\r
+\r
+    printf("%d errors\n", errors);\r
+\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/SSHSHA.C b/putty/SSHSHA.C
new file mode 100644 (file)
index 0000000..d1c7981
--- /dev/null
@@ -0,0 +1,411 @@
+/*\r
+ * SHA1 hash algorithm. Used in SSH-2 as a MAC, and the transform is\r
+ * also used as a `stirring' function for the PuTTY random number\r
+ * pool. Implemented directly from the specification by Simon\r
+ * Tatham.\r
+ */\r
+\r
+#include "ssh.h"\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Core SHA algorithm: processes 16-word blocks into a message digest.\r
+ */\r
+\r
+#define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) )\r
+\r
+static void SHA_Core_Init(uint32 h[5])\r
+{\r
+    h[0] = 0x67452301;\r
+    h[1] = 0xefcdab89;\r
+    h[2] = 0x98badcfe;\r
+    h[3] = 0x10325476;\r
+    h[4] = 0xc3d2e1f0;\r
+}\r
+\r
+void SHATransform(word32 * digest, word32 * block)\r
+{\r
+    word32 w[80];\r
+    word32 a, b, c, d, e;\r
+    int t;\r
+\r
+    for (t = 0; t < 16; t++)\r
+       w[t] = block[t];\r
+\r
+    for (t = 16; t < 80; t++) {\r
+       word32 tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];\r
+       w[t] = rol(tmp, 1);\r
+    }\r
+\r
+    a = digest[0];\r
+    b = digest[1];\r
+    c = digest[2];\r
+    d = digest[3];\r
+    e = digest[4];\r
+\r
+    for (t = 0; t < 20; t++) {\r
+       word32 tmp =\r
+           rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999;\r
+       e = d;\r
+       d = c;\r
+       c = rol(b, 30);\r
+       b = a;\r
+       a = tmp;\r
+    }\r
+    for (t = 20; t < 40; t++) {\r
+       word32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0x6ed9eba1;\r
+       e = d;\r
+       d = c;\r
+       c = rol(b, 30);\r
+       b = a;\r
+       a = tmp;\r
+    }\r
+    for (t = 40; t < 60; t++) {\r
+       word32 tmp = rol(a,\r
+                        5) + ((b & c) | (b & d) | (c & d)) + e + w[t] +\r
+           0x8f1bbcdc;\r
+       e = d;\r
+       d = c;\r
+       c = rol(b, 30);\r
+       b = a;\r
+       a = tmp;\r
+    }\r
+    for (t = 60; t < 80; t++) {\r
+       word32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0xca62c1d6;\r
+       e = d;\r
+       d = c;\r
+       c = rol(b, 30);\r
+       b = a;\r
+       a = tmp;\r
+    }\r
+\r
+    digest[0] += a;\r
+    digest[1] += b;\r
+    digest[2] += c;\r
+    digest[3] += d;\r
+    digest[4] += e;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Outer SHA algorithm: take an arbitrary length byte string,\r
+ * convert it into 16-word blocks with the prescribed padding at\r
+ * the end, and pass those blocks to the core SHA algorithm.\r
+ */\r
+\r
+void SHA_Init(SHA_State * s)\r
+{\r
+    SHA_Core_Init(s->h);\r
+    s->blkused = 0;\r
+    s->lenhi = s->lenlo = 0;\r
+}\r
+\r
+void SHA_Bytes(SHA_State * s, void *p, int len)\r
+{\r
+    unsigned char *q = (unsigned char *) p;\r
+    uint32 wordblock[16];\r
+    uint32 lenw = len;\r
+    int i;\r
+\r
+    /*\r
+     * Update the length field.\r
+     */\r
+    s->lenlo += lenw;\r
+    s->lenhi += (s->lenlo < lenw);\r
+\r
+    if (s->blkused && s->blkused + len < 64) {\r
+       /*\r
+        * Trivial case: just add to the block.\r
+        */\r
+       memcpy(s->block + s->blkused, q, len);\r
+       s->blkused += len;\r
+    } else {\r
+       /*\r
+        * We must complete and process at least one block.\r
+        */\r
+       while (s->blkused + len >= 64) {\r
+           memcpy(s->block + s->blkused, q, 64 - s->blkused);\r
+           q += 64 - s->blkused;\r
+           len -= 64 - s->blkused;\r
+           /* Now process the block. Gather bytes big-endian into words */\r
+           for (i = 0; i < 16; i++) {\r
+               wordblock[i] =\r
+                   (((uint32) s->block[i * 4 + 0]) << 24) |\r
+                   (((uint32) s->block[i * 4 + 1]) << 16) |\r
+                   (((uint32) s->block[i * 4 + 2]) << 8) |\r
+                   (((uint32) s->block[i * 4 + 3]) << 0);\r
+           }\r
+           SHATransform(s->h, wordblock);\r
+           s->blkused = 0;\r
+       }\r
+       memcpy(s->block, q, len);\r
+       s->blkused = len;\r
+    }\r
+}\r
+\r
+void SHA_Final(SHA_State * s, unsigned char *output)\r
+{\r
+    int i;\r
+    int pad;\r
+    unsigned char c[64];\r
+    uint32 lenhi, lenlo;\r
+\r
+    if (s->blkused >= 56)\r
+       pad = 56 + 64 - s->blkused;\r
+    else\r
+       pad = 56 - s->blkused;\r
+\r
+    lenhi = (s->lenhi << 3) | (s->lenlo >> (32 - 3));\r
+    lenlo = (s->lenlo << 3);\r
+\r
+    memset(c, 0, pad);\r
+    c[0] = 0x80;\r
+    SHA_Bytes(s, &c, pad);\r
+\r
+    c[0] = (lenhi >> 24) & 0xFF;\r
+    c[1] = (lenhi >> 16) & 0xFF;\r
+    c[2] = (lenhi >> 8) & 0xFF;\r
+    c[3] = (lenhi >> 0) & 0xFF;\r
+    c[4] = (lenlo >> 24) & 0xFF;\r
+    c[5] = (lenlo >> 16) & 0xFF;\r
+    c[6] = (lenlo >> 8) & 0xFF;\r
+    c[7] = (lenlo >> 0) & 0xFF;\r
+\r
+    SHA_Bytes(s, &c, 8);\r
+\r
+    for (i = 0; i < 5; i++) {\r
+       output[i * 4] = (s->h[i] >> 24) & 0xFF;\r
+       output[i * 4 + 1] = (s->h[i] >> 16) & 0xFF;\r
+       output[i * 4 + 2] = (s->h[i] >> 8) & 0xFF;\r
+       output[i * 4 + 3] = (s->h[i]) & 0xFF;\r
+    }\r
+}\r
+\r
+void SHA_Simple(void *p, int len, unsigned char *output)\r
+{\r
+    SHA_State s;\r
+\r
+    SHA_Init(&s);\r
+    SHA_Bytes(&s, p, len);\r
+    SHA_Final(&s, output);\r
+}\r
+\r
+/*\r
+ * Thin abstraction for things where hashes are pluggable.\r
+ */\r
+\r
+static void *sha1_init(void)\r
+{\r
+    SHA_State *s;\r
+\r
+    s = snew(SHA_State);\r
+    SHA_Init(s);\r
+    return s;\r
+}\r
+\r
+static void sha1_bytes(void *handle, void *p, int len)\r
+{\r
+    SHA_State *s = handle;\r
+\r
+    SHA_Bytes(s, p, len);\r
+}\r
+\r
+static void sha1_final(void *handle, unsigned char *output)\r
+{\r
+    SHA_State *s = handle;\r
+\r
+    SHA_Final(s, output);\r
+    sfree(s);\r
+}\r
+\r
+const struct ssh_hash ssh_sha1 = {\r
+    sha1_init, sha1_bytes, sha1_final, 20, "SHA-1"\r
+};\r
+\r
+/* ----------------------------------------------------------------------\r
+ * The above is the SHA-1 algorithm itself. Now we implement the\r
+ * HMAC wrapper on it.\r
+ */\r
+\r
+static void *sha1_make_context(void)\r
+{\r
+    return snewn(3, SHA_State);\r
+}\r
+\r
+static void sha1_free_context(void *handle)\r
+{\r
+    sfree(handle);\r
+}\r
+\r
+static void sha1_key_internal(void *handle, unsigned char *key, int len)\r
+{\r
+    SHA_State *keys = (SHA_State *)handle;\r
+    unsigned char foo[64];\r
+    int i;\r
+\r
+    memset(foo, 0x36, 64);\r
+    for (i = 0; i < len && i < 64; i++)\r
+       foo[i] ^= key[i];\r
+    SHA_Init(&keys[0]);\r
+    SHA_Bytes(&keys[0], foo, 64);\r
+\r
+    memset(foo, 0x5C, 64);\r
+    for (i = 0; i < len && i < 64; i++)\r
+       foo[i] ^= key[i];\r
+    SHA_Init(&keys[1]);\r
+    SHA_Bytes(&keys[1], foo, 64);\r
+\r
+    memset(foo, 0, 64);                       /* burn the evidence */\r
+}\r
+\r
+static void sha1_key(void *handle, unsigned char *key)\r
+{\r
+    sha1_key_internal(handle, key, 20);\r
+}\r
+\r
+static void sha1_key_buggy(void *handle, unsigned char *key)\r
+{\r
+    sha1_key_internal(handle, key, 16);\r
+}\r
+\r
+static void hmacsha1_start(void *handle)\r
+{\r
+    SHA_State *keys = (SHA_State *)handle;\r
+\r
+    keys[2] = keys[0];               /* structure copy */\r
+}\r
+\r
+static void hmacsha1_bytes(void *handle, unsigned char const *blk, int len)\r
+{\r
+    SHA_State *keys = (SHA_State *)handle;\r
+    SHA_Bytes(&keys[2], (void *)blk, len);\r
+}\r
+\r
+static void hmacsha1_genresult(void *handle, unsigned char *hmac)\r
+{\r
+    SHA_State *keys = (SHA_State *)handle;\r
+    SHA_State s;\r
+    unsigned char intermediate[20];\r
+\r
+    s = keys[2];                      /* structure copy */\r
+    SHA_Final(&s, intermediate);\r
+    s = keys[1];                      /* structure copy */\r
+    SHA_Bytes(&s, intermediate, 20);\r
+    SHA_Final(&s, hmac);\r
+}\r
+\r
+static void sha1_do_hmac(void *handle, unsigned char *blk, int len,\r
+                        unsigned long seq, unsigned char *hmac)\r
+{\r
+    unsigned char seqbuf[4];\r
+\r
+    seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF);\r
+    seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF);\r
+    seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF);\r
+    seqbuf[3] = (unsigned char) ((seq) & 0xFF);\r
+\r
+    hmacsha1_start(handle);\r
+    hmacsha1_bytes(handle, seqbuf, 4);\r
+    hmacsha1_bytes(handle, blk, len);\r
+    hmacsha1_genresult(handle, hmac);\r
+}\r
+\r
+static void sha1_generate(void *handle, unsigned char *blk, int len,\r
+                         unsigned long seq)\r
+{\r
+    sha1_do_hmac(handle, blk, len, seq, blk + len);\r
+}\r
+\r
+static int hmacsha1_verresult(void *handle, unsigned char const *hmac)\r
+{\r
+    unsigned char correct[20];\r
+    hmacsha1_genresult(handle, correct);\r
+    return !memcmp(correct, hmac, 20);\r
+}\r
+\r
+static int sha1_verify(void *handle, unsigned char *blk, int len,\r
+                      unsigned long seq)\r
+{\r
+    unsigned char correct[20];\r
+    sha1_do_hmac(handle, blk, len, seq, correct);\r
+    return !memcmp(correct, blk + len, 20);\r
+}\r
+\r
+static void hmacsha1_96_genresult(void *handle, unsigned char *hmac)\r
+{\r
+    unsigned char full[20];\r
+    hmacsha1_genresult(handle, full);\r
+    memcpy(hmac, full, 12);\r
+}\r
+\r
+static void sha1_96_generate(void *handle, unsigned char *blk, int len,\r
+                            unsigned long seq)\r
+{\r
+    unsigned char full[20];\r
+    sha1_do_hmac(handle, blk, len, seq, full);\r
+    memcpy(blk + len, full, 12);\r
+}\r
+\r
+static int hmacsha1_96_verresult(void *handle, unsigned char const *hmac)\r
+{\r
+    unsigned char correct[20];\r
+    hmacsha1_genresult(handle, correct);\r
+    return !memcmp(correct, hmac, 12);\r
+}\r
+\r
+static int sha1_96_verify(void *handle, unsigned char *blk, int len,\r
+                      unsigned long seq)\r
+{\r
+    unsigned char correct[20];\r
+    sha1_do_hmac(handle, blk, len, seq, correct);\r
+    return !memcmp(correct, blk + len, 12);\r
+}\r
+\r
+void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,\r
+                     unsigned char *output) {\r
+    SHA_State states[2];\r
+    unsigned char intermediate[20];\r
+\r
+    sha1_key_internal(states, key, keylen);\r
+    SHA_Bytes(&states[0], data, datalen);\r
+    SHA_Final(&states[0], intermediate);\r
+\r
+    SHA_Bytes(&states[1], intermediate, 20);\r
+    SHA_Final(&states[1], output);\r
+}\r
+\r
+const struct ssh_mac ssh_hmac_sha1 = {\r
+    sha1_make_context, sha1_free_context, sha1_key,\r
+    sha1_generate, sha1_verify,\r
+    hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,\r
+    "hmac-sha1",\r
+    20,\r
+    "HMAC-SHA1"\r
+};\r
+\r
+const struct ssh_mac ssh_hmac_sha1_96 = {\r
+    sha1_make_context, sha1_free_context, sha1_key,\r
+    sha1_96_generate, sha1_96_verify,\r
+    hmacsha1_start, hmacsha1_bytes,\r
+    hmacsha1_96_genresult, hmacsha1_96_verresult,\r
+    "hmac-sha1-96",\r
+    12,\r
+    "HMAC-SHA1-96"\r
+};\r
+\r
+const struct ssh_mac ssh_hmac_sha1_buggy = {\r
+    sha1_make_context, sha1_free_context, sha1_key_buggy,\r
+    sha1_generate, sha1_verify,\r
+    hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,\r
+    "hmac-sha1",\r
+    20,\r
+    "bug-compatible HMAC-SHA1"\r
+};\r
+\r
+const struct ssh_mac ssh_hmac_sha1_96_buggy = {\r
+    sha1_make_context, sha1_free_context, sha1_key_buggy,\r
+    sha1_96_generate, sha1_96_verify,\r
+    hmacsha1_start, hmacsha1_bytes,\r
+    hmacsha1_96_genresult, hmacsha1_96_verresult,\r
+    "hmac-sha1-96",\r
+    12,\r
+    "bug-compatible HMAC-SHA1-96"\r
+};\r
diff --git a/putty/SSHZLIB.C b/putty/SSHZLIB.C
new file mode 100644 (file)
index 0000000..9c780a4
--- /dev/null
@@ -0,0 +1,1385 @@
+/*\r
+ * Zlib (RFC1950 / RFC1951) compression for PuTTY.\r
+ * \r
+ * There will no doubt be criticism of my decision to reimplement\r
+ * Zlib compression from scratch instead of using the existing zlib\r
+ * code. People will cry `reinventing the wheel'; they'll claim\r
+ * that the `fundamental basis of OSS' is code reuse; they'll want\r
+ * to see a really good reason for me having chosen not to use the\r
+ * existing code.\r
+ * \r
+ * Well, here are my reasons. Firstly, I don't want to link the\r
+ * whole of zlib into the PuTTY binary; PuTTY is justifiably proud\r
+ * of its small size and I think zlib contains a lot of unnecessary\r
+ * baggage for the kind of compression that SSH requires.\r
+ * \r
+ * Secondly, I also don't like the alternative of using zlib.dll.\r
+ * Another thing PuTTY is justifiably proud of is its ease of\r
+ * installation, and the last thing I want to do is to start\r
+ * mandating DLLs. Not only that, but there are two _kinds_ of\r
+ * zlib.dll kicking around, one with C calling conventions on the\r
+ * exported functions and another with WINAPI conventions, and\r
+ * there would be a significant danger of getting the wrong one.\r
+ * \r
+ * Thirdly, there seems to be a difference of opinion on the IETF\r
+ * secsh mailing list about the correct way to round off a\r
+ * compressed packet and start the next. In particular, there's\r
+ * some talk of switching to a mechanism zlib isn't currently\r
+ * capable of supporting (see below for an explanation). Given that\r
+ * sort of uncertainty, I thought it might be better to have code\r
+ * that will support even the zlib-incompatible worst case.\r
+ * \r
+ * Fourthly, it's a _second implementation_. Second implementations\r
+ * are fundamentally a Good Thing in standardisation efforts. The\r
+ * difference of opinion mentioned above has arisen _precisely_\r
+ * because there has been only one zlib implementation and\r
+ * everybody has used it. I don't intend that this should happen\r
+ * again.\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+#ifdef ZLIB_STANDALONE\r
+\r
+/*\r
+ * This module also makes a handy zlib decoding tool for when\r
+ * you're picking apart Zip files or PDFs or PNGs. If you compile\r
+ * it with ZLIB_STANDALONE defined, it builds on its own and\r
+ * becomes a command-line utility.\r
+ * \r
+ * Therefore, here I provide a self-contained implementation of the\r
+ * macros required from the rest of the PuTTY sources.\r
+ */\r
+#define snew(type) ( (type *) malloc(sizeof(type)) )\r
+#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) )\r
+#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) )\r
+#define sfree(x) ( free((x)) )\r
+\r
+#else\r
+#include "ssh.h"\r
+#endif\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#define TRUE (!FALSE)\r
+#endif\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Basic LZ77 code. This bit is designed modularly, so it could be\r
+ * ripped out and used in a different LZ77 compressor. Go to it,\r
+ * and good luck :-)\r
+ */\r
+\r
+struct LZ77InternalContext;\r
+struct LZ77Context {\r
+    struct LZ77InternalContext *ictx;\r
+    void *userdata;\r
+    void (*literal) (struct LZ77Context * ctx, unsigned char c);\r
+    void (*match) (struct LZ77Context * ctx, int distance, int len);\r
+};\r
+\r
+/*\r
+ * Initialise the private fields of an LZ77Context. It's up to the\r
+ * user to initialise the public fields.\r
+ */\r
+static int lz77_init(struct LZ77Context *ctx);\r
+\r
+/*\r
+ * Supply data to be compressed. Will update the private fields of\r
+ * the LZ77Context, and will call literal() and match() to output.\r
+ * If `compress' is FALSE, it will never emit a match, but will\r
+ * instead call literal() for everything.\r
+ */\r
+static void lz77_compress(struct LZ77Context *ctx,\r
+                         unsigned char *data, int len, int compress);\r
+\r
+/*\r
+ * Modifiable parameters.\r
+ */\r
+#define WINSIZE 32768                 /* window size. Must be power of 2! */\r
+#define HASHMAX 2039                  /* one more than max hash value */\r
+#define MAXMATCH 32                   /* how many matches we track */\r
+#define HASHCHARS 3                   /* how many chars make a hash */\r
+\r
+/*\r
+ * This compressor takes a less slapdash approach than the\r
+ * gzip/zlib one. Rather than allowing our hash chains to fall into\r
+ * disuse near the far end, we keep them doubly linked so we can\r
+ * _find_ the far end, and then every time we add a new byte to the\r
+ * window (thus rolling round by one and removing the previous\r
+ * byte), we can carefully remove the hash chain entry.\r
+ */\r
+\r
+#define INVALID -1                    /* invalid hash _and_ invalid offset */\r
+struct WindowEntry {\r
+    short next, prev;                 /* array indices within the window */\r
+    short hashval;\r
+};\r
+\r
+struct HashEntry {\r
+    short first;                      /* window index of first in chain */\r
+};\r
+\r
+struct Match {\r
+    int distance, len;\r
+};\r
+\r
+struct LZ77InternalContext {\r
+    struct WindowEntry win[WINSIZE];\r
+    unsigned char data[WINSIZE];\r
+    int winpos;\r
+    struct HashEntry hashtab[HASHMAX];\r
+    unsigned char pending[HASHCHARS];\r
+    int npending;\r
+};\r
+\r
+static int lz77_hash(unsigned char *data)\r
+{\r
+    return (257 * data[0] + 263 * data[1] + 269 * data[2]) % HASHMAX;\r
+}\r
+\r
+static int lz77_init(struct LZ77Context *ctx)\r
+{\r
+    struct LZ77InternalContext *st;\r
+    int i;\r
+\r
+    st = snew(struct LZ77InternalContext);\r
+    if (!st)\r
+       return 0;\r
+\r
+    ctx->ictx = st;\r
+\r
+    for (i = 0; i < WINSIZE; i++)\r
+       st->win[i].next = st->win[i].prev = st->win[i].hashval = INVALID;\r
+    for (i = 0; i < HASHMAX; i++)\r
+       st->hashtab[i].first = INVALID;\r
+    st->winpos = 0;\r
+\r
+    st->npending = 0;\r
+\r
+    return 1;\r
+}\r
+\r
+static void lz77_advance(struct LZ77InternalContext *st,\r
+                        unsigned char c, int hash)\r
+{\r
+    int off;\r
+\r
+    /*\r
+     * Remove the hash entry at winpos from the tail of its chain,\r
+     * or empty the chain if it's the only thing on the chain.\r
+     */\r
+    if (st->win[st->winpos].prev != INVALID) {\r
+       st->win[st->win[st->winpos].prev].next = INVALID;\r
+    } else if (st->win[st->winpos].hashval != INVALID) {\r
+       st->hashtab[st->win[st->winpos].hashval].first = INVALID;\r
+    }\r
+\r
+    /*\r
+     * Create a new entry at winpos and add it to the head of its\r
+     * hash chain.\r
+     */\r
+    st->win[st->winpos].hashval = hash;\r
+    st->win[st->winpos].prev = INVALID;\r
+    off = st->win[st->winpos].next = st->hashtab[hash].first;\r
+    st->hashtab[hash].first = st->winpos;\r
+    if (off != INVALID)\r
+       st->win[off].prev = st->winpos;\r
+    st->data[st->winpos] = c;\r
+\r
+    /*\r
+     * Advance the window pointer.\r
+     */\r
+    st->winpos = (st->winpos + 1) & (WINSIZE - 1);\r
+}\r
+\r
+#define CHARAT(k) ( (k)<0 ? st->data[(st->winpos+k)&(WINSIZE-1)] : data[k] )\r
+\r
+static void lz77_compress(struct LZ77Context *ctx,\r
+                         unsigned char *data, int len, int compress)\r
+{\r
+    struct LZ77InternalContext *st = ctx->ictx;\r
+    int i, hash, distance, off, nmatch, matchlen, advance;\r
+    struct Match defermatch, matches[MAXMATCH];\r
+    int deferchr;\r
+\r
+    /*\r
+     * Add any pending characters from last time to the window. (We\r
+     * might not be able to.)\r
+     */\r
+    for (i = 0; i < st->npending; i++) {\r
+       unsigned char foo[HASHCHARS];\r
+       int j;\r
+       if (len + st->npending - i < HASHCHARS) {\r
+           /* Update the pending array. */\r
+           for (j = i; j < st->npending; j++)\r
+               st->pending[j - i] = st->pending[j];\r
+           break;\r
+       }\r
+       for (j = 0; j < HASHCHARS; j++)\r
+           foo[j] = (i + j < st->npending ? st->pending[i + j] :\r
+                     data[i + j - st->npending]);\r
+       lz77_advance(st, foo[0], lz77_hash(foo));\r
+    }\r
+    st->npending -= i;\r
+\r
+    defermatch.distance = 0; /* appease compiler */\r
+    defermatch.len = 0;\r
+    deferchr = '\0';\r
+    while (len > 0) {\r
+\r
+       /* Don't even look for a match, if we're not compressing. */\r
+       if (compress && len >= HASHCHARS) {\r
+           /*\r
+            * Hash the next few characters.\r
+            */\r
+           hash = lz77_hash(data);\r
+\r
+           /*\r
+            * Look the hash up in the corresponding hash chain and see\r
+            * what we can find.\r
+            */\r
+           nmatch = 0;\r
+           for (off = st->hashtab[hash].first;\r
+                off != INVALID; off = st->win[off].next) {\r
+               /* distance = 1       if off == st->winpos-1 */\r
+               /* distance = WINSIZE if off == st->winpos   */\r
+               distance =\r
+                   WINSIZE - (off + WINSIZE - st->winpos) % WINSIZE;\r
+               for (i = 0; i < HASHCHARS; i++)\r
+                   if (CHARAT(i) != CHARAT(i - distance))\r
+                       break;\r
+               if (i == HASHCHARS) {\r
+                   matches[nmatch].distance = distance;\r
+                   matches[nmatch].len = 3;\r
+                   if (++nmatch >= MAXMATCH)\r
+                       break;\r
+               }\r
+           }\r
+       } else {\r
+           nmatch = 0;\r
+           hash = INVALID;\r
+       }\r
+\r
+       if (nmatch > 0) {\r
+           /*\r
+            * We've now filled up matches[] with nmatch potential\r
+            * matches. Follow them down to find the longest. (We\r
+            * assume here that it's always worth favouring a\r
+            * longer match over a shorter one.)\r
+            */\r
+           matchlen = HASHCHARS;\r
+           while (matchlen < len) {\r
+               int j;\r
+               for (i = j = 0; i < nmatch; i++) {\r
+                   if (CHARAT(matchlen) ==\r
+                       CHARAT(matchlen - matches[i].distance)) {\r
+                       matches[j++] = matches[i];\r
+                   }\r
+               }\r
+               if (j == 0)\r
+                   break;\r
+               matchlen++;\r
+               nmatch = j;\r
+           }\r
+\r
+           /*\r
+            * We've now got all the longest matches. We favour the\r
+            * shorter distances, which means we go with matches[0].\r
+            * So see if we want to defer it or throw it away.\r
+            */\r
+           matches[0].len = matchlen;\r
+           if (defermatch.len > 0) {\r
+               if (matches[0].len > defermatch.len + 1) {\r
+                   /* We have a better match. Emit the deferred char,\r
+                    * and defer this match. */\r
+                   ctx->literal(ctx, (unsigned char) deferchr);\r
+                   defermatch = matches[0];\r
+                   deferchr = data[0];\r
+                   advance = 1;\r
+               } else {\r
+                   /* We don't have a better match. Do the deferred one. */\r
+                   ctx->match(ctx, defermatch.distance, defermatch.len);\r
+                   advance = defermatch.len - 1;\r
+                   defermatch.len = 0;\r
+               }\r
+           } else {\r
+               /* There was no deferred match. Defer this one. */\r
+               defermatch = matches[0];\r
+               deferchr = data[0];\r
+               advance = 1;\r
+           }\r
+       } else {\r
+           /*\r
+            * We found no matches. Emit the deferred match, if\r
+            * any; otherwise emit a literal.\r
+            */\r
+           if (defermatch.len > 0) {\r
+               ctx->match(ctx, defermatch.distance, defermatch.len);\r
+               advance = defermatch.len - 1;\r
+               defermatch.len = 0;\r
+           } else {\r
+               ctx->literal(ctx, data[0]);\r
+               advance = 1;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Now advance the position by `advance' characters,\r
+        * keeping the window and hash chains consistent.\r
+        */\r
+       while (advance > 0) {\r
+           if (len >= HASHCHARS) {\r
+               lz77_advance(st, *data, lz77_hash(data));\r
+           } else {\r
+               st->pending[st->npending++] = *data;\r
+           }\r
+           data++;\r
+           len--;\r
+           advance--;\r
+       }\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Zlib compression. We always use the static Huffman tree option.\r
+ * Mostly this is because it's hard to scan a block in advance to\r
+ * work out better trees; dynamic trees are great when you're\r
+ * compressing a large file under no significant time constraint,\r
+ * but when you're compressing little bits in real time, things get\r
+ * hairier.\r
+ * \r
+ * I suppose it's possible that I could compute Huffman trees based\r
+ * on the frequencies in the _previous_ block, as a sort of\r
+ * heuristic, but I'm not confident that the gain would balance out\r
+ * having to transmit the trees.\r
+ */\r
+\r
+struct Outbuf {\r
+    unsigned char *outbuf;\r
+    int outlen, outsize;\r
+    unsigned long outbits;\r
+    int noutbits;\r
+    int firstblock;\r
+    int comp_disabled;\r
+};\r
+\r
+static void outbits(struct Outbuf *out, unsigned long bits, int nbits)\r
+{\r
+    assert(out->noutbits + nbits <= 32);\r
+    out->outbits |= bits << out->noutbits;\r
+    out->noutbits += nbits;\r
+    while (out->noutbits >= 8) {\r
+       if (out->outlen >= out->outsize) {\r
+           out->outsize = out->outlen + 64;\r
+           out->outbuf = sresize(out->outbuf, out->outsize, unsigned char);\r
+       }\r
+       out->outbuf[out->outlen++] = (unsigned char) (out->outbits & 0xFF);\r
+       out->outbits >>= 8;\r
+       out->noutbits -= 8;\r
+    }\r
+}\r
+\r
+static const unsigned char mirrorbytes[256] = {\r
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,\r
+    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,\r
+    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,\r
+    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,\r
+    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,\r
+    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,\r
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,\r
+    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,\r
+    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,\r
+    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,\r
+    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,\r
+    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,\r
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,\r
+    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,\r
+    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,\r
+    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,\r
+    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,\r
+    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,\r
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,\r
+    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,\r
+    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,\r
+    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,\r
+    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,\r
+    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,\r
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,\r
+    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,\r
+    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,\r
+    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,\r
+    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,\r
+    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,\r
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,\r
+    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,\r
+};\r
+\r
+typedef struct {\r
+    short code, extrabits;\r
+    int min, max;\r
+} coderecord;\r
+\r
+static const coderecord lencodes[] = {\r
+    {257, 0, 3, 3},\r
+    {258, 0, 4, 4},\r
+    {259, 0, 5, 5},\r
+    {260, 0, 6, 6},\r
+    {261, 0, 7, 7},\r
+    {262, 0, 8, 8},\r
+    {263, 0, 9, 9},\r
+    {264, 0, 10, 10},\r
+    {265, 1, 11, 12},\r
+    {266, 1, 13, 14},\r
+    {267, 1, 15, 16},\r
+    {268, 1, 17, 18},\r
+    {269, 2, 19, 22},\r
+    {270, 2, 23, 26},\r
+    {271, 2, 27, 30},\r
+    {272, 2, 31, 34},\r
+    {273, 3, 35, 42},\r
+    {274, 3, 43, 50},\r
+    {275, 3, 51, 58},\r
+    {276, 3, 59, 66},\r
+    {277, 4, 67, 82},\r
+    {278, 4, 83, 98},\r
+    {279, 4, 99, 114},\r
+    {280, 4, 115, 130},\r
+    {281, 5, 131, 162},\r
+    {282, 5, 163, 194},\r
+    {283, 5, 195, 226},\r
+    {284, 5, 227, 257},\r
+    {285, 0, 258, 258},\r
+};\r
+\r
+static const coderecord distcodes[] = {\r
+    {0, 0, 1, 1},\r
+    {1, 0, 2, 2},\r
+    {2, 0, 3, 3},\r
+    {3, 0, 4, 4},\r
+    {4, 1, 5, 6},\r
+    {5, 1, 7, 8},\r
+    {6, 2, 9, 12},\r
+    {7, 2, 13, 16},\r
+    {8, 3, 17, 24},\r
+    {9, 3, 25, 32},\r
+    {10, 4, 33, 48},\r
+    {11, 4, 49, 64},\r
+    {12, 5, 65, 96},\r
+    {13, 5, 97, 128},\r
+    {14, 6, 129, 192},\r
+    {15, 6, 193, 256},\r
+    {16, 7, 257, 384},\r
+    {17, 7, 385, 512},\r
+    {18, 8, 513, 768},\r
+    {19, 8, 769, 1024},\r
+    {20, 9, 1025, 1536},\r
+    {21, 9, 1537, 2048},\r
+    {22, 10, 2049, 3072},\r
+    {23, 10, 3073, 4096},\r
+    {24, 11, 4097, 6144},\r
+    {25, 11, 6145, 8192},\r
+    {26, 12, 8193, 12288},\r
+    {27, 12, 12289, 16384},\r
+    {28, 13, 16385, 24576},\r
+    {29, 13, 24577, 32768},\r
+};\r
+\r
+static void zlib_literal(struct LZ77Context *ectx, unsigned char c)\r
+{\r
+    struct Outbuf *out = (struct Outbuf *) ectx->userdata;\r
+\r
+    if (out->comp_disabled) {\r
+       /*\r
+        * We're in an uncompressed block, so just output the byte.\r
+        */\r
+       outbits(out, c, 8);\r
+       return;\r
+    }\r
+\r
+    if (c <= 143) {\r
+       /* 0 through 143 are 8 bits long starting at 00110000. */\r
+       outbits(out, mirrorbytes[0x30 + c], 8);\r
+    } else {\r
+       /* 144 through 255 are 9 bits long starting at 110010000. */\r
+       outbits(out, 1 + 2 * mirrorbytes[0x90 - 144 + c], 9);\r
+    }\r
+}\r
+\r
+static void zlib_match(struct LZ77Context *ectx, int distance, int len)\r
+{\r
+    const coderecord *d, *l;\r
+    int i, j, k;\r
+    struct Outbuf *out = (struct Outbuf *) ectx->userdata;\r
+\r
+    assert(!out->comp_disabled);\r
+\r
+    while (len > 0) {\r
+       int thislen;\r
+\r
+       /*\r
+        * We can transmit matches of lengths 3 through 258\r
+        * inclusive. So if len exceeds 258, we must transmit in\r
+        * several steps, with 258 or less in each step.\r
+        * \r
+        * Specifically: if len >= 261, we can transmit 258 and be\r
+        * sure of having at least 3 left for the next step. And if\r
+        * len <= 258, we can just transmit len. But if len == 259\r
+        * or 260, we must transmit len-3.\r
+        */\r
+       thislen = (len > 260 ? 258 : len <= 258 ? len : len - 3);\r
+       len -= thislen;\r
+\r
+       /*\r
+        * Binary-search to find which length code we're\r
+        * transmitting.\r
+        */\r
+       i = -1;\r
+       j = sizeof(lencodes) / sizeof(*lencodes);\r
+       while (1) {\r
+           assert(j - i >= 2);\r
+           k = (j + i) / 2;\r
+           if (thislen < lencodes[k].min)\r
+               j = k;\r
+           else if (thislen > lencodes[k].max)\r
+               i = k;\r
+           else {\r
+               l = &lencodes[k];\r
+               break;                 /* found it! */\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Transmit the length code. 256-279 are seven bits\r
+        * starting at 0000000; 280-287 are eight bits starting at\r
+        * 11000000.\r
+        */\r
+       if (l->code <= 279) {\r
+           outbits(out, mirrorbytes[(l->code - 256) * 2], 7);\r
+       } else {\r
+           outbits(out, mirrorbytes[0xc0 - 280 + l->code], 8);\r
+       }\r
+\r
+       /*\r
+        * Transmit the extra bits.\r
+        */\r
+       if (l->extrabits)\r
+           outbits(out, thislen - l->min, l->extrabits);\r
+\r
+       /*\r
+        * Binary-search to find which distance code we're\r
+        * transmitting.\r
+        */\r
+       i = -1;\r
+       j = sizeof(distcodes) / sizeof(*distcodes);\r
+       while (1) {\r
+           assert(j - i >= 2);\r
+           k = (j + i) / 2;\r
+           if (distance < distcodes[k].min)\r
+               j = k;\r
+           else if (distance > distcodes[k].max)\r
+               i = k;\r
+           else {\r
+               d = &distcodes[k];\r
+               break;                 /* found it! */\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Transmit the distance code. Five bits starting at 00000.\r
+        */\r
+       outbits(out, mirrorbytes[d->code * 8], 5);\r
+\r
+       /*\r
+        * Transmit the extra bits.\r
+        */\r
+       if (d->extrabits)\r
+           outbits(out, distance - d->min, d->extrabits);\r
+    }\r
+}\r
+\r
+void *zlib_compress_init(void)\r
+{\r
+    struct Outbuf *out;\r
+    struct LZ77Context *ectx = snew(struct LZ77Context);\r
+\r
+    lz77_init(ectx);\r
+    ectx->literal = zlib_literal;\r
+    ectx->match = zlib_match;\r
+\r
+    out = snew(struct Outbuf);\r
+    out->outbits = out->noutbits = 0;\r
+    out->firstblock = 1;\r
+    out->comp_disabled = FALSE;\r
+    ectx->userdata = out;\r
+\r
+    return ectx;\r
+}\r
+\r
+void zlib_compress_cleanup(void *handle)\r
+{\r
+    struct LZ77Context *ectx = (struct LZ77Context *)handle;\r
+    sfree(ectx->userdata);\r
+    sfree(ectx->ictx);\r
+    sfree(ectx);\r
+}\r
+\r
+/*\r
+ * Turn off actual LZ77 analysis for one block, to facilitate\r
+ * construction of a precise-length IGNORE packet. Returns the\r
+ * length adjustment (which is only valid for packets < 65536\r
+ * bytes, but that seems reasonable enough).\r
+ */\r
+static int zlib_disable_compression(void *handle)\r
+{\r
+    struct LZ77Context *ectx = (struct LZ77Context *)handle;\r
+    struct Outbuf *out = (struct Outbuf *) ectx->userdata;\r
+    int n;\r
+\r
+    out->comp_disabled = TRUE;\r
+\r
+    n = 0;\r
+    /*\r
+     * If this is the first block, we will start by outputting two\r
+     * header bytes, and then three bits to begin an uncompressed\r
+     * block. This will cost three bytes (because we will start on\r
+     * a byte boundary, this is certain).\r
+     */\r
+    if (out->firstblock) {\r
+       n = 3;\r
+    } else {\r
+       /*\r
+        * Otherwise, we will output seven bits to close the\r
+        * previous static block, and _then_ three bits to begin an\r
+        * uncompressed block, and then flush the current byte.\r
+        * This may cost two bytes or three, depending on noutbits.\r
+        */\r
+       n += (out->noutbits + 10) / 8;\r
+    }\r
+\r
+    /*\r
+     * Now we output four bytes for the length / ~length pair in\r
+     * the uncompressed block.\r
+     */\r
+    n += 4;\r
+\r
+    return n;\r
+}\r
+\r
+int zlib_compress_block(void *handle, unsigned char *block, int len,\r
+                       unsigned char **outblock, int *outlen)\r
+{\r
+    struct LZ77Context *ectx = (struct LZ77Context *)handle;\r
+    struct Outbuf *out = (struct Outbuf *) ectx->userdata;\r
+    int in_block;\r
+\r
+    out->outbuf = NULL;\r
+    out->outlen = out->outsize = 0;\r
+\r
+    /*\r
+     * If this is the first block, output the Zlib (RFC1950) header\r
+     * bytes 78 9C. (Deflate compression, 32K window size, default\r
+     * algorithm.)\r
+     */\r
+    if (out->firstblock) {\r
+       outbits(out, 0x9C78, 16);\r
+       out->firstblock = 0;\r
+\r
+       in_block = FALSE;\r
+    } else\r
+       in_block = TRUE;\r
+\r
+    if (out->comp_disabled) {\r
+       if (in_block)\r
+           outbits(out, 0, 7);        /* close static block */\r
+\r
+       while (len > 0) {\r
+           int blen = (len < 65535 ? len : 65535);\r
+\r
+           /*\r
+            * Start a Deflate (RFC1951) uncompressed block. We\r
+            * transmit a zero bit (BFINAL=0), followed by two more\r
+            * zero bits (BTYPE=00). Of course these are in the\r
+            * wrong order (00 0), not that it matters.\r
+            */\r
+           outbits(out, 0, 3);\r
+\r
+           /*\r
+            * Output zero bits to align to a byte boundary.\r
+            */\r
+           if (out->noutbits)\r
+               outbits(out, 0, 8 - out->noutbits);\r
+\r
+           /*\r
+            * Output the block length, and then its one's\r
+            * complement. They're little-endian, so all we need to\r
+            * do is pass them straight to outbits() with bit count\r
+            * 16.\r
+            */\r
+           outbits(out, blen, 16);\r
+           outbits(out, blen ^ 0xFFFF, 16);\r
+\r
+           /*\r
+            * Do the `compression': we need to pass the data to\r
+            * lz77_compress so that it will be taken into account\r
+            * for subsequent (distance,length) pairs. But\r
+            * lz77_compress is passed FALSE, which means it won't\r
+            * actually find (or even look for) any matches; so\r
+            * every character will be passed straight to\r
+            * zlib_literal which will spot out->comp_disabled and\r
+            * emit in the uncompressed format.\r
+            */\r
+           lz77_compress(ectx, block, blen, FALSE);\r
+\r
+           len -= blen;\r
+           block += blen;\r
+       }\r
+       outbits(out, 2, 3);            /* open new block */\r
+    } else {\r
+       if (!in_block) {\r
+           /*\r
+            * Start a Deflate (RFC1951) fixed-trees block. We\r
+            * transmit a zero bit (BFINAL=0), followed by a zero\r
+            * bit and a one bit (BTYPE=01). Of course these are in\r
+            * the wrong order (01 0).\r
+            */\r
+           outbits(out, 2, 3);\r
+       }\r
+\r
+       /*\r
+        * Do the compression.\r
+        */\r
+       lz77_compress(ectx, block, len, TRUE);\r
+\r
+       /*\r
+        * End the block (by transmitting code 256, which is\r
+        * 0000000 in fixed-tree mode), and transmit some empty\r
+        * blocks to ensure we have emitted the byte containing the\r
+        * last piece of genuine data. There are three ways we can\r
+        * do this:\r
+        *\r
+        *  - Minimal flush. Output end-of-block and then open a\r
+        *    new static block. This takes 9 bits, which is\r
+        *    guaranteed to flush out the last genuine code in the\r
+        *    closed block; but allegedly zlib can't handle it.\r
+        *\r
+        *  - Zlib partial flush. Output EOB, open and close an\r
+        *    empty static block, and _then_ open the new block.\r
+        *    This is the best zlib can handle.\r
+        *\r
+        *  - Zlib sync flush. Output EOB, then an empty\r
+        *    _uncompressed_ block (000, then sync to byte\r
+        *    boundary, then send bytes 00 00 FF FF). Then open the\r
+        *    new block.\r
+        *\r
+        * For the moment, we will use Zlib partial flush.\r
+        */\r
+       outbits(out, 0, 7);            /* close block */\r
+       outbits(out, 2, 3 + 7);        /* empty static block */\r
+       outbits(out, 2, 3);            /* open new block */\r
+    }\r
+\r
+    out->comp_disabled = FALSE;\r
+\r
+    *outblock = out->outbuf;\r
+    *outlen = out->outlen;\r
+\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Zlib decompression. Of course, even though our compressor always\r
+ * uses static trees, our _decompressor_ has to be capable of\r
+ * handling dynamic trees if it sees them.\r
+ */\r
+\r
+/*\r
+ * The way we work the Huffman decode is to have a table lookup on\r
+ * the first N bits of the input stream (in the order they arrive,\r
+ * of course, i.e. the first bit of the Huffman code is in bit 0).\r
+ * Each table entry lists the number of bits to consume, plus\r
+ * either an output code or a pointer to a secondary table.\r
+ */\r
+struct zlib_table;\r
+struct zlib_tableentry;\r
+\r
+struct zlib_tableentry {\r
+    unsigned char nbits;\r
+    short code;\r
+    struct zlib_table *nexttable;\r
+};\r
+\r
+struct zlib_table {\r
+    int mask;                         /* mask applied to input bit stream */\r
+    struct zlib_tableentry *table;\r
+};\r
+\r
+#define MAXCODELEN 16\r
+#define MAXSYMS 288\r
+\r
+/*\r
+ * Build a single-level decode table for elements\r
+ * [minlength,maxlength) of the provided code/length tables, and\r
+ * recurse to build subtables.\r
+ */\r
+static struct zlib_table *zlib_mkonetab(int *codes, unsigned char *lengths,\r
+                                       int nsyms,\r
+                                       int pfx, int pfxbits, int bits)\r
+{\r
+    struct zlib_table *tab = snew(struct zlib_table);\r
+    int pfxmask = (1 << pfxbits) - 1;\r
+    int nbits, i, j, code;\r
+\r
+    tab->table = snewn(1 << bits, struct zlib_tableentry);\r
+    tab->mask = (1 << bits) - 1;\r
+\r
+    for (code = 0; code <= tab->mask; code++) {\r
+       tab->table[code].code = -1;\r
+       tab->table[code].nbits = 0;\r
+       tab->table[code].nexttable = NULL;\r
+    }\r
+\r
+    for (i = 0; i < nsyms; i++) {\r
+       if (lengths[i] <= pfxbits || (codes[i] & pfxmask) != pfx)\r
+           continue;\r
+       code = (codes[i] >> pfxbits) & tab->mask;\r
+       for (j = code; j <= tab->mask; j += 1 << (lengths[i] - pfxbits)) {\r
+           tab->table[j].code = i;\r
+           nbits = lengths[i] - pfxbits;\r
+           if (tab->table[j].nbits < nbits)\r
+               tab->table[j].nbits = nbits;\r
+       }\r
+    }\r
+    for (code = 0; code <= tab->mask; code++) {\r
+       if (tab->table[code].nbits <= bits)\r
+           continue;\r
+       /* Generate a subtable. */\r
+       tab->table[code].code = -1;\r
+       nbits = tab->table[code].nbits - bits;\r
+       if (nbits > 7)\r
+           nbits = 7;\r
+       tab->table[code].nbits = bits;\r
+       tab->table[code].nexttable = zlib_mkonetab(codes, lengths, nsyms,\r
+                                                  pfx | (code << pfxbits),\r
+                                                  pfxbits + bits, nbits);\r
+    }\r
+\r
+    return tab;\r
+}\r
+\r
+/*\r
+ * Build a decode table, given a set of Huffman tree lengths.\r
+ */\r
+static struct zlib_table *zlib_mktable(unsigned char *lengths,\r
+                                      int nlengths)\r
+{\r
+    int count[MAXCODELEN], startcode[MAXCODELEN], codes[MAXSYMS];\r
+    int code, maxlen;\r
+    int i, j;\r
+\r
+    /* Count the codes of each length. */\r
+    maxlen = 0;\r
+    for (i = 1; i < MAXCODELEN; i++)\r
+       count[i] = 0;\r
+    for (i = 0; i < nlengths; i++) {\r
+       count[lengths[i]]++;\r
+       if (maxlen < lengths[i])\r
+           maxlen = lengths[i];\r
+    }\r
+    /* Determine the starting code for each length block. */\r
+    code = 0;\r
+    for (i = 1; i < MAXCODELEN; i++) {\r
+       startcode[i] = code;\r
+       code += count[i];\r
+       code <<= 1;\r
+    }\r
+    /* Determine the code for each symbol. Mirrored, of course. */\r
+    for (i = 0; i < nlengths; i++) {\r
+       code = startcode[lengths[i]]++;\r
+       codes[i] = 0;\r
+       for (j = 0; j < lengths[i]; j++) {\r
+           codes[i] = (codes[i] << 1) | (code & 1);\r
+           code >>= 1;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Now we have the complete list of Huffman codes. Build a\r
+     * table.\r
+     */\r
+    return zlib_mkonetab(codes, lengths, nlengths, 0, 0,\r
+                        maxlen < 9 ? maxlen : 9);\r
+}\r
+\r
+static int zlib_freetable(struct zlib_table **ztab)\r
+{\r
+    struct zlib_table *tab;\r
+    int code;\r
+\r
+    if (ztab == NULL)\r
+       return -1;\r
+\r
+    if (*ztab == NULL)\r
+       return 0;\r
+\r
+    tab = *ztab;\r
+\r
+    for (code = 0; code <= tab->mask; code++)\r
+       if (tab->table[code].nexttable != NULL)\r
+           zlib_freetable(&tab->table[code].nexttable);\r
+\r
+    sfree(tab->table);\r
+    tab->table = NULL;\r
+\r
+    sfree(tab);\r
+    *ztab = NULL;\r
+\r
+    return (0);\r
+}\r
+\r
+struct zlib_decompress_ctx {\r
+    struct zlib_table *staticlentable, *staticdisttable;\r
+    struct zlib_table *currlentable, *currdisttable, *lenlentable;\r
+    enum {\r
+       START, OUTSIDEBLK,\r
+       TREES_HDR, TREES_LENLEN, TREES_LEN, TREES_LENREP,\r
+       INBLK, GOTLENSYM, GOTLEN, GOTDISTSYM,\r
+       UNCOMP_LEN, UNCOMP_NLEN, UNCOMP_DATA\r
+    } state;\r
+    int sym, hlit, hdist, hclen, lenptr, lenextrabits, lenaddon, len,\r
+       lenrep;\r
+    int uncomplen;\r
+    unsigned char lenlen[19];\r
+    unsigned char lengths[286 + 32];\r
+    unsigned long bits;\r
+    int nbits;\r
+    unsigned char window[WINSIZE];\r
+    int winpos;\r
+    unsigned char *outblk;\r
+    int outlen, outsize;\r
+};\r
+\r
+void *zlib_decompress_init(void)\r
+{\r
+    struct zlib_decompress_ctx *dctx = snew(struct zlib_decompress_ctx);\r
+    unsigned char lengths[288];\r
+\r
+    memset(lengths, 8, 144);\r
+    memset(lengths + 144, 9, 256 - 144);\r
+    memset(lengths + 256, 7, 280 - 256);\r
+    memset(lengths + 280, 8, 288 - 280);\r
+    dctx->staticlentable = zlib_mktable(lengths, 288);\r
+    memset(lengths, 5, 32);\r
+    dctx->staticdisttable = zlib_mktable(lengths, 32);\r
+    dctx->state = START;                      /* even before header */\r
+    dctx->currlentable = dctx->currdisttable = dctx->lenlentable = NULL;\r
+    dctx->bits = 0;\r
+    dctx->nbits = 0;\r
+    dctx->winpos = 0;\r
+\r
+    return dctx;\r
+}\r
+\r
+void zlib_decompress_cleanup(void *handle)\r
+{\r
+    struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle;\r
+\r
+    if (dctx->currlentable && dctx->currlentable != dctx->staticlentable)\r
+       zlib_freetable(&dctx->currlentable);\r
+    if (dctx->currdisttable && dctx->currdisttable != dctx->staticdisttable)\r
+       zlib_freetable(&dctx->currdisttable);\r
+    if (dctx->lenlentable)\r
+       zlib_freetable(&dctx->lenlentable);\r
+    zlib_freetable(&dctx->staticlentable);\r
+    zlib_freetable(&dctx->staticdisttable);\r
+    sfree(dctx);\r
+}\r
+\r
+static int zlib_huflookup(unsigned long *bitsp, int *nbitsp,\r
+                  struct zlib_table *tab)\r
+{\r
+    unsigned long bits = *bitsp;\r
+    int nbits = *nbitsp;\r
+    while (1) {\r
+       struct zlib_tableentry *ent;\r
+       ent = &tab->table[bits & tab->mask];\r
+       if (ent->nbits > nbits)\r
+           return -1;                 /* not enough data */\r
+       bits >>= ent->nbits;\r
+       nbits -= ent->nbits;\r
+       if (ent->code == -1)\r
+           tab = ent->nexttable;\r
+       else {\r
+           *bitsp = bits;\r
+           *nbitsp = nbits;\r
+           return ent->code;\r
+       }\r
+\r
+       if (!tab) {\r
+           /*\r
+            * There was a missing entry in the table, presumably\r
+            * due to an invalid Huffman table description, and the\r
+            * subsequent data has attempted to use the missing\r
+            * entry. Return a decoding failure.\r
+            */\r
+           return -2;\r
+       }\r
+    }\r
+}\r
+\r
+static void zlib_emit_char(struct zlib_decompress_ctx *dctx, int c)\r
+{\r
+    dctx->window[dctx->winpos] = c;\r
+    dctx->winpos = (dctx->winpos + 1) & (WINSIZE - 1);\r
+    if (dctx->outlen >= dctx->outsize) {\r
+       dctx->outsize = dctx->outlen + 512;\r
+       dctx->outblk = sresize(dctx->outblk, dctx->outsize, unsigned char);\r
+    }\r
+    dctx->outblk[dctx->outlen++] = c;\r
+}\r
+\r
+#define EATBITS(n) ( dctx->nbits -= (n), dctx->bits >>= (n) )\r
+\r
+int zlib_decompress_block(void *handle, unsigned char *block, int len,\r
+                         unsigned char **outblock, int *outlen)\r
+{\r
+    struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle;\r
+    const coderecord *rec;\r
+    int code, blktype, rep, dist, nlen, header;\r
+    static const unsigned char lenlenmap[] = {\r
+       16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15\r
+    };\r
+\r
+    dctx->outblk = snewn(256, unsigned char);\r
+    dctx->outsize = 256;\r
+    dctx->outlen = 0;\r
+\r
+    while (len > 0 || dctx->nbits > 0) {\r
+       while (dctx->nbits < 24 && len > 0) {\r
+           dctx->bits |= (*block++) << dctx->nbits;\r
+           dctx->nbits += 8;\r
+           len--;\r
+       }\r
+       switch (dctx->state) {\r
+         case START:\r
+           /* Expect 16-bit zlib header. */\r
+           if (dctx->nbits < 16)\r
+               goto finished;         /* done all we can */\r
+\r
+            /*\r
+             * The header is stored as a big-endian 16-bit integer,\r
+             * in contrast to the general little-endian policy in\r
+             * the rest of the format :-(\r
+             */\r
+            header = (((dctx->bits & 0xFF00) >> 8) |\r
+                      ((dctx->bits & 0x00FF) << 8));\r
+            EATBITS(16);\r
+\r
+            /*\r
+             * Check the header:\r
+             *\r
+             *  - bits 8-11 should be 1000 (Deflate/RFC1951)\r
+             *  - bits 12-15 should be at most 0111 (window size)\r
+             *  - bit 5 should be zero (no dictionary present)\r
+             *  - we don't care about bits 6-7 (compression rate)\r
+             *  - bits 0-4 should be set up to make the whole thing\r
+             *    a multiple of 31 (checksum).\r
+             */\r
+            if ((header & 0x0F00) != 0x0800 ||\r
+                (header & 0xF000) >  0x7000 ||\r
+                (header & 0x0020) != 0x0000 ||\r
+                (header % 31) != 0)\r
+                goto decode_error;\r
+\r
+           dctx->state = OUTSIDEBLK;\r
+           break;\r
+         case OUTSIDEBLK:\r
+           /* Expect 3-bit block header. */\r
+           if (dctx->nbits < 3)\r
+               goto finished;         /* done all we can */\r
+           EATBITS(1);\r
+           blktype = dctx->bits & 3;\r
+           EATBITS(2);\r
+           if (blktype == 0) {\r
+               int to_eat = dctx->nbits & 7;\r
+               dctx->state = UNCOMP_LEN;\r
+               EATBITS(to_eat);       /* align to byte boundary */\r
+           } else if (blktype == 1) {\r
+               dctx->currlentable = dctx->staticlentable;\r
+               dctx->currdisttable = dctx->staticdisttable;\r
+               dctx->state = INBLK;\r
+           } else if (blktype == 2) {\r
+               dctx->state = TREES_HDR;\r
+           }\r
+           break;\r
+         case TREES_HDR:\r
+           /*\r
+            * Dynamic block header. Five bits of HLIT, five of\r
+            * HDIST, four of HCLEN.\r
+            */\r
+           if (dctx->nbits < 5 + 5 + 4)\r
+               goto finished;         /* done all we can */\r
+           dctx->hlit = 257 + (dctx->bits & 31);\r
+           EATBITS(5);\r
+           dctx->hdist = 1 + (dctx->bits & 31);\r
+           EATBITS(5);\r
+           dctx->hclen = 4 + (dctx->bits & 15);\r
+           EATBITS(4);\r
+           dctx->lenptr = 0;\r
+           dctx->state = TREES_LENLEN;\r
+           memset(dctx->lenlen, 0, sizeof(dctx->lenlen));\r
+           break;\r
+         case TREES_LENLEN:\r
+           if (dctx->nbits < 3)\r
+               goto finished;\r
+           while (dctx->lenptr < dctx->hclen && dctx->nbits >= 3) {\r
+               dctx->lenlen[lenlenmap[dctx->lenptr++]] =\r
+                   (unsigned char) (dctx->bits & 7);\r
+               EATBITS(3);\r
+           }\r
+           if (dctx->lenptr == dctx->hclen) {\r
+               dctx->lenlentable = zlib_mktable(dctx->lenlen, 19);\r
+               dctx->state = TREES_LEN;\r
+               dctx->lenptr = 0;\r
+           }\r
+           break;\r
+         case TREES_LEN:\r
+           if (dctx->lenptr >= dctx->hlit + dctx->hdist) {\r
+               dctx->currlentable = zlib_mktable(dctx->lengths, dctx->hlit);\r
+               dctx->currdisttable = zlib_mktable(dctx->lengths + dctx->hlit,\r
+                                                 dctx->hdist);\r
+               zlib_freetable(&dctx->lenlentable);\r
+               dctx->lenlentable = NULL;\r
+               dctx->state = INBLK;\r
+               break;\r
+           }\r
+           code =\r
+               zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->lenlentable);\r
+           if (code == -1)\r
+               goto finished;\r
+           if (code == -2)\r
+               goto decode_error;\r
+           if (code < 16)\r
+               dctx->lengths[dctx->lenptr++] = code;\r
+           else {\r
+               dctx->lenextrabits = (code == 16 ? 2 : code == 17 ? 3 : 7);\r
+               dctx->lenaddon = (code == 18 ? 11 : 3);\r
+               dctx->lenrep = (code == 16 && dctx->lenptr > 0 ?\r
+                              dctx->lengths[dctx->lenptr - 1] : 0);\r
+               dctx->state = TREES_LENREP;\r
+           }\r
+           break;\r
+         case TREES_LENREP:\r
+           if (dctx->nbits < dctx->lenextrabits)\r
+               goto finished;\r
+           rep =\r
+               dctx->lenaddon +\r
+               (dctx->bits & ((1 << dctx->lenextrabits) - 1));\r
+           EATBITS(dctx->lenextrabits);\r
+           while (rep > 0 && dctx->lenptr < dctx->hlit + dctx->hdist) {\r
+               dctx->lengths[dctx->lenptr] = dctx->lenrep;\r
+               dctx->lenptr++;\r
+               rep--;\r
+           }\r
+           dctx->state = TREES_LEN;\r
+           break;\r
+         case INBLK:\r
+           code =\r
+               zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->currlentable);\r
+           if (code == -1)\r
+               goto finished;\r
+           if (code == -2)\r
+               goto decode_error;\r
+           if (code < 256)\r
+               zlib_emit_char(dctx, code);\r
+           else if (code == 256) {\r
+               dctx->state = OUTSIDEBLK;\r
+               if (dctx->currlentable != dctx->staticlentable) {\r
+                   zlib_freetable(&dctx->currlentable);\r
+                   dctx->currlentable = NULL;\r
+               }\r
+               if (dctx->currdisttable != dctx->staticdisttable) {\r
+                   zlib_freetable(&dctx->currdisttable);\r
+                   dctx->currdisttable = NULL;\r
+               }\r
+           } else if (code < 286) {   /* static tree can give >285; ignore */\r
+               dctx->state = GOTLENSYM;\r
+               dctx->sym = code;\r
+           }\r
+           break;\r
+         case GOTLENSYM:\r
+           rec = &lencodes[dctx->sym - 257];\r
+           if (dctx->nbits < rec->extrabits)\r
+               goto finished;\r
+           dctx->len =\r
+               rec->min + (dctx->bits & ((1 << rec->extrabits) - 1));\r
+           EATBITS(rec->extrabits);\r
+           dctx->state = GOTLEN;\r
+           break;\r
+         case GOTLEN:\r
+           code =\r
+               zlib_huflookup(&dctx->bits, &dctx->nbits,\r
+                              dctx->currdisttable);\r
+           if (code == -1)\r
+               goto finished;\r
+           if (code == -2)\r
+               goto decode_error;\r
+           dctx->state = GOTDISTSYM;\r
+           dctx->sym = code;\r
+           break;\r
+         case GOTDISTSYM:\r
+           rec = &distcodes[dctx->sym];\r
+           if (dctx->nbits < rec->extrabits)\r
+               goto finished;\r
+           dist = rec->min + (dctx->bits & ((1 << rec->extrabits) - 1));\r
+           EATBITS(rec->extrabits);\r
+           dctx->state = INBLK;\r
+           while (dctx->len--)\r
+               zlib_emit_char(dctx, dctx->window[(dctx->winpos - dist) &\r
+                                                 (WINSIZE - 1)]);\r
+           break;\r
+         case UNCOMP_LEN:\r
+           /*\r
+            * Uncompressed block. We expect to see a 16-bit LEN.\r
+            */\r
+           if (dctx->nbits < 16)\r
+               goto finished;\r
+           dctx->uncomplen = dctx->bits & 0xFFFF;\r
+           EATBITS(16);\r
+           dctx->state = UNCOMP_NLEN;\r
+           break;\r
+         case UNCOMP_NLEN:\r
+           /*\r
+            * Uncompressed block. We expect to see a 16-bit NLEN,\r
+            * which should be the one's complement of the previous\r
+            * LEN.\r
+            */\r
+           if (dctx->nbits < 16)\r
+               goto finished;\r
+           nlen = dctx->bits & 0xFFFF;\r
+           EATBITS(16);\r
+           if (dctx->uncomplen != (nlen ^ 0xFFFF))\r
+               goto decode_error;\r
+           if (dctx->uncomplen == 0)\r
+               dctx->state = OUTSIDEBLK;       /* block is empty */\r
+           else\r
+               dctx->state = UNCOMP_DATA;\r
+           break;\r
+         case UNCOMP_DATA:\r
+           if (dctx->nbits < 8)\r
+               goto finished;\r
+           zlib_emit_char(dctx, dctx->bits & 0xFF);\r
+           EATBITS(8);\r
+           if (--dctx->uncomplen == 0)\r
+               dctx->state = OUTSIDEBLK;       /* end of uncompressed block */\r
+           break;\r
+       }\r
+    }\r
+\r
+  finished:\r
+    *outblock = dctx->outblk;\r
+    *outlen = dctx->outlen;\r
+    return 1;\r
+\r
+  decode_error:\r
+    sfree(dctx->outblk);\r
+    *outblock = dctx->outblk = NULL;\r
+    *outlen = 0;\r
+    return 0;\r
+}\r
+\r
+#ifdef ZLIB_STANDALONE\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    unsigned char buf[16], *outbuf;\r
+    int ret, outlen;\r
+    void *handle;\r
+    int noheader = FALSE, opts = TRUE;\r
+    char *filename = NULL;\r
+    FILE *fp;\r
+\r
+    while (--argc) {\r
+        char *p = *++argv;\r
+\r
+        if (p[0] == '-' && opts) {\r
+            if (!strcmp(p, "-d"))\r
+                noheader = TRUE;\r
+            else if (!strcmp(p, "--"))\r
+                opts = FALSE;          /* next thing is filename */\r
+            else {\r
+                fprintf(stderr, "unknown command line option '%s'\n", p);\r
+                return 1;\r
+            }\r
+        } else if (!filename) {\r
+            filename = p;\r
+        } else {\r
+            fprintf(stderr, "can only handle one filename\n");\r
+            return 1;\r
+        }\r
+    }\r
+\r
+    handle = zlib_decompress_init();\r
+\r
+    if (noheader) {\r
+        /*\r
+         * Provide missing zlib header if -d was specified.\r
+         */\r
+        zlib_decompress_block(handle, "\x78\x9C", 2, &outbuf, &outlen);\r
+        assert(outlen == 0);\r
+    }\r
+\r
+    if (filename)\r
+        fp = fopen(filename, "rb");\r
+    else\r
+        fp = stdin;\r
+\r
+    if (!fp) {\r
+        assert(filename);\r
+        fprintf(stderr, "unable to open '%s'\n", filename);\r
+        return 1;\r
+    }\r
+\r
+    while (1) {\r
+       ret = fread(buf, 1, sizeof(buf), fp);\r
+       if (ret <= 0)\r
+           break;\r
+       zlib_decompress_block(handle, buf, ret, &outbuf, &outlen);\r
+        if (outbuf) {\r
+            if (outlen)\r
+                fwrite(outbuf, 1, outlen, stdout);\r
+            sfree(outbuf);\r
+        } else {\r
+            fprintf(stderr, "decoding error\n");\r
+            return 1;\r
+        }\r
+    }\r
+\r
+    zlib_decompress_cleanup(handle);\r
+\r
+    if (filename)\r
+        fclose(fp);\r
+\r
+    return 0;\r
+}\r
+\r
+#else\r
+\r
+const struct ssh_compress ssh_zlib = {\r
+    "zlib",\r
+    "zlib@openssh.com", /* delayed version */\r
+    zlib_compress_init,\r
+    zlib_compress_cleanup,\r
+    zlib_compress_block,\r
+    zlib_decompress_init,\r
+    zlib_decompress_cleanup,\r
+    zlib_decompress_block,\r
+    zlib_disable_compression,\r
+    "zlib (RFC1950)"\r
+};\r
+\r
+#endif\r
diff --git a/putty/STORAGE.H b/putty/STORAGE.H
new file mode 100644 (file)
index 0000000..0e0a7c0
--- /dev/null
@@ -0,0 +1,115 @@
+/*\r
+ * storage.h: interface defining functions for storage and recovery\r
+ * of PuTTY's persistent data.\r
+ */\r
+\r
+#ifndef PUTTY_STORAGE_H\r
+#define PUTTY_STORAGE_H\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Functions to save and restore PuTTY sessions. Note that this is\r
+ * only the low-level code to do the reading and writing. The\r
+ * higher-level code that translates a Config structure into a set\r
+ * of (key,value) pairs is elsewhere, since it doesn't (mostly)\r
+ * change between platforms.\r
+ */\r
+\r
+/*\r
+ * Write a saved session. The caller is expected to call\r
+ * open_setting_w() to get a `void *' handle, then pass that to a\r
+ * number of calls to write_setting_s() and write_setting_i(), and\r
+ * then close it using close_settings_w(). At the end of this call\r
+ * sequence the settings should have been written to the PuTTY\r
+ * persistent storage area.\r
+ *\r
+ * A given key will be written at most once while saving a session.\r
+ * Keys may be up to 255 characters long.  String values have no length\r
+ * limit.\r
+ * \r
+ * Any returned error message must be freed after use.\r
+ */\r
+void *open_settings_w(const char *sessionname, char **errmsg);\r
+void write_setting_s(void *handle, const char *key, const char *value);\r
+void write_setting_i(void *handle, const char *key, int value);\r
+void write_setting_filename(void *handle, const char *key, Filename value);\r
+void write_setting_fontspec(void *handle, const char *key, FontSpec font);\r
+void close_settings_w(void *handle);\r
+\r
+/*\r
+ * Read a saved session. The caller is expected to call\r
+ * open_setting_r() to get a `void *' handle, then pass that to a\r
+ * number of calls to read_setting_s() and read_setting_i(), and\r
+ * then close it using close_settings_r().\r
+ * \r
+ * read_setting_s() writes into the provided buffer and returns a\r
+ * pointer to the same buffer.\r
+ * \r
+ * If a particular string setting is not present in the session,\r
+ * read_setting_s() can return NULL, in which case the caller\r
+ * should invent a sensible default. If an integer setting is not\r
+ * present, read_setting_i() returns its provided default.\r
+ * \r
+ * read_setting_filename() and read_setting_fontspec() each read into\r
+ * the provided buffer, and return zero if they failed to.\r
+ */\r
+void *open_settings_r(const char *sessionname);\r
+char *read_setting_s(void *handle, const char *key, char *buffer, int buflen);\r
+int read_setting_i(void *handle, const char *key, int defvalue);\r
+int read_setting_filename(void *handle, const char *key, Filename *value);\r
+int read_setting_fontspec(void *handle, const char *key, FontSpec *font);\r
+void close_settings_r(void *handle);\r
+\r
+/*\r
+ * Delete a whole saved session.\r
+ */\r
+void del_settings(const char *sessionname);\r
+\r
+/*\r
+ * Enumerate all saved sessions.\r
+ */\r
+void *enum_settings_start(void);\r
+char *enum_settings_next(void *handle, char *buffer, int buflen);\r
+void enum_settings_finish(void *handle);\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Functions to access PuTTY's host key database.\r
+ */\r
+\r
+/*\r
+ * See if a host key matches the database entry. Return values can\r
+ * be 0 (entry matches database), 1 (entry is absent in database),\r
+ * or 2 (entry exists in database and is different).\r
+ */\r
+int verify_host_key(const char *hostname, int port,\r
+                   const char *keytype, const char *key);\r
+\r
+/*\r
+ * Write a host key into the database, overwriting any previous\r
+ * entry that might have been there.\r
+ */\r
+void store_host_key(const char *hostname, int port,\r
+                   const char *keytype, const char *key);\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Functions to access PuTTY's random number seed file.\r
+ */\r
+\r
+typedef void (*noise_consumer_t) (void *data, int len);\r
+\r
+/*\r
+ * Read PuTTY's random seed file and pass its contents to a noise\r
+ * consumer function.\r
+ */\r
+void read_random_seed(noise_consumer_t consumer);\r
+\r
+/*\r
+ * Write PuTTY's random seed file from a given chunk of noise.\r
+ */\r
+void write_random_seed(void *data, int len);\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Cleanup function: remove all of PuTTY's persistent state.\r
+ */\r
+void cleanup_all(void);\r
+\r
+#endif\r
diff --git a/putty/TELNET.C b/putty/TELNET.C
new file mode 100644 (file)
index 0000000..8fbe886
--- /dev/null
@@ -0,0 +1,1091 @@
+/*\r
+ * Telnet backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+#define        IAC     255                    /* interpret as command: */\r
+#define        DONT    254                    /* you are not to use option */\r
+#define        DO      253                    /* please, you use option */\r
+#define        WONT    252                    /* I won't use option */\r
+#define        WILL    251                    /* I will use option */\r
+#define        SB      250                    /* interpret as subnegotiation */\r
+#define        SE      240                    /* end sub negotiation */\r
+\r
+#define GA      249                   /* you may reverse the line */\r
+#define EL      248                   /* erase the current line */\r
+#define EC      247                   /* erase the current character */\r
+#define        AYT     246                    /* are you there */\r
+#define        AO      245                    /* abort output--but let prog finish */\r
+#define        IP      244                    /* interrupt process--permanently */\r
+#define        BREAK   243                    /* break */\r
+#define DM      242                   /* data mark--for connect. cleaning */\r
+#define NOP     241                   /* nop */\r
+#define EOR     239                   /* end of record (transparent mode) */\r
+#define ABORT   238                   /* Abort process */\r
+#define SUSP    237                   /* Suspend process */\r
+#define xEOF    236                   /* End of file: EOF is already used... */\r
+\r
+#define TELOPTS(X) \\r
+    X(BINARY, 0)                       /* 8-bit data path */ \\r
+    X(ECHO, 1)                         /* echo */ \\r
+    X(RCP, 2)                          /* prepare to reconnect */ \\r
+    X(SGA, 3)                          /* suppress go ahead */ \\r
+    X(NAMS, 4)                         /* approximate message size */ \\r
+    X(STATUS, 5)                       /* give status */ \\r
+    X(TM, 6)                           /* timing mark */ \\r
+    X(RCTE, 7)                         /* remote controlled transmission and echo */ \\r
+    X(NAOL, 8)                         /* negotiate about output line width */ \\r
+    X(NAOP, 9)                         /* negotiate about output page size */ \\r
+    X(NAOCRD, 10)                      /* negotiate about CR disposition */ \\r
+    X(NAOHTS, 11)                      /* negotiate about horizontal tabstops */ \\r
+    X(NAOHTD, 12)                      /* negotiate about horizontal tab disposition */ \\r
+    X(NAOFFD, 13)                      /* negotiate about formfeed disposition */ \\r
+    X(NAOVTS, 14)                      /* negotiate about vertical tab stops */ \\r
+    X(NAOVTD, 15)                      /* negotiate about vertical tab disposition */ \\r
+    X(NAOLFD, 16)                      /* negotiate about output LF disposition */ \\r
+    X(XASCII, 17)                      /* extended ascic character set */ \\r
+    X(LOGOUT, 18)                      /* force logout */ \\r
+    X(BM, 19)                          /* byte macro */ \\r
+    X(DET, 20)                         /* data entry terminal */ \\r
+    X(SUPDUP, 21)                      /* supdup protocol */ \\r
+    X(SUPDUPOUTPUT, 22)                /* supdup output */ \\r
+    X(SNDLOC, 23)                      /* send location */ \\r
+    X(TTYPE, 24)                       /* terminal type */ \\r
+    X(EOR, 25)                         /* end or record */ \\r
+    X(TUID, 26)                        /* TACACS user identification */ \\r
+    X(OUTMRK, 27)                      /* output marking */ \\r
+    X(TTYLOC, 28)                      /* terminal location number */ \\r
+    X(3270REGIME, 29)                  /* 3270 regime */ \\r
+    X(X3PAD, 30)                       /* X.3 PAD */ \\r
+    X(NAWS, 31)                        /* window size */ \\r
+    X(TSPEED, 32)                      /* terminal speed */ \\r
+    X(LFLOW, 33)                       /* remote flow control */ \\r
+    X(LINEMODE, 34)                    /* Linemode option */ \\r
+    X(XDISPLOC, 35)                    /* X Display Location */ \\r
+    X(OLD_ENVIRON, 36)                 /* Old - Environment variables */ \\r
+    X(AUTHENTICATION, 37)              /* Authenticate */ \\r
+    X(ENCRYPT, 38)                     /* Encryption option */ \\r
+    X(NEW_ENVIRON, 39)                 /* New - Environment variables */ \\r
+    X(TN3270E, 40)                     /* TN3270 enhancements */ \\r
+    X(XAUTH, 41)                       \\r
+    X(CHARSET, 42)                     /* Character set */ \\r
+    X(RSP, 43)                         /* Remote serial port */ \\r
+    X(COM_PORT_OPTION, 44)             /* Com port control */ \\r
+    X(SLE, 45)                         /* Suppress local echo */ \\r
+    X(STARTTLS, 46)                    /* Start TLS */ \\r
+    X(KERMIT, 47)                      /* Automatic Kermit file transfer */ \\r
+    X(SEND_URL, 48)                    \\r
+    X(FORWARD_X, 49)                   \\r
+    X(PRAGMA_LOGON, 138)               \\r
+    X(SSPI_LOGON, 139)                 \\r
+    X(PRAGMA_HEARTBEAT, 140)           \\r
+    X(EXOPL, 255)                      /* extended-options-list */\r
+\r
+#define telnet_enum(x,y) TELOPT_##x = y,\r
+enum { TELOPTS(telnet_enum) dummy=0 };\r
+#undef telnet_enum\r
+\r
+#define        TELQUAL_IS      0              /* option is... */\r
+#define        TELQUAL_SEND    1              /* send option */\r
+#define        TELQUAL_INFO    2              /* ENVIRON: informational version of IS */\r
+#define BSD_VAR 1\r
+#define BSD_VALUE 0\r
+#define RFC_VAR 0\r
+#define RFC_VALUE 1\r
+\r
+#define CR 13\r
+#define LF 10\r
+#define NUL 0\r
+\r
+#define iswritable(x) \\r
+       ( (x) != IAC && \\r
+             (telnet->opt_states[o_we_bin.index] == ACTIVE || (x) != CR))\r
+\r
+static char *telopt(int opt)\r
+{\r
+#define telnet_str(x,y) case TELOPT_##x: return #x;\r
+    switch (opt) {\r
+       TELOPTS(telnet_str)\r
+      default:\r
+       return "<unknown>";\r
+    }\r
+#undef telnet_str\r
+}\r
+\r
+static void telnet_size(void *handle, int width, int height);\r
+\r
+struct Opt {\r
+    int send;                         /* what we initially send */\r
+    int nsend;                        /* -ve send if requested to stop it */\r
+    int ack, nak;                     /* +ve and -ve acknowledgements */\r
+    int option;                               /* the option code */\r
+    int index;                        /* index into telnet->opt_states[] */\r
+    enum {\r
+       REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE\r
+    } initial_state;\r
+};\r
+\r
+enum {\r
+    OPTINDEX_NAWS,\r
+    OPTINDEX_TSPEED,\r
+    OPTINDEX_TTYPE,\r
+    OPTINDEX_OENV,\r
+    OPTINDEX_NENV,\r
+    OPTINDEX_ECHO,\r
+    OPTINDEX_WE_SGA,\r
+    OPTINDEX_THEY_SGA,\r
+    OPTINDEX_WE_BIN,\r
+    OPTINDEX_THEY_BIN,\r
+    NUM_OPTS\r
+};\r
+\r
+static const struct Opt o_naws =\r
+    { WILL, WONT, DO, DONT, TELOPT_NAWS, OPTINDEX_NAWS, REQUESTED };\r
+static const struct Opt o_tspeed =\r
+    { WILL, WONT, DO, DONT, TELOPT_TSPEED, OPTINDEX_TSPEED, REQUESTED };\r
+static const struct Opt o_ttype =\r
+    { WILL, WONT, DO, DONT, TELOPT_TTYPE, OPTINDEX_TTYPE, REQUESTED };\r
+static const struct Opt o_oenv =\r
+    { WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON, OPTINDEX_OENV, INACTIVE };\r
+static const struct Opt o_nenv =\r
+    { WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON, OPTINDEX_NENV, REQUESTED };\r
+static const struct Opt o_echo =\r
+    { DO, DONT, WILL, WONT, TELOPT_ECHO, OPTINDEX_ECHO, REQUESTED };\r
+static const struct Opt o_we_sga =\r
+    { WILL, WONT, DO, DONT, TELOPT_SGA, OPTINDEX_WE_SGA, REQUESTED };\r
+static const struct Opt o_they_sga =\r
+    { DO, DONT, WILL, WONT, TELOPT_SGA, OPTINDEX_THEY_SGA, REQUESTED };\r
+static const struct Opt o_we_bin =\r
+    { WILL, WONT, DO, DONT, TELOPT_BINARY, OPTINDEX_WE_BIN, INACTIVE };\r
+static const struct Opt o_they_bin =\r
+    { DO, DONT, WILL, WONT, TELOPT_BINARY, OPTINDEX_THEY_BIN, INACTIVE };\r
+\r
+static const struct Opt *const opts[] = {\r
+    &o_naws, &o_tspeed, &o_ttype, &o_oenv, &o_nenv, &o_echo,\r
+    &o_we_sga, &o_they_sga, &o_we_bin, &o_they_bin, NULL\r
+};\r
+\r
+typedef struct telnet_tag {\r
+    const struct plug_function_table *fn;\r
+    /* the above field _must_ be first in the structure */\r
+\r
+    Socket s;\r
+\r
+    void *frontend;\r
+    void *ldisc;\r
+    int term_width, term_height;\r
+\r
+    int opt_states[NUM_OPTS];\r
+\r
+    int echoing, editing;\r
+    int activated;\r
+    int bufsize;\r
+    int in_synch;\r
+    int sb_opt, sb_len;\r
+    unsigned char *sb_buf;\r
+    int sb_size;\r
+\r
+    enum {\r
+       TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,\r
+           SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR\r
+    } state;\r
+\r
+    Config cfg;\r
+\r
+    Pinger pinger;\r
+} *Telnet;\r
+\r
+#define TELNET_MAX_BACKLOG 4096\r
+\r
+#define SB_DELTA 1024\r
+\r
+static void c_write(Telnet telnet, char *buf, int len)\r
+{\r
+    int backlog;\r
+    backlog = from_backend(telnet->frontend, 0, buf, len);\r
+    sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);\r
+}\r
+\r
+static void log_option(Telnet telnet, char *sender, int cmd, int option)\r
+{\r
+    char *buf;\r
+    /*\r
+     * The strange-looking "<?""?>" below is there to avoid a\r
+     * trigraph - a double question mark followed by > maps to a\r
+     * closing brace character!\r
+     */\r
+    buf = dupprintf("%s:\t%s %s", sender,\r
+                   (cmd == WILL ? "WILL" : cmd == WONT ? "WONT" :\r
+                    cmd == DO ? "DO" : cmd == DONT ? "DONT" : "<?""?>"),\r
+                   telopt(option));\r
+    logevent(telnet->frontend, buf);\r
+    sfree(buf);\r
+}\r
+\r
+static void send_opt(Telnet telnet, int cmd, int option)\r
+{\r
+    unsigned char b[3];\r
+\r
+    b[0] = IAC;\r
+    b[1] = cmd;\r
+    b[2] = option;\r
+    telnet->bufsize = sk_write(telnet->s, (char *)b, 3);\r
+    log_option(telnet, "client", cmd, option);\r
+}\r
+\r
+static void deactivate_option(Telnet telnet, const struct Opt *o)\r
+{\r
+    if (telnet->opt_states[o->index] == REQUESTED ||\r
+       telnet->opt_states[o->index] == ACTIVE)\r
+       send_opt(telnet, o->nsend, o->option);\r
+    telnet->opt_states[o->index] = REALLY_INACTIVE;\r
+}\r
+\r
+/*\r
+ * Generate side effects of enabling or disabling an option.\r
+ */\r
+static void option_side_effects(Telnet telnet, const struct Opt *o, int enabled)\r
+{\r
+    if (o->option == TELOPT_ECHO && o->send == DO)\r
+       telnet->echoing = !enabled;\r
+    else if (o->option == TELOPT_SGA && o->send == DO)\r
+       telnet->editing = !enabled;\r
+    if (telnet->ldisc)                /* cause ldisc to notice the change */\r
+       ldisc_send(telnet->ldisc, NULL, 0, 0);\r
+\r
+    /* Ensure we get the minimum options */\r
+    if (!telnet->activated) {\r
+       if (telnet->opt_states[o_echo.index] == INACTIVE) {\r
+           telnet->opt_states[o_echo.index] = REQUESTED;\r
+           send_opt(telnet, o_echo.send, o_echo.option);\r
+       }\r
+       if (telnet->opt_states[o_we_sga.index] == INACTIVE) {\r
+           telnet->opt_states[o_we_sga.index] = REQUESTED;\r
+           send_opt(telnet, o_we_sga.send, o_we_sga.option);\r
+       }\r
+       if (telnet->opt_states[o_they_sga.index] == INACTIVE) {\r
+           telnet->opt_states[o_they_sga.index] = REQUESTED;\r
+           send_opt(telnet, o_they_sga.send, o_they_sga.option);\r
+       }\r
+       telnet->activated = TRUE;\r
+    }\r
+}\r
+\r
+static void activate_option(Telnet telnet, const struct Opt *o)\r
+{\r
+    if (o->send == WILL && o->option == TELOPT_NAWS)\r
+       telnet_size(telnet, telnet->term_width, telnet->term_height);\r
+    if (o->send == WILL &&\r
+       (o->option == TELOPT_NEW_ENVIRON ||\r
+        o->option == TELOPT_OLD_ENVIRON)) {\r
+       /*\r
+        * We may only have one kind of ENVIRON going at a time.\r
+        * This is a hack, but who cares.\r
+        */\r
+       deactivate_option(telnet, o->option ==\r
+                         TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv);\r
+    }\r
+    option_side_effects(telnet, o, 1);\r
+}\r
+\r
+static void refused_option(Telnet telnet, const struct Opt *o)\r
+{\r
+    if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON &&\r
+       telnet->opt_states[o_oenv.index] == INACTIVE) {\r
+       send_opt(telnet, WILL, TELOPT_OLD_ENVIRON);\r
+       telnet->opt_states[o_oenv.index] = REQUESTED;\r
+    }\r
+    option_side_effects(telnet, o, 0);\r
+}\r
+\r
+static void proc_rec_opt(Telnet telnet, int cmd, int option)\r
+{\r
+    const struct Opt *const *o;\r
+\r
+    log_option(telnet, "server", cmd, option);\r
+    for (o = opts; *o; o++) {\r
+       if ((*o)->option == option && (*o)->ack == cmd) {\r
+           switch (telnet->opt_states[(*o)->index]) {\r
+             case REQUESTED:\r
+               telnet->opt_states[(*o)->index] = ACTIVE;\r
+               activate_option(telnet, *o);\r
+               break;\r
+             case ACTIVE:\r
+               break;\r
+             case INACTIVE:\r
+               telnet->opt_states[(*o)->index] = ACTIVE;\r
+               send_opt(telnet, (*o)->send, option);\r
+               activate_option(telnet, *o);\r
+               break;\r
+             case REALLY_INACTIVE:\r
+               send_opt(telnet, (*o)->nsend, option);\r
+               break;\r
+           }\r
+           return;\r
+       } else if ((*o)->option == option && (*o)->nak == cmd) {\r
+           switch (telnet->opt_states[(*o)->index]) {\r
+             case REQUESTED:\r
+               telnet->opt_states[(*o)->index] = INACTIVE;\r
+               refused_option(telnet, *o);\r
+               break;\r
+             case ACTIVE:\r
+               telnet->opt_states[(*o)->index] = INACTIVE;\r
+               send_opt(telnet, (*o)->nsend, option);\r
+               option_side_effects(telnet, *o, 0);\r
+               break;\r
+             case INACTIVE:\r
+             case REALLY_INACTIVE:\r
+               break;\r
+           }\r
+           return;\r
+       }\r
+    }\r
+    /*\r
+     * If we reach here, the option was one we weren't prepared to\r
+     * cope with. If the request was positive (WILL or DO), we send\r
+     * a negative ack to indicate refusal. If the request was\r
+     * negative (WONT / DONT), we must do nothing.\r
+     */\r
+    if (cmd == WILL || cmd == DO)\r
+        send_opt(telnet, (cmd == WILL ? DONT : WONT), option);\r
+}\r
+\r
+static void process_subneg(Telnet telnet)\r
+{\r
+    unsigned char b[2048], *p, *q;\r
+    int var, value, n;\r
+    char *e;\r
+\r
+    switch (telnet->sb_opt) {\r
+      case TELOPT_TSPEED:\r
+       if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {\r
+           char *logbuf;\r
+           b[0] = IAC;\r
+           b[1] = SB;\r
+           b[2] = TELOPT_TSPEED;\r
+           b[3] = TELQUAL_IS;\r
+           strcpy((char *)(b + 4), telnet->cfg.termspeed);\r
+           n = 4 + strlen(telnet->cfg.termspeed);\r
+           b[n] = IAC;\r
+           b[n + 1] = SE;\r
+           telnet->bufsize = sk_write(telnet->s, (char *)b, n + 2);\r
+           logevent(telnet->frontend, "server:\tSB TSPEED SEND");\r
+           logbuf = dupprintf("client:\tSB TSPEED IS %s", telnet->cfg.termspeed);\r
+           logevent(telnet->frontend, logbuf);\r
+           sfree(logbuf);\r
+       } else\r
+           logevent(telnet->frontend, "server:\tSB TSPEED <something weird>");\r
+       break;\r
+      case TELOPT_TTYPE:\r
+       if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {\r
+           char *logbuf;\r
+           b[0] = IAC;\r
+           b[1] = SB;\r
+           b[2] = TELOPT_TTYPE;\r
+           b[3] = TELQUAL_IS;\r
+           for (n = 0; telnet->cfg.termtype[n]; n++)\r
+               b[n + 4] = (telnet->cfg.termtype[n] >= 'a'\r
+                           && telnet->cfg.termtype[n] <=\r
+                           'z' ? telnet->cfg.termtype[n] + 'A' -\r
+                           'a' : telnet->cfg.termtype[n]);\r
+           b[n + 4] = IAC;\r
+           b[n + 5] = SE;\r
+           telnet->bufsize = sk_write(telnet->s, (char *)b, n + 6);\r
+           b[n + 4] = 0;\r
+           logevent(telnet->frontend, "server:\tSB TTYPE SEND");\r
+           logbuf = dupprintf("client:\tSB TTYPE IS %s", b + 4);\r
+           logevent(telnet->frontend, logbuf);\r
+           sfree(logbuf);\r
+       } else\r
+           logevent(telnet->frontend, "server:\tSB TTYPE <something weird>\r\n");\r
+       break;\r
+      case TELOPT_OLD_ENVIRON:\r
+      case TELOPT_NEW_ENVIRON:\r
+       p = telnet->sb_buf;\r
+       q = p + telnet->sb_len;\r
+       if (p < q && *p == TELQUAL_SEND) {\r
+           char *logbuf;\r
+           p++;\r
+           logbuf = dupprintf("server:\tSB %s SEND", telopt(telnet->sb_opt));\r
+           logevent(telnet->frontend, logbuf);\r
+           sfree(logbuf);\r
+           if (telnet->sb_opt == TELOPT_OLD_ENVIRON) {\r
+               if (telnet->cfg.rfc_environ) {\r
+                   value = RFC_VALUE;\r
+                   var = RFC_VAR;\r
+               } else {\r
+                   value = BSD_VALUE;\r
+                   var = BSD_VAR;\r
+               }\r
+               /*\r
+                * Try to guess the sense of VAR and VALUE.\r
+                */\r
+               while (p < q) {\r
+                   if (*p == RFC_VAR) {\r
+                       value = RFC_VALUE;\r
+                       var = RFC_VAR;\r
+                   } else if (*p == BSD_VAR) {\r
+                       value = BSD_VALUE;\r
+                       var = BSD_VAR;\r
+                   }\r
+                   p++;\r
+               }\r
+           } else {\r
+               /*\r
+                * With NEW_ENVIRON, the sense of VAR and VALUE\r
+                * isn't in doubt.\r
+                */\r
+               value = RFC_VALUE;\r
+               var = RFC_VAR;\r
+           }\r
+           b[0] = IAC;\r
+           b[1] = SB;\r
+           b[2] = telnet->sb_opt;\r
+           b[3] = TELQUAL_IS;\r
+           n = 4;\r
+           e = telnet->cfg.environmt;\r
+           while (*e) {\r
+               b[n++] = var;\r
+               while (*e && *e != '\t')\r
+                   b[n++] = *e++;\r
+               if (*e == '\t')\r
+                   e++;\r
+               b[n++] = value;\r
+               while (*e)\r
+                   b[n++] = *e++;\r
+               e++;\r
+           }\r
+           {\r
+               char user[sizeof(telnet->cfg.username)];\r
+               (void) get_remote_username(&telnet->cfg, user, sizeof(user));\r
+               if (*user) {\r
+                   b[n++] = var;\r
+                   b[n++] = 'U';\r
+                   b[n++] = 'S';\r
+                   b[n++] = 'E';\r
+                   b[n++] = 'R';\r
+                   b[n++] = value;\r
+                   e = user;\r
+                   while (*e)\r
+                       b[n++] = *e++;\r
+               }\r
+               b[n++] = IAC;\r
+               b[n++] = SE;\r
+               telnet->bufsize = sk_write(telnet->s, (char *)b, n);\r
+               logbuf = dupprintf("client:\tSB %s IS %s%s%s%s",\r
+                                  telopt(telnet->sb_opt),\r
+                                  *user ? "USER=" : "",\r
+                                  user,\r
+                                  *user ? " " : "",\r
+                                  n == 6 ? "<nothing>" :\r
+                                  (*telnet->cfg.environmt ? "<stuff>" : ""));\r
+               logevent(telnet->frontend, logbuf);\r
+               sfree(logbuf);\r
+           }\r
+       }\r
+       break;\r
+    }\r
+}\r
+\r
+static void do_telnet_read(Telnet telnet, char *buf, int len)\r
+{\r
+    char *outbuf = NULL;\r
+    int outbuflen = 0, outbufsize = 0;\r
+\r
+#define ADDTOBUF(c) do { \\r
+    if (outbuflen >= outbufsize) { \\r
+       outbufsize = outbuflen + 256; \\r
+        outbuf = sresize(outbuf, outbufsize, char); \\r
+    } \\r
+    outbuf[outbuflen++] = (c); \\r
+} while (0)\r
+\r
+    while (len--) {\r
+       int c = (unsigned char) *buf++;\r
+\r
+       switch (telnet->state) {\r
+         case TOP_LEVEL:\r
+         case SEENCR:\r
+           if (c == NUL && telnet->state == SEENCR)\r
+               telnet->state = TOP_LEVEL;\r
+           else if (c == IAC)\r
+               telnet->state = SEENIAC;\r
+           else {\r
+               if (!telnet->in_synch)\r
+                   ADDTOBUF(c);\r
+\r
+#if 1\r
+               /* I can't get the F***ing winsock to insert the urgent IAC\r
+                * into the right position! Even with SO_OOBINLINE it gives\r
+                * it to recv too soon. And of course the DM byte (that\r
+                * arrives in the same packet!) appears several K later!!\r
+                *\r
+                * Oh well, we do get the DM in the right place so I'll\r
+                * just stop hiding on the next 0xf2 and hope for the best.\r
+                */\r
+               else if (c == DM)\r
+                   telnet->in_synch = 0;\r
+#endif\r
+               if (c == CR && telnet->opt_states[o_they_bin.index] != ACTIVE)\r
+                   telnet->state = SEENCR;\r
+               else\r
+                   telnet->state = TOP_LEVEL;\r
+           }\r
+           break;\r
+         case SEENIAC:\r
+           if (c == DO)\r
+               telnet->state = SEENDO;\r
+           else if (c == DONT)\r
+               telnet->state = SEENDONT;\r
+           else if (c == WILL)\r
+               telnet->state = SEENWILL;\r
+           else if (c == WONT)\r
+               telnet->state = SEENWONT;\r
+           else if (c == SB)\r
+               telnet->state = SEENSB;\r
+           else if (c == DM) {\r
+               telnet->in_synch = 0;\r
+               telnet->state = TOP_LEVEL;\r
+           } else {\r
+               /* ignore everything else; print it if it's IAC */\r
+               if (c == IAC) {\r
+                   ADDTOBUF(c);\r
+               }\r
+               telnet->state = TOP_LEVEL;\r
+           }\r
+           break;\r
+         case SEENWILL:\r
+           proc_rec_opt(telnet, WILL, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENWONT:\r
+           proc_rec_opt(telnet, WONT, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENDO:\r
+           proc_rec_opt(telnet, DO, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENDONT:\r
+           proc_rec_opt(telnet, DONT, c);\r
+           telnet->state = TOP_LEVEL;\r
+           break;\r
+         case SEENSB:\r
+           telnet->sb_opt = c;\r
+           telnet->sb_len = 0;\r
+           telnet->state = SUBNEGOT;\r
+           break;\r
+         case SUBNEGOT:\r
+           if (c == IAC)\r
+               telnet->state = SUBNEG_IAC;\r
+           else {\r
+             subneg_addchar:\r
+               if (telnet->sb_len >= telnet->sb_size) {\r
+                   telnet->sb_size += SB_DELTA;\r
+                   telnet->sb_buf = sresize(telnet->sb_buf, telnet->sb_size,\r
+                                            unsigned char);\r
+               }\r
+               telnet->sb_buf[telnet->sb_len++] = c;\r
+               telnet->state = SUBNEGOT;       /* in case we came here by goto */\r
+           }\r
+           break;\r
+         case SUBNEG_IAC:\r
+           if (c != SE)\r
+               goto subneg_addchar;   /* yes, it's a hack, I know, but... */\r
+           else {\r
+               process_subneg(telnet);\r
+               telnet->state = TOP_LEVEL;\r
+           }\r
+           break;\r
+       }\r
+    }\r
+\r
+    if (outbuflen)\r
+       c_write(telnet, outbuf, outbuflen);\r
+    sfree(outbuf);\r
+}\r
+\r
+static void telnet_log(Plug plug, int type, SockAddr addr, int port,\r
+                      const char *error_msg, int error_code)\r
+{\r
+    Telnet telnet = (Telnet) plug;\r
+    char addrbuf[256], *msg;\r
+\r
+    sk_getaddr(addr, addrbuf, lenof(addrbuf));\r
+\r
+    if (type == 0)\r
+       msg = dupprintf("Connecting to %s port %d", addrbuf, port);\r
+    else\r
+       msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);\r
+\r
+    logevent(telnet->frontend, msg);\r
+}\r
+\r
+static int telnet_closing(Plug plug, const char *error_msg, int error_code,\r
+                         int calling_back)\r
+{\r
+    Telnet telnet = (Telnet) plug;\r
+\r
+    if (telnet->s) {\r
+        sk_close(telnet->s);\r
+        telnet->s = NULL;\r
+       notify_remote_exit(telnet->frontend);\r
+    }\r
+    if (error_msg) {\r
+       logevent(telnet->frontend, error_msg);\r
+       connection_fatal(telnet->frontend, "%s", error_msg);\r
+    }\r
+    /* Otherwise, the remote side closed the connection normally. */\r
+    return 0;\r
+}\r
+\r
+static int telnet_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    Telnet telnet = (Telnet) plug;\r
+    if (urgent)\r
+       telnet->in_synch = TRUE;\r
+    do_telnet_read(telnet, data, len);\r
+    return 1;\r
+}\r
+\r
+static void telnet_sent(Plug plug, int bufsize)\r
+{\r
+    Telnet telnet = (Telnet) plug;\r
+    telnet->bufsize = bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set up the Telnet connection.\r
+ *\r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *telnet_init(void *frontend_handle, void **backend_handle,\r
+                              Config *cfg,\r
+                              char *host, int port, char **realhost,\r
+                              int nodelay, int keepalive)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       telnet_log,\r
+       telnet_closing,\r
+       telnet_receive,\r
+       telnet_sent\r
+    };\r
+    SockAddr addr;\r
+    const char *err;\r
+    Telnet telnet;\r
+\r
+    telnet = snew(struct telnet_tag);\r
+    telnet->fn = &fn_table;\r
+    telnet->cfg = *cfg;                       /* STRUCTURE COPY */\r
+    telnet->s = NULL;\r
+    telnet->echoing = TRUE;\r
+    telnet->editing = TRUE;\r
+    telnet->activated = FALSE;\r
+    telnet->sb_buf = NULL;\r
+    telnet->sb_size = 0;\r
+    telnet->frontend = frontend_handle;\r
+    telnet->term_width = telnet->cfg.width;\r
+    telnet->term_height = telnet->cfg.height;\r
+    telnet->state = TOP_LEVEL;\r
+    telnet->ldisc = NULL;\r
+    telnet->pinger = NULL;\r
+    *backend_handle = telnet;\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    {\r
+       char *buf;\r
+       buf = dupprintf("Looking up host \"%s\"%s", host,\r
+                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
+                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :\r
+                         "")));\r
+       logevent(telnet->frontend, buf);\r
+       sfree(buf);\r
+    }\r
+    addr = name_lookup(host, port, realhost, &telnet->cfg, cfg->addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+\r
+    if (port < 0)\r
+       port = 23;                     /* default telnet port */\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    telnet->s = new_connection(addr, *realhost, port, 0, 1,\r
+                              nodelay, keepalive, (Plug) telnet, &telnet->cfg);\r
+    if ((err = sk_socket_error(telnet->s)) != NULL)\r
+       return err;\r
+\r
+    telnet->pinger = pinger_new(&telnet->cfg, &telnet_backend, telnet);\r
+\r
+    /*\r
+     * Initialise option states.\r
+     */\r
+    if (telnet->cfg.passive_telnet) {\r
+       const struct Opt *const *o;\r
+\r
+       for (o = opts; *o; o++)\r
+           telnet->opt_states[(*o)->index] = INACTIVE;\r
+    } else {\r
+       const struct Opt *const *o;\r
+\r
+       for (o = opts; *o; o++) {\r
+           telnet->opt_states[(*o)->index] = (*o)->initial_state;\r
+           if (telnet->opt_states[(*o)->index] == REQUESTED)\r
+               send_opt(telnet, (*o)->send, (*o)->option);\r
+       }\r
+       telnet->activated = TRUE;\r
+    }\r
+\r
+    /*\r
+     * Set up SYNCH state.\r
+     */\r
+    telnet->in_synch = FALSE;\r
+\r
+    /*\r
+     * We can send special commands from the start.\r
+     */\r
+    update_specials_menu(telnet->frontend);\r
+\r
+    /*\r
+     * loghost overrides realhost, if specified.\r
+     */\r
+    if (*telnet->cfg.loghost) {\r
+       char *colon;\r
+\r
+       sfree(*realhost);\r
+       *realhost = dupstr(telnet->cfg.loghost);\r
+       colon = strrchr(*realhost, ':');\r
+       if (colon) {\r
+           /*\r
+            * FIXME: if we ever update this aspect of ssh.c for\r
+            * IPv6 literal management, this should change in line\r
+            * with it.\r
+            */\r
+           *colon++ = '\0';\r
+       }\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+static void telnet_free(void *handle)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+\r
+    sfree(telnet->sb_buf);\r
+    if (telnet->s)\r
+       sk_close(telnet->s);\r
+    if (telnet->pinger)\r
+       pinger_free(telnet->pinger);\r
+    sfree(telnet);\r
+}\r
+/*\r
+ * Reconfigure the Telnet backend. There's no immediate action\r
+ * necessary, in this backend: we just save the fresh config for\r
+ * any subsequent negotiations.\r
+ */\r
+static void telnet_reconfig(void *handle, Config *cfg)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    pinger_reconfig(telnet->pinger, &telnet->cfg, cfg);\r
+    telnet->cfg = *cfg;                       /* STRUCTURE COPY */\r
+}\r
+\r
+/*\r
+ * Called to send data down the Telnet connection.\r
+ */\r
+static int telnet_send(void *handle, char *buf, int len)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    unsigned char *p, *end;\r
+    static const unsigned char iac[2] = { IAC, IAC };\r
+    static const unsigned char cr[2] = { CR, NUL };\r
+#if 0\r
+    static const unsigned char nl[2] = { CR, LF };\r
+#endif\r
+\r
+    if (telnet->s == NULL)\r
+       return 0;\r
+\r
+    p = (unsigned char *)buf;\r
+    end = (unsigned char *)(buf + len);\r
+    while (p < end) {\r
+       unsigned char *q = p;\r
+\r
+       while (p < end && iswritable(*p))\r
+           p++;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)q, p - q);\r
+\r
+       while (p < end && !iswritable(*p)) {\r
+           telnet->bufsize = \r
+               sk_write(telnet->s, (char *)(*p == IAC ? iac : cr), 2);\r
+           p++;\r
+       }\r
+    }\r
+\r
+    return telnet->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to query the current socket sendability status.\r
+ */\r
+static int telnet_sendbuffer(void *handle)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    return telnet->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window from Telnet's POV.\r
+ */\r
+static void telnet_size(void *handle, int width, int height)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    unsigned char b[24];\r
+    int n;\r
+    char *logbuf;\r
+\r
+    telnet->term_width = width;\r
+    telnet->term_height = height;\r
+\r
+    if (telnet->s == NULL || telnet->opt_states[o_naws.index] != ACTIVE)\r
+       return;\r
+    n = 0;\r
+    b[n++] = IAC;\r
+    b[n++] = SB;\r
+    b[n++] = TELOPT_NAWS;\r
+    b[n++] = telnet->term_width >> 8;\r
+    if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */\r
+    b[n++] = telnet->term_width & 0xFF;\r
+    if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */\r
+    b[n++] = telnet->term_height >> 8;\r
+    if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */\r
+    b[n++] = telnet->term_height & 0xFF;\r
+    if (b[n-1] == IAC) b[n++] = IAC;   /* duplicate any IAC byte occurs */\r
+    b[n++] = IAC;\r
+    b[n++] = SE;\r
+    telnet->bufsize = sk_write(telnet->s, (char *)b, n);\r
+    logbuf = dupprintf("client:\tSB NAWS %d,%d",\r
+                      telnet->term_width, telnet->term_height);\r
+    logevent(telnet->frontend, logbuf);\r
+    sfree(logbuf);\r
+}\r
+\r
+/*\r
+ * Send Telnet special codes.\r
+ */\r
+static void telnet_special(void *handle, Telnet_Special code)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    unsigned char b[2];\r
+\r
+    if (telnet->s == NULL)\r
+       return;\r
+\r
+    b[0] = IAC;\r
+    switch (code) {\r
+      case TS_AYT:\r
+       b[1] = AYT;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_BRK:\r
+       b[1] = BREAK;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_EC:\r
+       b[1] = EC;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_EL:\r
+       b[1] = EL;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_GA:\r
+       b[1] = GA;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_NOP:\r
+       b[1] = NOP;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_ABORT:\r
+       b[1] = ABORT;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_AO:\r
+       b[1] = AO;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_IP:\r
+       b[1] = IP;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_SUSP:\r
+       b[1] = SUSP;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_EOR:\r
+       b[1] = EOR;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_EOF:\r
+       b[1] = xEOF;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       break;\r
+      case TS_EOL:\r
+       /* In BINARY mode, CR-LF becomes just CR -\r
+        * and without the NUL suffix too. */\r
+       if (telnet->opt_states[o_we_bin.index] == ACTIVE)\r
+           telnet->bufsize = sk_write(telnet->s, "\r", 1);\r
+       else\r
+           telnet->bufsize = sk_write(telnet->s, "\r\n", 2);\r
+       break;\r
+      case TS_SYNCH:\r
+       b[1] = DM;\r
+       telnet->bufsize = sk_write(telnet->s, (char *)b, 1);\r
+       telnet->bufsize = sk_write_oob(telnet->s, (char *)(b + 1), 1);\r
+       break;\r
+      case TS_RECHO:\r
+       if (telnet->opt_states[o_echo.index] == INACTIVE ||\r
+           telnet->opt_states[o_echo.index] == REALLY_INACTIVE) {\r
+           telnet->opt_states[o_echo.index] = REQUESTED;\r
+           send_opt(telnet, o_echo.send, o_echo.option);\r
+       }\r
+       break;\r
+      case TS_LECHO:\r
+       if (telnet->opt_states[o_echo.index] == ACTIVE) {\r
+           telnet->opt_states[o_echo.index] = REQUESTED;\r
+           send_opt(telnet, o_echo.nsend, o_echo.option);\r
+       }\r
+       break;\r
+      case TS_PING:\r
+       if (telnet->opt_states[o_they_sga.index] == ACTIVE) {\r
+           b[1] = NOP;\r
+           telnet->bufsize = sk_write(telnet->s, (char *)b, 2);\r
+       }\r
+       break;\r
+      default:\r
+       break;  /* never heard of it */\r
+    }\r
+}\r
+\r
+static const struct telnet_special *telnet_get_specials(void *handle)\r
+{\r
+    static const struct telnet_special specials[] = {\r
+       {"Are You There", TS_AYT},\r
+       {"Break", TS_BRK},\r
+       {"Synch", TS_SYNCH},\r
+       {"Erase Character", TS_EC},\r
+       {"Erase Line", TS_EL},\r
+       {"Go Ahead", TS_GA},\r
+       {"No Operation", TS_NOP},\r
+       {NULL, TS_SEP},\r
+       {"Abort Process", TS_ABORT},\r
+       {"Abort Output", TS_AO},\r
+       {"Interrupt Process", TS_IP},\r
+       {"Suspend Process", TS_SUSP},\r
+       {NULL, TS_SEP},\r
+       {"End Of Record", TS_EOR},\r
+       {"End Of File", TS_EOF},\r
+       {NULL, TS_EXITMENU}\r
+    };\r
+    return specials;\r
+}\r
+\r
+static int telnet_connected(void *handle)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    return telnet->s != NULL;\r
+}\r
+\r
+static int telnet_sendok(void *handle)\r
+{\r
+    /* Telnet telnet = (Telnet) handle; */\r
+    return 1;\r
+}\r
+\r
+static void telnet_unthrottle(void *handle, int backlog)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);\r
+}\r
+\r
+static int telnet_ldisc(void *handle, int option)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    if (option == LD_ECHO)\r
+       return telnet->echoing;\r
+    if (option == LD_EDIT)\r
+       return telnet->editing;\r
+    return FALSE;\r
+}\r
+\r
+static void telnet_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    telnet->ldisc = ldisc;\r
+}\r
+\r
+static void telnet_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static int telnet_exitcode(void *handle)\r
+{\r
+    Telnet telnet = (Telnet) handle;\r
+    if (telnet->s != NULL)\r
+        return -1;                     /* still connected */\r
+    else\r
+        /* Telnet doesn't transmit exit codes back to the client */\r
+        return 0;\r
+}\r
+\r
+/*\r
+ * cfg_info for Telnet does nothing at all.\r
+ */\r
+static int telnet_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+Backend telnet_backend = {\r
+    telnet_init,\r
+    telnet_free,\r
+    telnet_reconfig,\r
+    telnet_send,\r
+    telnet_sendbuffer,\r
+    telnet_size,\r
+    telnet_special,\r
+    telnet_get_specials,\r
+    telnet_connected,\r
+    telnet_exitcode,\r
+    telnet_sendok,\r
+    telnet_ldisc,\r
+    telnet_provide_ldisc,\r
+    telnet_provide_logctx,\r
+    telnet_unthrottle,\r
+    telnet_cfg_info,\r
+    "telnet",\r
+    PROT_TELNET,\r
+    23\r
+};\r
diff --git a/putty/TERMINAL.C b/putty/TERMINAL.C
new file mode 100644 (file)
index 0000000..baadad4
--- /dev/null
@@ -0,0 +1,6613 @@
+/*\r
+ * Terminal emulator.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+#include <time.h>\r
+#include <assert.h>\r
+#include "putty.h"\r
+#include "terminal.h"\r
+\r
+#define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )\r
+#define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )\r
+#define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )\r
+#define posdiff(p1,p2) ( ((p1).y - (p2).y) * (term->cols+1) + (p1).x - (p2).x )\r
+\r
+/* Product-order comparisons for rectangular block selection. */\r
+#define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )\r
+#define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )\r
+\r
+#define incpos(p) ( (p).x == term->cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )\r
+#define decpos(p) ( (p).x == 0 ? ((p).x = term->cols, (p).y--, 1) : ((p).x--, 0) )\r
+\r
+#define VT52_PLUS\r
+\r
+#define CL_ANSIMIN     0x0001         /* Codes in all ANSI like terminals. */\r
+#define CL_VT100       0x0002         /* VT100 */\r
+#define CL_VT100AVO    0x0004         /* VT100 +AVO; 132x24 (not 132x14) & attrs */\r
+#define CL_VT102       0x0008         /* VT102 */\r
+#define CL_VT220       0x0010         /* VT220 */\r
+#define CL_VT320       0x0020         /* VT320 */\r
+#define CL_VT420       0x0040         /* VT420 */\r
+#define CL_VT510       0x0080         /* VT510, NB VT510 includes ANSI */\r
+#define CL_VT340TEXT   0x0100         /* VT340 extensions that appear in the VT420 */\r
+#define CL_SCOANSI     0x1000         /* SCOANSI not in ANSIMIN. */\r
+#define CL_ANSI                0x2000         /* ANSI ECMA-48 not in the VT100..VT420 */\r
+#define CL_OTHER       0x4000         /* Others, Xterm, linux, putty, dunno, etc */\r
+\r
+#define TM_VT100       (CL_ANSIMIN|CL_VT100)\r
+#define TM_VT100AVO    (TM_VT100|CL_VT100AVO)\r
+#define TM_VT102       (TM_VT100AVO|CL_VT102)\r
+#define TM_VT220       (TM_VT102|CL_VT220)\r
+#define TM_VTXXX       (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)\r
+#define TM_SCOANSI     (CL_ANSIMIN|CL_SCOANSI)\r
+\r
+#define TM_PUTTY       (0xFFFF)\r
+\r
+#define UPDATE_DELAY    ((TICKSPERSEC+49)/50)/* ticks to defer window update */\r
+#define TBLINK_DELAY    ((TICKSPERSEC*9+19)/20)/* ticks between text blinks*/\r
+#define CBLINK_DELAY    (CURSORBLINK) /* ticks between cursor blinks */\r
+#define VBELL_DELAY     (VBELL_TIMEOUT) /* visual bell timeout in ticks */\r
+\r
+#define compatibility(x) \\r
+    if ( ((CL_##x)&term->compatibility_level) == 0 ) {         \\r
+       term->termstate=TOPLEVEL;                       \\r
+       break;                                          \\r
+    }\r
+#define compatibility2(x,y) \\r
+    if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \\r
+       term->termstate=TOPLEVEL;                       \\r
+       break;                                          \\r
+    }\r
+\r
+#define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )\r
+\r
+char *EMPTY_WINDOW_TITLE = "";\r
+\r
+const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 };\r
+\r
+#define sel_nl_sz  (sizeof(sel_nl)/sizeof(wchar_t))\r
+const wchar_t sel_nl[] = SEL_NL;\r
+\r
+/*\r
+ * Fetch the character at a particular position in a line array,\r
+ * for purposes of `wordtype'. The reason this isn't just a simple\r
+ * array reference is that if the character we find is UCSWIDE,\r
+ * then we must look one space further to the left.\r
+ */\r
+#define UCSGET(a, x) \\r
+    ( (x)>0 && (a)[(x)].chr == UCSWIDE ? (a)[(x)-1].chr : (a)[(x)].chr )\r
+\r
+/*\r
+ * Detect the various aliases of U+0020 SPACE.\r
+ */\r
+#define IS_SPACE_CHR(chr) \\r
+       ((chr) == 0x20 || (DIRECT_CHAR(chr) && ((chr) & 0xFF) == 0x20))\r
+\r
+/*\r
+ * Spot magic CSETs.\r
+ */\r
+#define CSET_OF(chr) (DIRECT_CHAR(chr)||DIRECT_FONT(chr) ? (chr)&CSET_MASK : 0)\r
+\r
+/*\r
+ * Internal prototypes.\r
+ */\r
+static void resizeline(Terminal *, termline *, int);\r
+static termline *lineptr(Terminal *, int, int, int);\r
+static void unlineptr(termline *);\r
+static void do_paint(Terminal *, Context, int);\r
+static void erase_lots(Terminal *, int, int, int);\r
+static int find_last_nonempty_line(Terminal *, tree234 *);\r
+static void swap_screen(Terminal *, int, int, int);\r
+static void update_sbar(Terminal *);\r
+static void deselect(Terminal *);\r
+static void term_print_finish(Terminal *);\r
+static void scroll(Terminal *, int, int, int, int);\r
+#ifdef OPTIMISE_SCROLL\r
+static void scroll_display(Terminal *, int, int, int);\r
+#endif /* OPTIMISE_SCROLL */\r
+\r
+static termline *newline(Terminal *term, int cols, int bce)\r
+{\r
+    termline *line;\r
+    int j;\r
+\r
+    line = snew(termline);\r
+    line->chars = snewn(cols, termchar);\r
+    for (j = 0; j < cols; j++)\r
+       line->chars[j] = (bce ? term->erase_char : term->basic_erase_char);\r
+    line->cols = line->size = cols;\r
+    line->lattr = LATTR_NORM;\r
+    line->temporary = FALSE;\r
+    line->cc_free = 0;\r
+\r
+    return line;\r
+}\r
+\r
+static void freeline(termline *line)\r
+{\r
+    if (line) {\r
+       sfree(line->chars);\r
+       sfree(line);\r
+    }\r
+}\r
+\r
+static void unlineptr(termline *line)\r
+{\r
+    if (line->temporary)\r
+       freeline(line);\r
+}\r
+\r
+#ifdef TERM_CC_DIAGS\r
+/*\r
+ * Diagnostic function: verify that a termline has a correct\r
+ * combining character structure.\r
+ * \r
+ * This is a performance-intensive check, so it's no longer enabled\r
+ * by default.\r
+ */\r
+static void cc_check(termline *line)\r
+{\r
+    unsigned char *flags;\r
+    int i, j;\r
+\r
+    assert(line->size >= line->cols);\r
+\r
+    flags = snewn(line->size, unsigned char);\r
+\r
+    for (i = 0; i < line->size; i++)\r
+       flags[i] = (i < line->cols);\r
+\r
+    for (i = 0; i < line->cols; i++) {\r
+       j = i;\r
+       while (line->chars[j].cc_next) {\r
+           j += line->chars[j].cc_next;\r
+           assert(j >= line->cols && j < line->size);\r
+           assert(!flags[j]);\r
+           flags[j] = TRUE;\r
+       }\r
+    }\r
+\r
+    j = line->cc_free;\r
+    if (j) {\r
+       while (1) {\r
+           assert(j >= line->cols && j < line->size);\r
+           assert(!flags[j]);\r
+           flags[j] = TRUE;\r
+           if (line->chars[j].cc_next)\r
+               j += line->chars[j].cc_next;\r
+           else\r
+               break;\r
+       }\r
+    }\r
+\r
+    j = 0;\r
+    for (i = 0; i < line->size; i++)\r
+       j += (flags[i] != 0);\r
+\r
+    assert(j == line->size);\r
+\r
+    sfree(flags);\r
+}\r
+#endif\r
+\r
+/*\r
+ * Add a combining character to a character cell.\r
+ */\r
+static void add_cc(termline *line, int col, unsigned long chr)\r
+{\r
+    int newcc;\r
+\r
+    assert(col >= 0 && col < line->cols);\r
+\r
+    /*\r
+     * Start by extending the cols array if the free list is empty.\r
+     */\r
+    if (!line->cc_free) {\r
+       int n = line->size;\r
+       line->size += 16 + (line->size - line->cols) / 2;\r
+       line->chars = sresize(line->chars, line->size, termchar);\r
+       line->cc_free = n;\r
+       while (n < line->size) {\r
+           if (n+1 < line->size)\r
+               line->chars[n].cc_next = 1;\r
+           else\r
+               line->chars[n].cc_next = 0;\r
+           n++;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Now walk the cc list of the cell in question.\r
+     */\r
+    while (line->chars[col].cc_next)\r
+       col += line->chars[col].cc_next;\r
+\r
+    /*\r
+     * `col' now points at the last cc currently in this cell; so\r
+     * we simply add another one.\r
+     */\r
+    newcc = line->cc_free;\r
+    if (line->chars[newcc].cc_next)\r
+       line->cc_free = newcc + line->chars[newcc].cc_next;\r
+    else\r
+       line->cc_free = 0;\r
+    line->chars[newcc].cc_next = 0;\r
+    line->chars[newcc].chr = chr;\r
+    line->chars[col].cc_next = newcc - col;\r
+\r
+#ifdef TERM_CC_DIAGS\r
+    cc_check(line);\r
+#endif\r
+}\r
+\r
+/*\r
+ * Clear the combining character list in a character cell.\r
+ */\r
+static void clear_cc(termline *line, int col)\r
+{\r
+    int oldfree, origcol = col;\r
+\r
+    assert(col >= 0 && col < line->cols);\r
+\r
+    if (!line->chars[col].cc_next)\r
+       return;                        /* nothing needs doing */\r
+\r
+    oldfree = line->cc_free;\r
+    line->cc_free = col + line->chars[col].cc_next;\r
+    while (line->chars[col].cc_next)\r
+       col += line->chars[col].cc_next;\r
+    if (oldfree)\r
+       line->chars[col].cc_next = oldfree - col;\r
+    else\r
+       line->chars[col].cc_next = 0;\r
+\r
+    line->chars[origcol].cc_next = 0;\r
+\r
+#ifdef TERM_CC_DIAGS\r
+    cc_check(line);\r
+#endif\r
+}\r
+\r
+/*\r
+ * Compare two character cells for equality. Special case required\r
+ * in do_paint() where we override what we expect the chr and attr\r
+ * fields to be.\r
+ */\r
+static int termchars_equal_override(termchar *a, termchar *b,\r
+                                   unsigned long bchr, unsigned long battr)\r
+{\r
+    /* FULL-TERMCHAR */\r
+    if (a->chr != bchr)\r
+       return FALSE;\r
+    if ((a->attr &~ DATTR_MASK) != (battr &~ DATTR_MASK))\r
+       return FALSE;\r
+    while (a->cc_next || b->cc_next) {\r
+       if (!a->cc_next || !b->cc_next)\r
+           return FALSE;              /* one cc-list ends, other does not */\r
+       a += a->cc_next;\r
+       b += b->cc_next;\r
+       if (a->chr != b->chr)\r
+           return FALSE;\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+static int termchars_equal(termchar *a, termchar *b)\r
+{\r
+    return termchars_equal_override(a, b, b->chr, b->attr);\r
+}\r
+\r
+/*\r
+ * Copy a character cell. (Requires a pointer to the destination\r
+ * termline, so as to access its free list.)\r
+ */\r
+static void copy_termchar(termline *destline, int x, termchar *src)\r
+{\r
+    clear_cc(destline, x);\r
+\r
+    destline->chars[x] = *src;        /* copy everything except cc-list */\r
+    destline->chars[x].cc_next = 0;    /* and make sure this is zero */\r
+\r
+    while (src->cc_next) {\r
+       src += src->cc_next;\r
+       add_cc(destline, x, src->chr);\r
+    }\r
+\r
+#ifdef TERM_CC_DIAGS\r
+    cc_check(destline);\r
+#endif\r
+}\r
+\r
+/*\r
+ * Move a character cell within its termline.\r
+ */\r
+static void move_termchar(termline *line, termchar *dest, termchar *src)\r
+{\r
+    /* First clear the cc list from the original char, just in case. */\r
+    clear_cc(line, dest - line->chars);\r
+\r
+    /* Move the character cell and adjust its cc_next. */\r
+    *dest = *src;                     /* copy everything except cc-list */\r
+    if (src->cc_next)\r
+       dest->cc_next = src->cc_next - (dest-src);\r
+\r
+    /* Ensure the original cell doesn't have a cc list. */\r
+    src->cc_next = 0;\r
+\r
+#ifdef TERM_CC_DIAGS\r
+    cc_check(line);\r
+#endif\r
+}\r
+\r
+/*\r
+ * Compress and decompress a termline into an RLE-based format for\r
+ * storing in scrollback. (Since scrollback almost never needs to\r
+ * be modified and exists in huge quantities, this is a sensible\r
+ * tradeoff, particularly since it allows us to continue adding\r
+ * features to the main termchar structure without proportionally\r
+ * bloating the terminal emulator's memory footprint unless those\r
+ * features are in constant use.)\r
+ */\r
+struct buf {\r
+    unsigned char *data;\r
+    int len, size;\r
+};\r
+static void add(struct buf *b, unsigned char c)\r
+{\r
+    if (b->len >= b->size) {\r
+       b->size = (b->len * 3 / 2) + 512;\r
+       b->data = sresize(b->data, b->size, unsigned char);\r
+    }\r
+    b->data[b->len++] = c;\r
+}\r
+static int get(struct buf *b)\r
+{\r
+    return b->data[b->len++];\r
+}\r
+static void makerle(struct buf *b, termline *ldata,\r
+                   void (*makeliteral)(struct buf *b, termchar *c,\r
+                                       unsigned long *state))\r
+{\r
+    int hdrpos, hdrsize, n, prevlen, prevpos, thislen, thispos, prev2;\r
+    termchar *c = ldata->chars;\r
+    unsigned long state = 0, oldstate;\r
+\r
+    n = ldata->cols;\r
+\r
+    hdrpos = b->len;\r
+    hdrsize = 0;\r
+    add(b, 0);\r
+    prevlen = prevpos = 0;\r
+    prev2 = FALSE;\r
+\r
+    while (n-- > 0) {\r
+       thispos = b->len;\r
+       makeliteral(b, c++, &state);\r
+       thislen = b->len - thispos;\r
+       if (thislen == prevlen &&\r
+           !memcmp(b->data + prevpos, b->data + thispos, thislen)) {\r
+           /*\r
+            * This literal precisely matches the previous one.\r
+            * Turn it into a run if it's worthwhile.\r
+            * \r
+            * With one-byte literals, it costs us two bytes to\r
+            * encode a run, plus another byte to write the header\r
+            * to resume normal output; so a three-element run is\r
+            * neutral, and anything beyond that is unconditionally\r
+            * worthwhile. With two-byte literals or more, even a\r
+            * 2-run is a win.\r
+            */\r
+           if (thislen > 1 || prev2) {\r
+               int runpos, runlen;\r
+\r
+               /*\r
+                * It's worth encoding a run. Start at prevpos,\r
+                * unless hdrsize==0 in which case we can back up\r
+                * another one and start by overwriting hdrpos.\r
+                */\r
+\r
+               hdrsize--;             /* remove the literal at prevpos */\r
+               if (prev2) {\r
+                   assert(hdrsize > 0);\r
+                   hdrsize--;\r
+                   prevpos -= prevlen;/* and possibly another one */\r
+               }\r
+\r
+               if (hdrsize == 0) {\r
+                   assert(prevpos == hdrpos + 1);\r
+                   runpos = hdrpos;\r
+                   b->len = prevpos+prevlen;\r
+               } else {\r
+                   memmove(b->data + prevpos+1, b->data + prevpos, prevlen);\r
+                   runpos = prevpos;\r
+                   b->len = prevpos+prevlen+1;\r
+                   /*\r
+                    * Terminate the previous run of ordinary\r
+                    * literals.\r
+                    */\r
+                   assert(hdrsize >= 1 && hdrsize <= 128);\r
+                   b->data[hdrpos] = hdrsize - 1;\r
+               }\r
+\r
+               runlen = prev2 ? 3 : 2;\r
+\r
+               while (n > 0 && runlen < 129) {\r
+                   int tmppos, tmplen;\r
+                   tmppos = b->len;\r
+                   oldstate = state;\r
+                   makeliteral(b, c, &state);\r
+                   tmplen = b->len - tmppos;\r
+                   b->len = tmppos;\r
+                   if (tmplen != thislen ||\r
+                       memcmp(b->data + runpos+1, b->data + tmppos, tmplen)) {\r
+                       state = oldstate;\r
+                       break;         /* run over */\r
+                   }\r
+                   n--, c++, runlen++;\r
+               }\r
+\r
+               assert(runlen >= 2 && runlen <= 129);\r
+               b->data[runpos] = runlen + 0x80 - 2;\r
+\r
+               hdrpos = b->len;\r
+               hdrsize = 0;\r
+               add(b, 0);\r
+               /* And ensure this run doesn't interfere with the next. */\r
+               prevlen = prevpos = 0;\r
+               prev2 = FALSE;\r
+\r
+               continue;\r
+           } else {\r
+               /*\r
+                * Just flag that the previous two literals were\r
+                * identical, in case we find a third identical one\r
+                * we want to turn into a run.\r
+                */\r
+               prev2 = TRUE;\r
+               prevlen = thislen;\r
+               prevpos = thispos;\r
+           }\r
+       } else {\r
+           prev2 = FALSE;\r
+           prevlen = thislen;\r
+           prevpos = thispos;\r
+       }\r
+\r
+       /*\r
+        * This character isn't (yet) part of a run. Add it to\r
+        * hdrsize.\r
+        */\r
+       hdrsize++;\r
+       if (hdrsize == 128) {\r
+           b->data[hdrpos] = hdrsize - 1;\r
+           hdrpos = b->len;\r
+           hdrsize = 0;\r
+           add(b, 0);\r
+           prevlen = prevpos = 0;\r
+           prev2 = FALSE;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Clean up.\r
+     */\r
+    if (hdrsize > 0) {\r
+       assert(hdrsize <= 128);\r
+       b->data[hdrpos] = hdrsize - 1;\r
+    } else {\r
+       b->len = hdrpos;\r
+    }\r
+}\r
+static void makeliteral_chr(struct buf *b, termchar *c, unsigned long *state)\r
+{\r
+    /*\r
+     * My encoding for characters is UTF-8-like, in that it stores\r
+     * 7-bit ASCII in one byte and uses high-bit-set bytes as\r
+     * introducers to indicate a longer sequence. However, it's\r
+     * unlike UTF-8 in that it doesn't need to be able to\r
+     * resynchronise, and therefore I don't want to waste two bits\r
+     * per byte on having recognisable continuation characters.\r
+     * Also I don't want to rule out the possibility that I may one\r
+     * day use values 0x80000000-0xFFFFFFFF for interesting\r
+     * purposes, so unlike UTF-8 I need a full 32-bit range.\r
+     * Accordingly, here is my encoding:\r
+     * \r
+     * 00000000-0000007F: 0xxxxxxx (but see below)\r
+     * 00000080-00003FFF: 10xxxxxx xxxxxxxx\r
+     * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx\r
+     * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx\r
+     * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx\r
+     * \r
+     * (`Z' is like `x' but is always going to be zero since the\r
+     * values I'm encoding don't go above 2^32. In principle the\r
+     * five-byte form of the encoding could extend to 2^35, and\r
+     * there could be six-, seven-, eight- and nine-byte forms as\r
+     * well to allow up to 64-bit values to be encoded. But that's\r
+     * completely unnecessary for these purposes!)\r
+     * \r
+     * The encoding as written above would be very simple, except\r
+     * that 7-bit ASCII can occur in several different ways in the\r
+     * terminal data; sometimes it crops up in the D800 page\r
+     * (CSET_ASCII) but at other times it's in the 0000 page (real\r
+     * Unicode). Therefore, this encoding is actually _stateful_:\r
+     * the one-byte encoding of 00-7F actually indicates `reuse the\r
+     * upper three bytes of the last character', and to encode an\r
+     * absolute value of 00-7F you need to use the two-byte form\r
+     * instead.\r
+     */\r
+    if ((c->chr & ~0x7F) == *state) {\r
+       add(b, (unsigned char)(c->chr & 0x7F));\r
+    } else if (c->chr < 0x4000) {\r
+       add(b, (unsigned char)(((c->chr >> 8) & 0x3F) | 0x80));\r
+       add(b, (unsigned char)(c->chr & 0xFF));\r
+    } else if (c->chr < 0x200000) {\r
+       add(b, (unsigned char)(((c->chr >> 16) & 0x1F) | 0xC0));\r
+       add(b, (unsigned char)((c->chr >> 8) & 0xFF));\r
+       add(b, (unsigned char)(c->chr & 0xFF));\r
+    } else if (c->chr < 0x10000000) {\r
+       add(b, (unsigned char)(((c->chr >> 24) & 0x0F) | 0xE0));\r
+       add(b, (unsigned char)((c->chr >> 16) & 0xFF));\r
+       add(b, (unsigned char)((c->chr >> 8) & 0xFF));\r
+       add(b, (unsigned char)(c->chr & 0xFF));\r
+    } else {\r
+       add(b, 0xF0);\r
+       add(b, (unsigned char)((c->chr >> 24) & 0xFF));\r
+       add(b, (unsigned char)((c->chr >> 16) & 0xFF));\r
+       add(b, (unsigned char)((c->chr >> 8) & 0xFF));\r
+       add(b, (unsigned char)(c->chr & 0xFF));\r
+    }\r
+    *state = c->chr & ~0xFF;\r
+}\r
+static void makeliteral_attr(struct buf *b, termchar *c, unsigned long *state)\r
+{\r
+    /*\r
+     * My encoding for attributes is 16-bit-granular and assumes\r
+     * that the top bit of the word is never required. I either\r
+     * store a two-byte value with the top bit clear (indicating\r
+     * just that value), or a four-byte value with the top bit set\r
+     * (indicating the same value with its top bit clear).\r
+     * \r
+     * However, first I permute the bits of the attribute value, so\r
+     * that the eight bits of colour (four in each of fg and bg)\r
+     * which are never non-zero unless xterm 256-colour mode is in\r
+     * use are placed higher up the word than everything else. This\r
+     * ensures that attribute values remain 16-bit _unless_ the\r
+     * user uses extended colour.\r
+     */\r
+    unsigned attr, colourbits;\r
+\r
+    attr = c->attr;\r
+\r
+    assert(ATTR_BGSHIFT > ATTR_FGSHIFT);\r
+\r
+    colourbits = (attr >> (ATTR_BGSHIFT + 4)) & 0xF;\r
+    colourbits <<= 4;\r
+    colourbits |= (attr >> (ATTR_FGSHIFT + 4)) & 0xF;\r
+\r
+    attr = (((attr >> (ATTR_BGSHIFT + 8)) << (ATTR_BGSHIFT + 4)) |\r
+           (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));\r
+    attr = (((attr >> (ATTR_FGSHIFT + 8)) << (ATTR_FGSHIFT + 4)) |\r
+           (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));\r
+\r
+    attr |= (colourbits << (32-9));\r
+\r
+    if (attr < 0x8000) {\r
+       add(b, (unsigned char)((attr >> 8) & 0xFF));\r
+       add(b, (unsigned char)(attr & 0xFF));\r
+    } else {\r
+       add(b, (unsigned char)(((attr >> 24) & 0x7F) | 0x80));\r
+       add(b, (unsigned char)((attr >> 16) & 0xFF));\r
+       add(b, (unsigned char)((attr >> 8) & 0xFF));\r
+       add(b, (unsigned char)(attr & 0xFF));\r
+    }\r
+}\r
+static void makeliteral_cc(struct buf *b, termchar *c, unsigned long *state)\r
+{\r
+    /*\r
+     * For combining characters, I just encode a bunch of ordinary\r
+     * chars using makeliteral_chr, and terminate with a \0\r
+     * character (which I know won't come up as a combining char\r
+     * itself).\r
+     * \r
+     * I don't use the stateful encoding in makeliteral_chr.\r
+     */\r
+    unsigned long zstate;\r
+    termchar z;\r
+\r
+    while (c->cc_next) {\r
+       c += c->cc_next;\r
+\r
+       assert(c->chr != 0);\r
+\r
+       zstate = 0;\r
+       makeliteral_chr(b, c, &zstate);\r
+    }\r
+\r
+    z.chr = 0;\r
+    zstate = 0;\r
+    makeliteral_chr(b, &z, &zstate);\r
+}\r
+\r
+static termline *decompressline(unsigned char *data, int *bytes_used);\r
+\r
+static unsigned char *compressline(termline *ldata)\r
+{\r
+    struct buf buffer = { NULL, 0, 0 }, *b = &buffer;\r
+\r
+    /*\r
+     * First, store the column count, 7 bits at a time, least\r
+     * significant `digit' first, with the high bit set on all but\r
+     * the last.\r
+     */\r
+    {\r
+       int n = ldata->cols;\r
+       while (n >= 128) {\r
+           add(b, (unsigned char)((n & 0x7F) | 0x80));\r
+           n >>= 7;\r
+       }\r
+       add(b, (unsigned char)(n));\r
+    }\r
+\r
+    /*\r
+     * Next store the lattrs; same principle.\r
+     */\r
+    {\r
+       int n = ldata->lattr;\r
+       while (n >= 128) {\r
+           add(b, (unsigned char)((n & 0x7F) | 0x80));\r
+           n >>= 7;\r
+       }\r
+       add(b, (unsigned char)(n));\r
+    }\r
+\r
+    /*\r
+     * Now we store a sequence of separate run-length encoded\r
+     * fragments, each containing exactly as many symbols as there\r
+     * are columns in the ldata.\r
+     * \r
+     * All of these have a common basic format:\r
+     * \r
+     *  - a byte 00-7F indicates that X+1 literals follow it\r
+     *         - a byte 80-FF indicates that a single literal follows it\r
+     *           and expects to be repeated (X-0x80)+2 times.\r
+     * \r
+     * The format of the `literals' varies between the fragments.\r
+     */\r
+    makerle(b, ldata, makeliteral_chr);\r
+    makerle(b, ldata, makeliteral_attr);\r
+    makerle(b, ldata, makeliteral_cc);\r
+\r
+    /*\r
+     * Diagnostics: ensure that the compressed data really does\r
+     * decompress to the right thing.\r
+     * \r
+     * This is a bit performance-heavy for production code.\r
+     */\r
+#ifdef TERM_CC_DIAGS\r
+#ifndef CHECK_SB_COMPRESSION\r
+    {\r
+       int dused;\r
+       termline *dcl;\r
+       int i;\r
+\r
+#ifdef DIAGNOSTIC_SB_COMPRESSION\r
+       for (i = 0; i < b->len; i++) {\r
+           printf(" %02x ", b->data[i]);\r
+       }\r
+       printf("\n");\r
+#endif\r
+\r
+       dcl = decompressline(b->data, &dused);\r
+       assert(b->len == dused);\r
+       assert(ldata->cols == dcl->cols);\r
+       assert(ldata->lattr == dcl->lattr);\r
+       for (i = 0; i < ldata->cols; i++)\r
+           assert(termchars_equal(&ldata->chars[i], &dcl->chars[i]));\r
+\r
+#ifdef DIAGNOSTIC_SB_COMPRESSION\r
+       printf("%d cols (%d bytes) -> %d bytes (factor of %g)\n",\r
+              ldata->cols, 4 * ldata->cols, dused,\r
+              (double)dused / (4 * ldata->cols));\r
+#endif\r
+\r
+       freeline(dcl);\r
+    }\r
+#endif\r
+#endif /* TERM_CC_DIAGS */\r
+\r
+    /*\r
+     * Trim the allocated memory so we don't waste any, and return.\r
+     */\r
+    return sresize(b->data, b->len, unsigned char);\r
+}\r
+\r
+static void readrle(struct buf *b, termline *ldata,\r
+                   void (*readliteral)(struct buf *b, termchar *c,\r
+                                       termline *ldata, unsigned long *state))\r
+{\r
+    int n = 0;\r
+    unsigned long state = 0;\r
+\r
+    while (n < ldata->cols) {\r
+       int hdr = get(b);\r
+\r
+       if (hdr >= 0x80) {\r
+           /* A run. */\r
+\r
+           int pos = b->len, count = hdr + 2 - 0x80;\r
+           while (count--) {\r
+               assert(n < ldata->cols);\r
+               b->len = pos;\r
+               readliteral(b, ldata->chars + n, ldata, &state);\r
+               n++;\r
+           }\r
+       } else {\r
+           /* Just a sequence of consecutive literals. */\r
+\r
+           int count = hdr + 1;\r
+           while (count--) {\r
+               assert(n < ldata->cols);\r
+               readliteral(b, ldata->chars + n, ldata, &state);\r
+               n++;\r
+           }\r
+       }\r
+    }\r
+\r
+    assert(n == ldata->cols);\r
+}\r
+static void readliteral_chr(struct buf *b, termchar *c, termline *ldata,\r
+                           unsigned long *state)\r
+{\r
+    int byte;\r
+\r
+    /*\r
+     * 00000000-0000007F: 0xxxxxxx\r
+     * 00000080-00003FFF: 10xxxxxx xxxxxxxx\r
+     * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx\r
+     * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx\r
+     * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx\r
+     */\r
+\r
+    byte = get(b);\r
+    if (byte < 0x80) {\r
+       c->chr = byte | *state;\r
+    } else if (byte < 0xC0) {\r
+       c->chr = (byte &~ 0xC0) << 8;\r
+       c->chr |= get(b);\r
+    } else if (byte < 0xE0) {\r
+       c->chr = (byte &~ 0xE0) << 16;\r
+       c->chr |= get(b) << 8;\r
+       c->chr |= get(b);\r
+    } else if (byte < 0xF0) {\r
+       c->chr = (byte &~ 0xF0) << 24;\r
+       c->chr |= get(b) << 16;\r
+       c->chr |= get(b) << 8;\r
+       c->chr |= get(b);\r
+    } else {\r
+       assert(byte == 0xF0);\r
+       c->chr = get(b) << 24;\r
+       c->chr |= get(b) << 16;\r
+       c->chr |= get(b) << 8;\r
+       c->chr |= get(b);\r
+    }\r
+    *state = c->chr & ~0xFF;\r
+}\r
+static void readliteral_attr(struct buf *b, termchar *c, termline *ldata,\r
+                            unsigned long *state)\r
+{\r
+    unsigned val, attr, colourbits;\r
+\r
+    val = get(b) << 8;\r
+    val |= get(b);\r
+\r
+    if (val >= 0x8000) {\r
+       val &= ~0x8000;\r
+       val <<= 16;\r
+       val |= get(b) << 8;\r
+       val |= get(b);\r
+    }\r
+\r
+    colourbits = (val >> (32-9)) & 0xFF;\r
+    attr = (val & ((1<<(32-9))-1));\r
+\r
+    attr = (((attr >> (ATTR_FGSHIFT + 4)) << (ATTR_FGSHIFT + 8)) |\r
+           (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));\r
+    attr = (((attr >> (ATTR_BGSHIFT + 4)) << (ATTR_BGSHIFT + 8)) |\r
+           (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));\r
+\r
+    attr |= (colourbits >> 4) << (ATTR_BGSHIFT + 4);\r
+    attr |= (colourbits & 0xF) << (ATTR_FGSHIFT + 4);\r
+\r
+    c->attr = attr;\r
+}\r
+static void readliteral_cc(struct buf *b, termchar *c, termline *ldata,\r
+                          unsigned long *state)\r
+{\r
+    termchar n;\r
+    unsigned long zstate;\r
+    int x = c - ldata->chars;\r
+\r
+    c->cc_next = 0;\r
+\r
+    while (1) {\r
+       zstate = 0;\r
+       readliteral_chr(b, &n, ldata, &zstate);\r
+       if (!n.chr)\r
+           break;\r
+       add_cc(ldata, x, n.chr);\r
+    }\r
+}\r
+\r
+static termline *decompressline(unsigned char *data, int *bytes_used)\r
+{\r
+    int ncols, byte, shift;\r
+    struct buf buffer, *b = &buffer;\r
+    termline *ldata;\r
+\r
+    b->data = data;\r
+    b->len = 0;\r
+\r
+    /*\r
+     * First read in the column count.\r
+     */\r
+    ncols = shift = 0;\r
+    do {\r
+       byte = get(b);\r
+       ncols |= (byte & 0x7F) << shift;\r
+       shift += 7;\r
+    } while (byte & 0x80);\r
+\r
+    /*\r
+     * Now create the output termline.\r
+     */\r
+    ldata = snew(termline);\r
+    ldata->chars = snewn(ncols, termchar);\r
+    ldata->cols = ldata->size = ncols;\r
+    ldata->temporary = TRUE;\r
+    ldata->cc_free = 0;\r
+\r
+    /*\r
+     * We must set all the cc pointers in ldata->chars to 0 right\r
+     * now, so that cc diagnostics that verify the integrity of the\r
+     * whole line will make sense while we're in the middle of\r
+     * building it up.\r
+     */\r
+    {\r
+       int i;\r
+       for (i = 0; i < ldata->cols; i++)\r
+           ldata->chars[i].cc_next = 0;\r
+    }\r
+\r
+    /*\r
+     * Now read in the lattr.\r
+     */\r
+    ldata->lattr = shift = 0;\r
+    do {\r
+       byte = get(b);\r
+       ldata->lattr |= (byte & 0x7F) << shift;\r
+       shift += 7;\r
+    } while (byte & 0x80);\r
+\r
+    /*\r
+     * Now we read in each of the RLE streams in turn.\r
+     */\r
+    readrle(b, ldata, readliteral_chr);\r
+    readrle(b, ldata, readliteral_attr);\r
+    readrle(b, ldata, readliteral_cc);\r
+\r
+    /* Return the number of bytes read, for diagnostic purposes. */\r
+    if (bytes_used)\r
+       *bytes_used = b->len;\r
+\r
+    return ldata;\r
+}\r
+\r
+/*\r
+ * Resize a line to make it `cols' columns wide.\r
+ */\r
+static void resizeline(Terminal *term, termline *line, int cols)\r
+{\r
+    int i, oldcols;\r
+\r
+    if (line->cols != cols) {\r
+\r
+       oldcols = line->cols;\r
+\r
+       /*\r
+        * This line is the wrong length, which probably means it\r
+        * hasn't been accessed since a resize. Resize it now.\r
+        * \r
+        * First, go through all the characters that will be thrown\r
+        * out in the resize (if we're shrinking the line) and\r
+        * return their cc lists to the cc free list.\r
+        */\r
+       for (i = cols; i < oldcols; i++)\r
+           clear_cc(line, i);\r
+\r
+       /*\r
+        * If we're shrinking the line, we now bodily move the\r
+        * entire cc section from where it started to where it now\r
+        * needs to be. (We have to do this before the resize, so\r
+        * that the data we're copying is still there. However, if\r
+        * we're expanding, we have to wait until _after_ the\r
+        * resize so that the space we're copying into is there.)\r
+        */\r
+       if (cols < oldcols)\r
+           memmove(line->chars + cols, line->chars + oldcols,\r
+                   (line->size - line->cols) * TSIZE);\r
+\r
+       /*\r
+        * Now do the actual resize, leaving the _same_ amount of\r
+        * cc space as there was to begin with.\r
+        */\r
+       line->size += cols - oldcols;\r
+       line->chars = sresize(line->chars, line->size, TTYPE);\r
+       line->cols = cols;\r
+\r
+       /*\r
+        * If we're expanding the line, _now_ we move the cc\r
+        * section.\r
+        */\r
+       if (cols > oldcols)\r
+           memmove(line->chars + cols, line->chars + oldcols,\r
+                   (line->size - line->cols) * TSIZE);\r
+\r
+       /*\r
+        * Go through what's left of the original line, and adjust\r
+        * the first cc_next pointer in each list. (All the\r
+        * subsequent ones are still valid because they are\r
+        * relative offsets within the cc block.) Also do the same\r
+        * to the head of the cc_free list.\r
+        */\r
+       for (i = 0; i < oldcols && i < cols; i++)\r
+           if (line->chars[i].cc_next)\r
+               line->chars[i].cc_next += cols - oldcols;\r
+       if (line->cc_free)\r
+           line->cc_free += cols - oldcols;\r
+\r
+       /*\r
+        * And finally fill in the new space with erase chars. (We\r
+        * don't have to worry about cc lists here, because we\r
+        * _know_ the erase char doesn't have one.)\r
+        */\r
+       for (i = oldcols; i < cols; i++)\r
+           line->chars[i] = term->basic_erase_char;\r
+\r
+#ifdef TERM_CC_DIAGS\r
+       cc_check(line);\r
+#endif\r
+    }\r
+}\r
+\r
+/*\r
+ * Get the number of lines in the scrollback.\r
+ */\r
+static int sblines(Terminal *term)\r
+{\r
+    int sblines = count234(term->scrollback);\r
+    if (term->cfg.erase_to_scrollback &&\r
+       term->alt_which && term->alt_screen) {\r
+           sblines += term->alt_sblines;\r
+    }\r
+    return sblines;\r
+}\r
+\r
+/*\r
+ * Retrieve a line of the screen or of the scrollback, according to\r
+ * whether the y coordinate is non-negative or negative\r
+ * (respectively).\r
+ */\r
+static termline *lineptr(Terminal *term, int y, int lineno, int screen)\r
+{\r
+    termline *line;\r
+    tree234 *whichtree;\r
+    int treeindex;\r
+\r
+    if (y >= 0) {\r
+       whichtree = term->screen;\r
+       treeindex = y;\r
+    } else {\r
+       int altlines = 0;\r
+\r
+       assert(!screen);\r
+\r
+       if (term->cfg.erase_to_scrollback &&\r
+           term->alt_which && term->alt_screen) {\r
+           altlines = term->alt_sblines;\r
+       }\r
+       if (y < -altlines) {\r
+           whichtree = term->scrollback;\r
+           treeindex = y + altlines + count234(term->scrollback);\r
+       } else {\r
+           whichtree = term->alt_screen;\r
+           treeindex = y + term->alt_sblines;\r
+           /* treeindex = y + count234(term->alt_screen); */\r
+       }\r
+    }\r
+    if (whichtree == term->scrollback) {\r
+       unsigned char *cline = index234(whichtree, treeindex);\r
+       line = decompressline(cline, NULL);\r
+    } else {\r
+       line = index234(whichtree, treeindex);\r
+    }\r
+\r
+    /* We assume that we don't screw up and retrieve something out of range. */\r
+    if (line == NULL) {\r
+       fatalbox("line==NULL in terminal.c\n"\r
+                "lineno=%d y=%d w=%d h=%d\n"\r
+                "count(scrollback=%p)=%d\n"\r
+                "count(screen=%p)=%d\n"\r
+                "count(alt=%p)=%d alt_sblines=%d\n"\r
+                "whichtree=%p treeindex=%d\n\n"\r
+                "Please contact <putty@projects.tartarus.org> "\r
+                "and pass on the above information.",\r
+                lineno, y, term->cols, term->rows,\r
+                term->scrollback, count234(term->scrollback),\r
+                term->screen, count234(term->screen),\r
+                term->alt_screen, count234(term->alt_screen), term->alt_sblines,\r
+                whichtree, treeindex);\r
+    }\r
+    assert(line != NULL);\r
+\r
+    resizeline(term, line, term->cols);\r
+    /* FIXME: should we sort the compressed scrollback out here? */\r
+\r
+    return line;\r
+}\r
+\r
+#define lineptr(x) (lineptr)(term,x,__LINE__,FALSE)\r
+#define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE)\r
+\r
+static void term_schedule_tblink(Terminal *term);\r
+static void term_schedule_cblink(Terminal *term);\r
+\r
+static void term_timer(void *ctx, long now)\r
+{\r
+    Terminal *term = (Terminal *)ctx;\r
+    int update = FALSE;\r
+\r
+    if (term->tblink_pending && now - term->next_tblink >= 0) {\r
+       term->tblinker = !term->tblinker;\r
+       term->tblink_pending = FALSE;\r
+       term_schedule_tblink(term);\r
+       update = TRUE;\r
+    }\r
+\r
+    if (term->cblink_pending && now - term->next_cblink >= 0) {\r
+       term->cblinker = !term->cblinker;\r
+       term->cblink_pending = FALSE;\r
+       term_schedule_cblink(term);\r
+       update = TRUE;\r
+    }\r
+\r
+    if (term->in_vbell && now - term->vbell_end >= 0) {\r
+       term->in_vbell = FALSE;\r
+       update = TRUE;\r
+    }\r
+\r
+    if (update ||\r
+       (term->window_update_pending && now - term->next_update >= 0))\r
+       term_update(term);\r
+}\r
+\r
+static void term_schedule_update(Terminal *term)\r
+{\r
+    if (!term->window_update_pending) {\r
+       term->window_update_pending = TRUE;\r
+       term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term);\r
+    }\r
+}\r
+\r
+/*\r
+ * Call this whenever the terminal window state changes, to queue\r
+ * an update.\r
+ */\r
+static void seen_disp_event(Terminal *term)\r
+{\r
+    term->seen_disp_event = TRUE;      /* for scrollback-reset-on-activity */\r
+    term_schedule_update(term);\r
+}\r
+\r
+/*\r
+ * Call when the terminal's blinking-text settings change, or when\r
+ * a text blink has just occurred.\r
+ */\r
+static void term_schedule_tblink(Terminal *term)\r
+{\r
+    if (term->blink_is_real) {\r
+       if (!term->tblink_pending)\r
+           term->next_tblink = schedule_timer(TBLINK_DELAY, term_timer, term);\r
+       term->tblink_pending = TRUE;\r
+    } else {\r
+       term->tblinker = 1;            /* reset when not in use */\r
+       term->tblink_pending = FALSE;\r
+    }\r
+}\r
+\r
+/*\r
+ * Likewise with cursor blinks.\r
+ */\r
+static void term_schedule_cblink(Terminal *term)\r
+{\r
+    if (term->cfg.blink_cur && term->has_focus) {\r
+       if (!term->cblink_pending)\r
+           term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term);\r
+       term->cblink_pending = TRUE;\r
+    } else {\r
+       term->cblinker = 1;            /* reset when not in use */\r
+       term->cblink_pending = FALSE;\r
+    }\r
+}\r
+\r
+/*\r
+ * Call to reset cursor blinking on new output.\r
+ */\r
+static void term_reset_cblink(Terminal *term)\r
+{\r
+    seen_disp_event(term);\r
+    term->cblinker = 1;\r
+    term->cblink_pending = FALSE;\r
+    term_schedule_cblink(term);\r
+}\r
+\r
+/*\r
+ * Call to begin a visual bell.\r
+ */\r
+static void term_schedule_vbell(Terminal *term, int already_started,\r
+                               long startpoint)\r
+{\r
+    long ticks_already_gone;\r
+\r
+    if (already_started)\r
+       ticks_already_gone = GETTICKCOUNT() - startpoint;\r
+    else\r
+       ticks_already_gone = 0;\r
+\r
+    if (ticks_already_gone < VBELL_DELAY) {\r
+       term->in_vbell = TRUE;\r
+       term->vbell_end = schedule_timer(VBELL_DELAY - ticks_already_gone,\r
+                                        term_timer, term);\r
+    } else {\r
+       term->in_vbell = FALSE;\r
+    }\r
+}\r
+\r
+/*\r
+ * Set up power-on settings for the terminal.\r
+ * If 'clear' is false, don't actually clear the primary screen, and\r
+ * position the cursor below the last non-blank line (scrolling if\r
+ * necessary).\r
+ */\r
+static void power_on(Terminal *term, int clear)\r
+{\r
+    term->alt_x = term->alt_y = 0;\r
+    term->savecurs.x = term->savecurs.y = 0;\r
+    term->alt_savecurs.x = term->alt_savecurs.y = 0;\r
+    term->alt_t = term->marg_t = 0;\r
+    if (term->rows != -1)\r
+       term->alt_b = term->marg_b = term->rows - 1;\r
+    else\r
+       term->alt_b = term->marg_b = 0;\r
+    if (term->cols != -1) {\r
+       int i;\r
+       for (i = 0; i < term->cols; i++)\r
+           term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);\r
+    }\r
+    term->alt_om = term->dec_om = term->cfg.dec_om;\r
+    term->alt_ins = term->insert = FALSE;\r
+    term->alt_wnext = term->wrapnext =\r
+        term->save_wnext = term->alt_save_wnext = FALSE;\r
+    term->alt_wrap = term->wrap = term->cfg.wrap_mode;\r
+    term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0;\r
+    term->alt_utf = term->utf = term->save_utf = term->alt_save_utf = 0;\r
+    term->utf_state = 0;\r
+    term->alt_sco_acs = term->sco_acs =\r
+        term->save_sco_acs = term->alt_save_sco_acs = 0;\r
+    term->cset_attr[0] = term->cset_attr[1] =\r
+        term->save_csattr = term->alt_save_csattr = CSET_ASCII;\r
+    term->rvideo = 0;\r
+    term->in_vbell = FALSE;\r
+    term->cursor_on = 1;\r
+    term->big_cursor = 0;\r
+    term->default_attr = term->save_attr =\r
+       term->alt_save_attr = term->curr_attr = ATTR_DEFAULT;\r
+    term->term_editing = term->term_echoing = FALSE;\r
+    term->app_cursor_keys = term->cfg.app_cursor;\r
+    term->app_keypad_keys = term->cfg.app_keypad;\r
+    term->use_bce = term->cfg.bce;\r
+    term->blink_is_real = term->cfg.blinktext;\r
+    term->erase_char = term->basic_erase_char;\r
+    term->alt_which = 0;\r
+    term_print_finish(term);\r
+    term->xterm_mouse = 0;\r
+    set_raw_mouse_mode(term->frontend, FALSE);\r
+    {\r
+       int i;\r
+       for (i = 0; i < 256; i++)\r
+           term->wordness[i] = term->cfg.wordness[i];\r
+    }\r
+    if (term->screen) {\r
+       swap_screen(term, 1, FALSE, FALSE);\r
+       erase_lots(term, FALSE, TRUE, TRUE);\r
+       swap_screen(term, 0, FALSE, FALSE);\r
+       if (clear)\r
+           erase_lots(term, FALSE, TRUE, TRUE);\r
+       term->curs.y = find_last_nonempty_line(term, term->screen) + 1;\r
+       if (term->curs.y == term->rows) {\r
+           term->curs.y--;\r
+           scroll(term, 0, term->rows - 1, 1, TRUE);\r
+       }\r
+    } else {\r
+       term->curs.y = 0;\r
+    }\r
+    term->curs.x = 0;\r
+    term_schedule_tblink(term);\r
+    term_schedule_cblink(term);\r
+}\r
+\r
+/*\r
+ * Force a screen update.\r
+ */\r
+void term_update(Terminal *term)\r
+{\r
+    Context ctx;\r
+\r
+    term->window_update_pending = FALSE;\r
+\r
+    ctx = get_ctx(term->frontend);\r
+    if (ctx) {\r
+       int need_sbar_update = term->seen_disp_event;\r
+       if (term->seen_disp_event && term->cfg.scroll_on_disp) {\r
+           term->disptop = 0;         /* return to main screen */\r
+           term->seen_disp_event = 0;\r
+           need_sbar_update = TRUE;\r
+       }\r
+\r
+       if (need_sbar_update)\r
+           update_sbar(term);\r
+       do_paint(term, ctx, TRUE);\r
+       sys_cursor(term->frontend, term->curs.x, term->curs.y - term->disptop);\r
+       free_ctx(ctx);\r
+    }\r
+}\r
+\r
+/*\r
+ * Called from front end when a keypress occurs, to trigger\r
+ * anything magical that needs to happen in that situation.\r
+ */\r
+void term_seen_key_event(Terminal *term)\r
+{\r
+    /*\r
+     * On any keypress, clear the bell overload mechanism\r
+     * completely, on the grounds that large numbers of\r
+     * beeps coming from deliberate key action are likely\r
+     * to be intended (e.g. beeps from filename completion\r
+     * blocking repeatedly).\r
+     */\r
+    term->beep_overloaded = FALSE;\r
+    while (term->beephead) {\r
+       struct beeptime *tmp = term->beephead;\r
+       term->beephead = tmp->next;\r
+       sfree(tmp);\r
+    }\r
+    term->beeptail = NULL;\r
+    term->nbeeps = 0;\r
+\r
+    /*\r
+     * Reset the scrollback on keypress, if we're doing that.\r
+     */\r
+    if (term->cfg.scroll_on_key) {\r
+       term->disptop = 0;             /* return to main screen */\r
+       seen_disp_event(term);\r
+    }\r
+}\r
+\r
+/*\r
+ * Same as power_on(), but an external function.\r
+ */\r
+void term_pwron(Terminal *term, int clear)\r
+{\r
+    power_on(term, clear);\r
+    if (term->ldisc)                  /* cause ldisc to notice changes */\r
+       ldisc_send(term->ldisc, NULL, 0, 0);\r
+    term->disptop = 0;\r
+    deselect(term);\r
+    term_update(term);\r
+}\r
+\r
+static void set_erase_char(Terminal *term)\r
+{\r
+    term->erase_char = term->basic_erase_char;\r
+    if (term->use_bce)\r
+       term->erase_char.attr = (term->curr_attr &\r
+                                (ATTR_FGMASK | ATTR_BGMASK));\r
+}\r
+\r
+/*\r
+ * When the user reconfigures us, we need to check the forbidden-\r
+ * alternate-screen config option, disable raw mouse mode if the\r
+ * user has disabled mouse reporting, and abandon a print job if\r
+ * the user has disabled printing.\r
+ */\r
+void term_reconfig(Terminal *term, Config *cfg)\r
+{\r
+    /*\r
+     * Before adopting the new config, check all those terminal\r
+     * settings which control power-on defaults; and if they've\r
+     * changed, we will modify the current state as well as the\r
+     * default one. The full list is: Auto wrap mode, DEC Origin\r
+     * Mode, BCE, blinking text, character classes.\r
+     */\r
+    int reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass;\r
+    int i;\r
+\r
+    reset_wrap = (term->cfg.wrap_mode != cfg->wrap_mode);\r
+    reset_decom = (term->cfg.dec_om != cfg->dec_om);\r
+    reset_bce = (term->cfg.bce != cfg->bce);\r
+    reset_tblink = (term->cfg.blinktext != cfg->blinktext);\r
+    reset_charclass = 0;\r
+    for (i = 0; i < lenof(term->cfg.wordness); i++)\r
+       if (term->cfg.wordness[i] != cfg->wordness[i])\r
+           reset_charclass = 1;\r
+\r
+    /*\r
+     * If the bidi or shaping settings have changed, flush the bidi\r
+     * cache completely.\r
+     */\r
+    if (term->cfg.arabicshaping != cfg->arabicshaping ||\r
+       term->cfg.bidi != cfg->bidi) {\r
+       for (i = 0; i < term->bidi_cache_size; i++) {\r
+           sfree(term->pre_bidi_cache[i].chars);\r
+           sfree(term->post_bidi_cache[i].chars);\r
+           term->pre_bidi_cache[i].width = -1;\r
+           term->pre_bidi_cache[i].chars = NULL;\r
+           term->post_bidi_cache[i].width = -1;\r
+           term->post_bidi_cache[i].chars = NULL;\r
+       }\r
+    }\r
+\r
+    term->cfg = *cfg;                 /* STRUCTURE COPY */\r
+\r
+    if (reset_wrap)\r
+       term->alt_wrap = term->wrap = term->cfg.wrap_mode;\r
+    if (reset_decom)\r
+       term->alt_om = term->dec_om = term->cfg.dec_om;\r
+    if (reset_bce) {\r
+       term->use_bce = term->cfg.bce;\r
+       set_erase_char(term);\r
+    }\r
+    if (reset_tblink) {\r
+       term->blink_is_real = term->cfg.blinktext;\r
+    }\r
+    if (reset_charclass)\r
+       for (i = 0; i < 256; i++)\r
+           term->wordness[i] = term->cfg.wordness[i];\r
+\r
+    if (term->cfg.no_alt_screen)\r
+       swap_screen(term, 0, FALSE, FALSE);\r
+    if (term->cfg.no_mouse_rep) {\r
+       term->xterm_mouse = 0;\r
+       set_raw_mouse_mode(term->frontend, 0);\r
+    }\r
+    if (term->cfg.no_remote_charset) {\r
+       term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII;\r
+       term->sco_acs = term->alt_sco_acs = 0;\r
+       term->utf = 0;\r
+    }\r
+    if (!*term->cfg.printer) {\r
+       term_print_finish(term);\r
+    }\r
+    term_schedule_tblink(term);\r
+    term_schedule_cblink(term);\r
+}\r
+\r
+/*\r
+ * Clear the scrollback.\r
+ */\r
+void term_clrsb(Terminal *term)\r
+{\r
+    unsigned char *line;\r
+    term->disptop = 0;\r
+    while ((line = delpos234(term->scrollback, 0)) != NULL) {\r
+       sfree(line);            /* this is compressed data, not a termline */\r
+    }\r
+    term->tempsblines = 0;\r
+    term->alt_sblines = 0;\r
+    update_sbar(term);\r
+}\r
+\r
+/*\r
+ * Initialise the terminal.\r
+ */\r
+Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,\r
+                   void *frontend)\r
+{\r
+    Terminal *term;\r
+\r
+    /*\r
+     * Allocate a new Terminal structure and initialise the fields\r
+     * that need it.\r
+     */\r
+    term = snew(Terminal);\r
+    term->frontend = frontend;\r
+    term->ucsdata = ucsdata;\r
+    term->cfg = *mycfg;                       /* STRUCTURE COPY */\r
+    term->logctx = NULL;\r
+    term->compatibility_level = TM_PUTTY;\r
+    strcpy(term->id_string, "\033[?6c");\r
+    term->cblink_pending = term->tblink_pending = FALSE;\r
+    term->paste_buffer = NULL;\r
+    term->paste_len = 0;\r
+    term->last_paste = 0;\r
+    bufchain_init(&term->inbuf);\r
+    bufchain_init(&term->printer_buf);\r
+    term->printing = term->only_printing = FALSE;\r
+    term->print_job = NULL;\r
+    term->vt52_mode = FALSE;\r
+    term->cr_lf_return = FALSE;\r
+    term->seen_disp_event = FALSE;\r
+    term->mouse_is_down = FALSE;\r
+    term->reset_132 = FALSE;\r
+    term->cblinker = term->tblinker = 0;\r
+    term->has_focus = 1;\r
+    term->repeat_off = FALSE;\r
+    term->termstate = TOPLEVEL;\r
+    term->selstate = NO_SELECTION;\r
+    term->curstype = 0;\r
+\r
+    term->screen = term->alt_screen = term->scrollback = NULL;\r
+    term->tempsblines = 0;\r
+    term->alt_sblines = 0;\r
+    term->disptop = 0;\r
+    term->disptext = NULL;\r
+    term->dispcursx = term->dispcursy = -1;\r
+    term->tabs = NULL;\r
+    deselect(term);\r
+    term->rows = term->cols = -1;\r
+    power_on(term, TRUE);\r
+    term->beephead = term->beeptail = NULL;\r
+#ifdef OPTIMISE_SCROLL\r
+    term->scrollhead = term->scrolltail = NULL;\r
+#endif /* OPTIMISE_SCROLL */\r
+    term->nbeeps = 0;\r
+    term->lastbeep = FALSE;\r
+    term->beep_overloaded = FALSE;\r
+    term->attr_mask = 0xffffffff;\r
+    term->resize_fn = NULL;\r
+    term->resize_ctx = NULL;\r
+    term->in_term_out = FALSE;\r
+    term->ltemp = NULL;\r
+    term->ltemp_size = 0;\r
+    term->wcFrom = NULL;\r
+    term->wcTo = NULL;\r
+    term->wcFromTo_size = 0;\r
+\r
+    term->window_update_pending = FALSE;\r
+\r
+    term->bidi_cache_size = 0;\r
+    term->pre_bidi_cache = term->post_bidi_cache = NULL;\r
+\r
+    /* FULL-TERMCHAR */\r
+    term->basic_erase_char.chr = CSET_ASCII | ' ';\r
+    term->basic_erase_char.attr = ATTR_DEFAULT;\r
+    term->basic_erase_char.cc_next = 0;\r
+    term->erase_char = term->basic_erase_char;\r
+\r
+    return term;\r
+}\r
+\r
+void term_free(Terminal *term)\r
+{\r
+    termline *line;\r
+    struct beeptime *beep;\r
+    int i;\r
+\r
+    while ((line = delpos234(term->scrollback, 0)) != NULL)\r
+       sfree(line);                   /* compressed data, not a termline */\r
+    freetree234(term->scrollback);\r
+    while ((line = delpos234(term->screen, 0)) != NULL)\r
+       freeline(line);\r
+    freetree234(term->screen);\r
+    while ((line = delpos234(term->alt_screen, 0)) != NULL)\r
+       freeline(line);\r
+    freetree234(term->alt_screen);\r
+    if (term->disptext) {\r
+       for (i = 0; i < term->rows; i++)\r
+           freeline(term->disptext[i]);\r
+    }\r
+    sfree(term->disptext);\r
+    while (term->beephead) {\r
+       beep = term->beephead;\r
+       term->beephead = beep->next;\r
+       sfree(beep);\r
+    }\r
+    bufchain_clear(&term->inbuf);\r
+    if(term->print_job)\r
+       printer_finish_job(term->print_job);\r
+    bufchain_clear(&term->printer_buf);\r
+    sfree(term->paste_buffer);\r
+    sfree(term->ltemp);\r
+    sfree(term->wcFrom);\r
+    sfree(term->wcTo);\r
+\r
+    for (i = 0; i < term->bidi_cache_size; i++) {\r
+       sfree(term->pre_bidi_cache[i].chars);\r
+       sfree(term->post_bidi_cache[i].chars);\r
+    }\r
+    sfree(term->pre_bidi_cache);\r
+    sfree(term->post_bidi_cache);\r
+\r
+    expire_timer_context(term);\r
+\r
+    sfree(term);\r
+}\r
+\r
+/*\r
+ * Set up the terminal for a given size.\r
+ */\r
+void term_size(Terminal *term, int newrows, int newcols, int newsavelines)\r
+{\r
+    tree234 *newalt;\r
+    termline **newdisp, *line;\r
+    int i, j, oldrows = term->rows;\r
+    int sblen;\r
+    int save_alt_which = term->alt_which;\r
+\r
+    if (newrows == term->rows && newcols == term->cols &&\r
+       newsavelines == term->savelines)\r
+       return;                        /* nothing to do */\r
+\r
+    /* Behave sensibly if we're given zero (or negative) rows/cols */\r
+\r
+    if (newrows < 1) newrows = 1;\r
+    if (newcols < 1) newcols = 1;\r
+\r
+    deselect(term);\r
+    swap_screen(term, 0, FALSE, FALSE);\r
+\r
+    term->alt_t = term->marg_t = 0;\r
+    term->alt_b = term->marg_b = newrows - 1;\r
+\r
+    if (term->rows == -1) {\r
+       term->scrollback = newtree234(NULL);\r
+       term->screen = newtree234(NULL);\r
+       term->tempsblines = 0;\r
+       term->rows = 0;\r
+    }\r
+\r
+    /*\r
+     * Resize the screen and scrollback. We only need to shift\r
+     * lines around within our data structures, because lineptr()\r
+     * will take care of resizing each individual line if\r
+     * necessary. So:\r
+     * \r
+     *  - If the new screen is longer, we shunt lines in from temporary\r
+     *    scrollback if possible, otherwise we add new blank lines at\r
+     *    the bottom.\r
+     *\r
+     *  - If the new screen is shorter, we remove any blank lines at\r
+     *    the bottom if possible, otherwise shunt lines above the cursor\r
+     *    to scrollback if possible, otherwise delete lines below the\r
+     *    cursor.\r
+     * \r
+     *  - Then, if the new scrollback length is less than the\r
+     *    amount of scrollback we actually have, we must throw some\r
+     *    away.\r
+     */\r
+    sblen = count234(term->scrollback);\r
+    /* Do this loop to expand the screen if newrows > rows */\r
+    assert(term->rows == count234(term->screen));\r
+    while (term->rows < newrows) {\r
+       if (term->tempsblines > 0) {\r
+           unsigned char *cline;\r
+           /* Insert a line from the scrollback at the top of the screen. */\r
+           assert(sblen >= term->tempsblines);\r
+           cline = delpos234(term->scrollback, --sblen);\r
+           line = decompressline(cline, NULL);\r
+           sfree(cline);\r
+           line->temporary = FALSE;   /* reconstituted line is now real */\r
+           term->tempsblines -= 1;\r
+           addpos234(term->screen, line, 0);\r
+           term->curs.y += 1;\r
+           term->savecurs.y += 1;\r
+           term->alt_y += 1;\r
+           term->alt_savecurs.y += 1;\r
+       } else {\r
+           /* Add a new blank line at the bottom of the screen. */\r
+           line = newline(term, newcols, FALSE);\r
+           addpos234(term->screen, line, count234(term->screen));\r
+       }\r
+       term->rows += 1;\r
+    }\r
+    /* Do this loop to shrink the screen if newrows < rows */\r
+    while (term->rows > newrows) {\r
+       if (term->curs.y < term->rows - 1) {\r
+           /* delete bottom row, unless it contains the cursor */\r
+           sfree(delpos234(term->screen, term->rows - 1));\r
+       } else {\r
+           /* push top row to scrollback */\r
+           line = delpos234(term->screen, 0);\r
+           addpos234(term->scrollback, compressline(line), sblen++);\r
+           freeline(line);\r
+           term->tempsblines += 1;\r
+           term->curs.y -= 1;\r
+           term->savecurs.y -= 1;\r
+           term->alt_y -= 1;\r
+           term->alt_savecurs.y -= 1;\r
+       }\r
+       term->rows -= 1;\r
+    }\r
+    assert(term->rows == newrows);\r
+    assert(count234(term->screen) == newrows);\r
+\r
+    /* Delete any excess lines from the scrollback. */\r
+    while (sblen > newsavelines) {\r
+       line = delpos234(term->scrollback, 0);\r
+       sfree(line);\r
+       sblen--;\r
+    }\r
+    if (sblen < term->tempsblines)\r
+       term->tempsblines = sblen;\r
+    assert(count234(term->scrollback) <= newsavelines);\r
+    assert(count234(term->scrollback) >= term->tempsblines);\r
+    term->disptop = 0;\r
+\r
+    /* Make a new displayed text buffer. */\r
+    newdisp = snewn(newrows, termline *);\r
+    for (i = 0; i < newrows; i++) {\r
+       newdisp[i] = newline(term, newcols, FALSE);\r
+       for (j = 0; j < newcols; j++)\r
+           newdisp[i]->chars[j].attr = ATTR_INVALID;\r
+    }\r
+    if (term->disptext) {\r
+       for (i = 0; i < oldrows; i++)\r
+           freeline(term->disptext[i]);\r
+    }\r
+    sfree(term->disptext);\r
+    term->disptext = newdisp;\r
+    term->dispcursx = term->dispcursy = -1;\r
+\r
+    /* Make a new alternate screen. */\r
+    newalt = newtree234(NULL);\r
+    for (i = 0; i < newrows; i++) {\r
+       line = newline(term, newcols, TRUE);\r
+       addpos234(newalt, line, i);\r
+    }\r
+    if (term->alt_screen) {\r
+       while (NULL != (line = delpos234(term->alt_screen, 0)))\r
+           freeline(line);\r
+       freetree234(term->alt_screen);\r
+    }\r
+    term->alt_screen = newalt;\r
+    term->alt_sblines = 0;\r
+\r
+    term->tabs = sresize(term->tabs, newcols, unsigned char);\r
+    {\r
+       int i;\r
+       for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++)\r
+           term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);\r
+    }\r
+\r
+    /* Check that the cursor positions are still valid. */\r
+    if (term->savecurs.y < 0)\r
+       term->savecurs.y = 0;\r
+    if (term->savecurs.y >= newrows)\r
+       term->savecurs.y = newrows - 1;\r
+    if (term->savecurs.x >= newcols)\r
+       term->savecurs.x = newcols - 1;\r
+    if (term->alt_savecurs.y < 0)\r
+       term->alt_savecurs.y = 0;\r
+    if (term->alt_savecurs.y >= newrows)\r
+       term->alt_savecurs.y = newrows - 1;\r
+    if (term->alt_savecurs.x >= newcols)\r
+       term->alt_savecurs.x = newcols - 1;\r
+    if (term->curs.y < 0)\r
+       term->curs.y = 0;\r
+    if (term->curs.y >= newrows)\r
+       term->curs.y = newrows - 1;\r
+    if (term->curs.x >= newcols)\r
+       term->curs.x = newcols - 1;\r
+    if (term->alt_y < 0)\r
+       term->alt_y = 0;\r
+    if (term->alt_y >= newrows)\r
+       term->alt_y = newrows - 1;\r
+    if (term->alt_x >= newcols)\r
+       term->alt_x = newcols - 1;\r
+    term->alt_x = term->alt_y = 0;\r
+    term->wrapnext = term->alt_wnext = FALSE;\r
+\r
+    term->rows = newrows;\r
+    term->cols = newcols;\r
+    term->savelines = newsavelines;\r
+\r
+    swap_screen(term, save_alt_which, FALSE, FALSE);\r
+\r
+    update_sbar(term);\r
+    term_update(term);\r
+    if (term->resize_fn)\r
+       term->resize_fn(term->resize_ctx, term->cols, term->rows);\r
+}\r
+\r
+/*\r
+ * Hand a function and context pointer to the terminal which it can\r
+ * use to notify a back end of resizes.\r
+ */\r
+void term_provide_resize_fn(Terminal *term,\r
+                           void (*resize_fn)(void *, int, int),\r
+                           void *resize_ctx)\r
+{\r
+    term->resize_fn = resize_fn;\r
+    term->resize_ctx = resize_ctx;\r
+    if (resize_fn && term->cols > 0 && term->rows > 0)\r
+       resize_fn(resize_ctx, term->cols, term->rows);\r
+}\r
+\r
+/* Find the bottom line on the screen that has any content.\r
+ * If only the top line has content, returns 0.\r
+ * If no lines have content, return -1.\r
+ */ \r
+static int find_last_nonempty_line(Terminal * term, tree234 * screen)\r
+{\r
+    int i;\r
+    for (i = count234(screen) - 1; i >= 0; i--) {\r
+       termline *line = index234(screen, i);\r
+       int j;\r
+       for (j = 0; j < line->cols; j++)\r
+           if (!termchars_equal(&line->chars[j], &term->erase_char))\r
+               break;\r
+       if (j != line->cols) break;\r
+    }\r
+    return i;\r
+}\r
+\r
+/*\r
+ * Swap screens. If `reset' is TRUE and we have been asked to\r
+ * switch to the alternate screen, we must bring most of its\r
+ * configuration from the main screen and erase the contents of the\r
+ * alternate screen completely. (This is even true if we're already\r
+ * on it! Blame xterm.)\r
+ */\r
+static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos)\r
+{\r
+    int t;\r
+    pos tp;\r
+    tree234 *ttr;\r
+\r
+    if (!which)\r
+       reset = FALSE;                 /* do no weird resetting if which==0 */\r
+\r
+    if (which != term->alt_which) {\r
+       term->alt_which = which;\r
+\r
+       ttr = term->alt_screen;\r
+       term->alt_screen = term->screen;\r
+       term->screen = ttr;\r
+       term->alt_sblines = find_last_nonempty_line(term, term->alt_screen) + 1;\r
+       t = term->curs.x;\r
+       if (!reset && !keep_cur_pos)\r
+           term->curs.x = term->alt_x;\r
+       term->alt_x = t;\r
+       t = term->curs.y;\r
+       if (!reset && !keep_cur_pos)\r
+           term->curs.y = term->alt_y;\r
+       term->alt_y = t;\r
+       t = term->marg_t;\r
+       if (!reset) term->marg_t = term->alt_t;\r
+       term->alt_t = t;\r
+       t = term->marg_b;\r
+       if (!reset) term->marg_b = term->alt_b;\r
+       term->alt_b = t;\r
+       t = term->dec_om;\r
+       if (!reset) term->dec_om = term->alt_om;\r
+       term->alt_om = t;\r
+       t = term->wrap;\r
+       if (!reset) term->wrap = term->alt_wrap;\r
+       term->alt_wrap = t;\r
+       t = term->wrapnext;\r
+       if (!reset) term->wrapnext = term->alt_wnext;\r
+       term->alt_wnext = t;\r
+       t = term->insert;\r
+       if (!reset) term->insert = term->alt_ins;\r
+       term->alt_ins = t;\r
+       t = term->cset;\r
+       if (!reset) term->cset = term->alt_cset;\r
+       term->alt_cset = t;\r
+       t = term->utf;\r
+       if (!reset) term->utf = term->alt_utf;\r
+       term->alt_utf = t;\r
+       t = term->sco_acs;\r
+       if (!reset) term->sco_acs = term->alt_sco_acs;\r
+       term->alt_sco_acs = t;\r
+\r
+       tp = term->savecurs;\r
+       if (!reset && !keep_cur_pos)\r
+           term->savecurs = term->alt_savecurs;\r
+       term->alt_savecurs = tp;\r
+        t = term->save_cset;\r
+        if (!reset && !keep_cur_pos)\r
+            term->save_cset = term->alt_save_cset;\r
+        term->alt_save_cset = t;\r
+        t = term->save_csattr;\r
+        if (!reset && !keep_cur_pos)\r
+            term->save_csattr = term->alt_save_csattr;\r
+        term->alt_save_csattr = t;\r
+        t = term->save_attr;\r
+        if (!reset && !keep_cur_pos)\r
+            term->save_attr = term->alt_save_attr;\r
+        term->alt_save_attr = t;\r
+        t = term->save_utf;\r
+        if (!reset && !keep_cur_pos)\r
+            term->save_utf = term->alt_save_utf;\r
+        term->alt_save_utf = t;\r
+        t = term->save_wnext;\r
+        if (!reset && !keep_cur_pos)\r
+            term->save_wnext = term->alt_save_wnext;\r
+        term->alt_save_wnext = t;\r
+        t = term->save_sco_acs;\r
+        if (!reset && !keep_cur_pos)\r
+            term->save_sco_acs = term->alt_save_sco_acs;\r
+        term->alt_save_sco_acs = t;\r
+    }\r
+\r
+    if (reset && term->screen) {\r
+       /*\r
+        * Yes, this _is_ supposed to honour background-colour-erase.\r
+        */\r
+       erase_lots(term, FALSE, TRUE, TRUE);\r
+    }\r
+}\r
+\r
+/*\r
+ * Update the scroll bar.\r
+ */\r
+static void update_sbar(Terminal *term)\r
+{\r
+    int nscroll = sblines(term);\r
+    set_sbar(term->frontend, nscroll + term->rows,\r
+            nscroll + term->disptop, term->rows);\r
+}\r
+\r
+/*\r
+ * Check whether the region bounded by the two pointers intersects\r
+ * the scroll region, and de-select the on-screen selection if so.\r
+ */\r
+static void check_selection(Terminal *term, pos from, pos to)\r
+{\r
+    if (poslt(from, term->selend) && poslt(term->selstart, to))\r
+       deselect(term);\r
+}\r
+\r
+/*\r
+ * Scroll the screen. (`lines' is +ve for scrolling forward, -ve\r
+ * for backward.) `sb' is TRUE if the scrolling is permitted to\r
+ * affect the scrollback buffer.\r
+ */\r
+static void scroll(Terminal *term, int topline, int botline, int lines, int sb)\r
+{\r
+    termline *line;\r
+    int i, seltop;\r
+#ifdef OPTIMISE_SCROLL\r
+    int olddisptop, shift;\r
+#endif /* OPTIMISE_SCROLL */\r
+\r
+    if (topline != 0 || term->alt_which != 0)\r
+       sb = FALSE;\r
+\r
+#ifdef OPTIMISE_SCROLL\r
+    olddisptop = term->disptop;\r
+    shift = lines;\r
+#endif /* OPTIMISE_SCROLL */\r
+    if (lines < 0) {\r
+       while (lines < 0) {\r
+           line = delpos234(term->screen, botline);\r
+            resizeline(term, line, term->cols);\r
+           for (i = 0; i < term->cols; i++)\r
+               copy_termchar(line, i, &term->erase_char);\r
+           line->lattr = LATTR_NORM;\r
+           addpos234(term->screen, line, topline);\r
+\r
+           if (term->selstart.y >= topline && term->selstart.y <= botline) {\r
+               term->selstart.y++;\r
+               if (term->selstart.y > botline) {\r
+                   term->selstart.y = botline + 1;\r
+                   term->selstart.x = 0;\r
+               }\r
+           }\r
+           if (term->selend.y >= topline && term->selend.y <= botline) {\r
+               term->selend.y++;\r
+               if (term->selend.y > botline) {\r
+                   term->selend.y = botline + 1;\r
+                   term->selend.x = 0;\r
+               }\r
+           }\r
+\r
+           lines++;\r
+       }\r
+    } else {\r
+       while (lines > 0) {\r
+           line = delpos234(term->screen, topline);\r
+#ifdef TERM_CC_DIAGS\r
+           cc_check(line);\r
+#endif\r
+           if (sb && term->savelines > 0) {\r
+               int sblen = count234(term->scrollback);\r
+               /*\r
+                * We must add this line to the scrollback. We'll\r
+                * remove a line from the top of the scrollback if\r
+                * the scrollback is full.\r
+                */\r
+               if (sblen == term->savelines) {\r
+                   unsigned char *cline;\r
+\r
+                   sblen--;\r
+                   cline = delpos234(term->scrollback, 0);\r
+                   sfree(cline);\r
+               } else\r
+                   term->tempsblines += 1;\r
+\r
+               addpos234(term->scrollback, compressline(line), sblen);\r
+\r
+               /* now `line' itself can be reused as the bottom line */\r
+\r
+               /*\r
+                * If the user is currently looking at part of the\r
+                * scrollback, and they haven't enabled any options\r
+                * that are going to reset the scrollback as a\r
+                * result of this movement, then the chances are\r
+                * they'd like to keep looking at the same line. So\r
+                * we move their viewpoint at the same rate as the\r
+                * scroll, at least until their viewpoint hits the\r
+                * top end of the scrollback buffer, at which point\r
+                * we don't have the choice any more.\r
+                * \r
+                * Thanks to Jan Holmen Holsten for the idea and\r
+                * initial implementation.\r
+                */\r
+               if (term->disptop > -term->savelines && term->disptop < 0)\r
+                   term->disptop--;\r
+           }\r
+            resizeline(term, line, term->cols);\r
+           for (i = 0; i < term->cols; i++)\r
+               copy_termchar(line, i, &term->erase_char);\r
+           line->lattr = LATTR_NORM;\r
+           addpos234(term->screen, line, botline);\r
+\r
+           /*\r
+            * If the selection endpoints move into the scrollback,\r
+            * we keep them moving until they hit the top. However,\r
+            * of course, if the line _hasn't_ moved into the\r
+            * scrollback then we don't do this, and cut them off\r
+            * at the top of the scroll region.\r
+            * \r
+            * This applies to selstart and selend (for an existing\r
+            * selection), and also selanchor (for one being\r
+            * selected as we speak).\r
+            */\r
+           seltop = sb ? -term->savelines : topline;\r
+\r
+           if (term->selstate != NO_SELECTION) {\r
+               if (term->selstart.y >= seltop &&\r
+                   term->selstart.y <= botline) {\r
+                   term->selstart.y--;\r
+                   if (term->selstart.y < seltop) {\r
+                       term->selstart.y = seltop;\r
+                       term->selstart.x = 0;\r
+                   }\r
+               }\r
+               if (term->selend.y >= seltop && term->selend.y <= botline) {\r
+                   term->selend.y--;\r
+                   if (term->selend.y < seltop) {\r
+                       term->selend.y = seltop;\r
+                       term->selend.x = 0;\r
+                   }\r
+               }\r
+               if (term->selanchor.y >= seltop &&\r
+                   term->selanchor.y <= botline) {\r
+                   term->selanchor.y--;\r
+                   if (term->selanchor.y < seltop) {\r
+                       term->selanchor.y = seltop;\r
+                       term->selanchor.x = 0;\r
+                   }\r
+               }\r
+           }\r
+\r
+           lines--;\r
+       }\r
+    }\r
+#ifdef OPTIMISE_SCROLL\r
+    shift += term->disptop - olddisptop;\r
+    if (shift < term->rows && shift > -term->rows && shift != 0)\r
+       scroll_display(term, topline, botline, shift);\r
+#endif /* OPTIMISE_SCROLL */\r
+}\r
+\r
+#ifdef OPTIMISE_SCROLL\r
+/*\r
+ * Add a scroll of a region on the screen into the pending scroll list.\r
+ * `lines' is +ve for scrolling forward, -ve for backward.\r
+ *\r
+ * If the scroll is on the same area as the last scroll in the list,\r
+ * merge them.\r
+ */\r
+static void save_scroll(Terminal *term, int topline, int botline, int lines)\r
+{\r
+    struct scrollregion *newscroll;\r
+    if (term->scrolltail &&\r
+       term->scrolltail->topline == topline && \r
+       term->scrolltail->botline == botline) {\r
+       term->scrolltail->lines += lines;\r
+    } else {\r
+       newscroll = snew(struct scrollregion);\r
+       newscroll->topline = topline;\r
+       newscroll->botline = botline;\r
+       newscroll->lines = lines;\r
+       newscroll->next = NULL;\r
+\r
+       if (!term->scrollhead)\r
+           term->scrollhead = newscroll;\r
+       else\r
+           term->scrolltail->next = newscroll;\r
+       term->scrolltail = newscroll;\r
+    }\r
+}\r
+\r
+/*\r
+ * Scroll the physical display, and our conception of it in disptext.\r
+ */\r
+static void scroll_display(Terminal *term, int topline, int botline, int lines)\r
+{\r
+    int distance, nlines, i, j;\r
+\r
+    distance = lines > 0 ? lines : -lines;\r
+    nlines = botline - topline + 1 - distance;\r
+    if (lines > 0) {\r
+       for (i = 0; i < nlines; i++)\r
+           for (j = 0; j < term->cols; j++)\r
+               copy_termchar(term->disptext[i], j,\r
+                             term->disptext[i+distance]->chars+j);\r
+       if (term->dispcursy >= 0 &&\r
+           term->dispcursy >= topline + distance &&\r
+           term->dispcursy < topline + distance + nlines)\r
+           term->dispcursy -= distance;\r
+       for (i = 0; i < distance; i++)\r
+           for (j = 0; j < term->cols; j++)\r
+               term->disptext[nlines+i]->chars[j].attr |= ATTR_INVALID;\r
+    } else {\r
+       for (i = nlines; i-- ;)\r
+           for (j = 0; j < term->cols; j++)\r
+               copy_termchar(term->disptext[i+distance], j,\r
+                             term->disptext[i]->chars+j);\r
+       if (term->dispcursy >= 0 &&\r
+           term->dispcursy >= topline &&\r
+           term->dispcursy < topline + nlines)\r
+           term->dispcursy += distance;\r
+       for (i = 0; i < distance; i++)\r
+           for (j = 0; j < term->cols; j++)\r
+               term->disptext[i]->chars[j].attr |= ATTR_INVALID;\r
+    }\r
+    save_scroll(term, topline, botline, lines);\r
+}\r
+#endif /* OPTIMISE_SCROLL */\r
+\r
+/*\r
+ * Move the cursor to a given position, clipping at boundaries. We\r
+ * may or may not want to clip at the scroll margin: marg_clip is 0\r
+ * not to, 1 to disallow _passing_ the margins, and 2 to disallow\r
+ * even _being_ outside the margins.\r
+ */\r
+static void move(Terminal *term, int x, int y, int marg_clip)\r
+{\r
+    if (x < 0)\r
+       x = 0;\r
+    if (x >= term->cols)\r
+       x = term->cols - 1;\r
+    if (marg_clip) {\r
+       if ((term->curs.y >= term->marg_t || marg_clip == 2) &&\r
+           y < term->marg_t)\r
+           y = term->marg_t;\r
+       if ((term->curs.y <= term->marg_b || marg_clip == 2) &&\r
+           y > term->marg_b)\r
+           y = term->marg_b;\r
+    }\r
+    if (y < 0)\r
+       y = 0;\r
+    if (y >= term->rows)\r
+       y = term->rows - 1;\r
+    term->curs.x = x;\r
+    term->curs.y = y;\r
+    term->wrapnext = FALSE;\r
+}\r
+\r
+/*\r
+ * Save or restore the cursor and SGR mode.\r
+ */\r
+static void save_cursor(Terminal *term, int save)\r
+{\r
+    if (save) {\r
+       term->savecurs = term->curs;\r
+       term->save_attr = term->curr_attr;\r
+       term->save_cset = term->cset;\r
+       term->save_utf = term->utf;\r
+       term->save_wnext = term->wrapnext;\r
+       term->save_csattr = term->cset_attr[term->cset];\r
+       term->save_sco_acs = term->sco_acs;\r
+    } else {\r
+       term->curs = term->savecurs;\r
+       /* Make sure the window hasn't shrunk since the save */\r
+       if (term->curs.x >= term->cols)\r
+           term->curs.x = term->cols - 1;\r
+       if (term->curs.y >= term->rows)\r
+           term->curs.y = term->rows - 1;\r
+\r
+       term->curr_attr = term->save_attr;\r
+       term->cset = term->save_cset;\r
+       term->utf = term->save_utf;\r
+       term->wrapnext = term->save_wnext;\r
+       /*\r
+        * wrapnext might reset to False if the x position is no\r
+        * longer at the rightmost edge.\r
+        */\r
+       if (term->wrapnext && term->curs.x < term->cols-1)\r
+           term->wrapnext = FALSE;\r
+       term->cset_attr[term->cset] = term->save_csattr;\r
+       term->sco_acs = term->save_sco_acs;\r
+       set_erase_char(term);\r
+    }\r
+}\r
+\r
+/*\r
+ * This function is called before doing _anything_ which affects\r
+ * only part of a line of text. It is used to mark the boundary\r
+ * between two character positions, and it indicates that some sort\r
+ * of effect is going to happen on only one side of that boundary.\r
+ * \r
+ * The effect of this function is to check whether a CJK\r
+ * double-width character is straddling the boundary, and to remove\r
+ * it and replace it with two spaces if so. (Of course, one or\r
+ * other of those spaces is then likely to be replaced with\r
+ * something else again, as a result of whatever happens next.)\r
+ * \r
+ * Also, if the boundary is at the right-hand _edge_ of the screen,\r
+ * it implies something deliberate is being done to the rightmost\r
+ * column position; hence we must clear LATTR_WRAPPED2.\r
+ * \r
+ * The input to the function is the coordinates of the _second_\r
+ * character of the pair.\r
+ */\r
+static void check_boundary(Terminal *term, int x, int y)\r
+{\r
+    termline *ldata;\r
+\r
+    /* Validate input coordinates, just in case. */\r
+    if (x == 0 || x > term->cols)\r
+       return;\r
+\r
+    ldata = scrlineptr(y);\r
+    if (x == term->cols) {\r
+       ldata->lattr &= ~LATTR_WRAPPED2;\r
+    } else {\r
+       if (ldata->chars[x].chr == UCSWIDE) {\r
+           clear_cc(ldata, x-1);\r
+           clear_cc(ldata, x);\r
+           ldata->chars[x-1].chr = ' ' | CSET_ASCII;\r
+           ldata->chars[x] = ldata->chars[x-1];\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Erase a large portion of the screen: the whole screen, or the\r
+ * whole line, or parts thereof.\r
+ */\r
+static void erase_lots(Terminal *term,\r
+                      int line_only, int from_begin, int to_end)\r
+{\r
+    pos start, end;\r
+    int erase_lattr;\r
+    int erasing_lines_from_top = 0;\r
+\r
+    if (line_only) {\r
+       start.y = term->curs.y;\r
+       start.x = 0;\r
+       end.y = term->curs.y + 1;\r
+       end.x = 0;\r
+       erase_lattr = FALSE;\r
+    } else {\r
+       start.y = 0;\r
+       start.x = 0;\r
+       end.y = term->rows;\r
+       end.x = 0;\r
+       erase_lattr = TRUE;\r
+    }\r
+    if (!from_begin) {\r
+       start = term->curs;\r
+    }\r
+    if (!to_end) {\r
+       end = term->curs;\r
+       incpos(end);\r
+    }\r
+    if (!from_begin || !to_end)\r
+       check_boundary(term, term->curs.x, term->curs.y);\r
+    check_selection(term, start, end);\r
+\r
+    /* Clear screen also forces a full window redraw, just in case. */\r
+    if (start.y == 0 && start.x == 0 && end.y == term->rows)\r
+       term_invalidate(term);\r
+\r
+    /* Lines scrolled away shouldn't be brought back on if the terminal\r
+     * resizes. */\r
+    if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr)\r
+       erasing_lines_from_top = 1;\r
+\r
+    if (term->cfg.erase_to_scrollback && erasing_lines_from_top) {\r
+       /* If it's a whole number of lines, starting at the top, and\r
+        * we're fully erasing them, erase by scrolling and keep the\r
+        * lines in the scrollback. */\r
+       int scrolllines = end.y;\r
+       if (end.y == term->rows) {\r
+           /* Shrink until we find a non-empty row.*/\r
+           scrolllines = find_last_nonempty_line(term, term->screen) + 1;\r
+       }\r
+       if (scrolllines > 0)\r
+           scroll(term, 0, scrolllines - 1, scrolllines, TRUE);\r
+    } else {\r
+       termline *ldata = scrlineptr(start.y);\r
+       while (poslt(start, end)) {\r
+           if (start.x == term->cols) {\r
+               if (!erase_lattr)\r
+                   ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);\r
+               else\r
+                   ldata->lattr = LATTR_NORM;\r
+           } else {\r
+               copy_termchar(ldata, start.x, &term->erase_char);\r
+           }\r
+           if (incpos(start) && start.y < term->rows) {\r
+               ldata = scrlineptr(start.y);\r
+           }\r
+       }\r
+    }\r
+\r
+    /* After an erase of lines from the top of the screen, we shouldn't\r
+     * bring the lines back again if the terminal enlarges (since the user or\r
+     * application has explictly thrown them away). */\r
+    if (erasing_lines_from_top && !(term->alt_which))\r
+       term->tempsblines = 0;\r
+}\r
+\r
+/*\r
+ * Insert or delete characters within the current line. n is +ve if\r
+ * insertion is desired, and -ve for deletion.\r
+ */\r
+static void insch(Terminal *term, int n)\r
+{\r
+    int dir = (n < 0 ? -1 : +1);\r
+    int m, j;\r
+    pos cursplus;\r
+    termline *ldata;\r
+\r
+    n = (n < 0 ? -n : n);\r
+    if (n > term->cols - term->curs.x)\r
+       n = term->cols - term->curs.x;\r
+    m = term->cols - term->curs.x - n;\r
+    cursplus.y = term->curs.y;\r
+    cursplus.x = term->curs.x + n;\r
+    check_selection(term, term->curs, cursplus);\r
+    check_boundary(term, term->curs.x, term->curs.y);\r
+    if (dir < 0)\r
+       check_boundary(term, term->curs.x + n, term->curs.y);\r
+    ldata = scrlineptr(term->curs.y);\r
+    if (dir < 0) {\r
+       for (j = 0; j < m; j++)\r
+           move_termchar(ldata,\r
+                         ldata->chars + term->curs.x + j,\r
+                         ldata->chars + term->curs.x + j + n);\r
+       while (n--)\r
+           copy_termchar(ldata, term->curs.x + m++, &term->erase_char);\r
+    } else {\r
+       for (j = m; j-- ;)\r
+           move_termchar(ldata,\r
+                         ldata->chars + term->curs.x + j + n,\r
+                         ldata->chars + term->curs.x + j);\r
+       while (n--)\r
+           copy_termchar(ldata, term->curs.x + n, &term->erase_char);\r
+    }\r
+}\r
+\r
+/*\r
+ * Toggle terminal mode `mode' to state `state'. (`query' indicates\r
+ * whether the mode is a DEC private one or a normal one.)\r
+ */\r
+static void toggle_mode(Terminal *term, int mode, int query, int state)\r
+{\r
+    if (query)\r
+       switch (mode) {\r
+         case 1:                      /* DECCKM: application cursor keys */\r
+           term->app_cursor_keys = state;\r
+           break;\r
+         case 2:                      /* DECANM: VT52 mode */\r
+           term->vt52_mode = !state;\r
+           if (term->vt52_mode) {\r
+               term->blink_is_real = FALSE;\r
+               term->vt52_bold = FALSE;\r
+           } else {\r
+               term->blink_is_real = term->cfg.blinktext;\r
+           }\r
+           term_schedule_tblink(term);\r
+           break;\r
+         case 3:                      /* DECCOLM: 80/132 columns */\r
+           deselect(term);\r
+           if (!term->cfg.no_remote_resize)\r
+               request_resize(term->frontend, state ? 132 : 80, term->rows);\r
+           term->reset_132 = state;\r
+           term->alt_t = term->marg_t = 0;\r
+           term->alt_b = term->marg_b = term->rows - 1;\r
+           move(term, 0, 0, 0);\r
+           erase_lots(term, FALSE, TRUE, TRUE);\r
+           break;\r
+         case 5:                      /* DECSCNM: reverse video */\r
+           /*\r
+            * Toggle reverse video. If we receive an OFF within the\r
+            * visual bell timeout period after an ON, we trigger an\r
+            * effective visual bell, so that ESC[?5hESC[?5l will\r
+            * always be an actually _visible_ visual bell.\r
+            */\r
+           if (term->rvideo && !state) {\r
+               /* This is an OFF, so set up a vbell */\r
+               term_schedule_vbell(term, TRUE, term->rvbell_startpoint);\r
+           } else if (!term->rvideo && state) {\r
+               /* This is an ON, so we notice the time and save it. */\r
+               term->rvbell_startpoint = GETTICKCOUNT();\r
+           }\r
+           term->rvideo = state;\r
+           seen_disp_event(term);\r
+           break;\r
+         case 6:                      /* DECOM: DEC origin mode */\r
+           term->dec_om = state;\r
+           break;\r
+         case 7:                      /* DECAWM: auto wrap */\r
+           term->wrap = state;\r
+           break;\r
+         case 8:                      /* DECARM: auto key repeat */\r
+           term->repeat_off = !state;\r
+           break;\r
+         case 10:                     /* DECEDM: set local edit mode */\r
+           term->term_editing = state;\r
+           if (term->ldisc)           /* cause ldisc to notice changes */\r
+               ldisc_send(term->ldisc, NULL, 0, 0);\r
+           break;\r
+         case 25:                     /* DECTCEM: enable/disable cursor */\r
+           compatibility2(OTHER, VT220);\r
+           term->cursor_on = state;\r
+           seen_disp_event(term);\r
+           break;\r
+         case 47:                     /* alternate screen */\r
+           compatibility(OTHER);\r
+           deselect(term);\r
+           swap_screen(term, term->cfg.no_alt_screen ? 0 : state, FALSE, FALSE);\r
+           term->disptop = 0;\r
+           break;\r
+         case 1000:                   /* xterm mouse 1 (normal) */\r
+           term->xterm_mouse = state ? 1 : 0;\r
+           set_raw_mouse_mode(term->frontend, state);\r
+           break;\r
+         case 1002:                   /* xterm mouse 2 (inc. button drags) */\r
+           term->xterm_mouse = state ? 2 : 0;\r
+           set_raw_mouse_mode(term->frontend, state);\r
+           break;\r
+         case 1047:                   /* alternate screen */\r
+           compatibility(OTHER);\r
+           deselect(term);\r
+           swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, TRUE);\r
+           term->disptop = 0;\r
+           break;\r
+         case 1048:                   /* save/restore cursor */\r
+           if (!term->cfg.no_alt_screen)\r
+                save_cursor(term, state);\r
+           if (!state) seen_disp_event(term);\r
+           break;\r
+         case 1049:                   /* cursor & alternate screen */\r
+           if (state && !term->cfg.no_alt_screen)\r
+               save_cursor(term, state);\r
+           if (!state) seen_disp_event(term);\r
+           compatibility(OTHER);\r
+           deselect(term);\r
+           swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE);\r
+           if (!state && !term->cfg.no_alt_screen)\r
+               save_cursor(term, state);\r
+           term->disptop = 0;\r
+           break;\r
+    } else\r
+       switch (mode) {\r
+         case 4:                      /* IRM: set insert mode */\r
+           compatibility(VT102);\r
+           term->insert = state;\r
+           break;\r
+         case 12:                     /* SRM: set echo mode */\r
+           term->term_echoing = !state;\r
+           if (term->ldisc)           /* cause ldisc to notice changes */\r
+               ldisc_send(term->ldisc, NULL, 0, 0);\r
+           break;\r
+         case 20:                     /* LNM: Return sends ... */\r
+           term->cr_lf_return = state;\r
+           break;\r
+         case 34:                     /* WYULCURM: Make cursor BIG */\r
+           compatibility2(OTHER, VT220);\r
+           term->big_cursor = !state;\r
+       }\r
+}\r
+\r
+/*\r
+ * Process an OSC sequence: set window title or icon name.\r
+ */\r
+static void do_osc(Terminal *term)\r
+{\r
+    if (term->osc_w) {\r
+       while (term->osc_strlen--)\r
+           term->wordness[(unsigned char)\r
+               term->osc_string[term->osc_strlen]] = term->esc_args[0];\r
+    } else {\r
+       term->osc_string[term->osc_strlen] = '\0';\r
+       switch (term->esc_args[0]) {\r
+         case 0:\r
+         case 1:\r
+           if (!term->cfg.no_remote_wintitle)\r
+               set_icon(term->frontend, term->osc_string);\r
+           if (term->esc_args[0] == 1)\r
+               break;\r
+           /* fall through: parameter 0 means set both */\r
+         case 2:\r
+         case 21:\r
+           if (!term->cfg.no_remote_wintitle)\r
+               set_title(term->frontend, term->osc_string);\r
+           break;\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * ANSI printing routines.\r
+ */\r
+static void term_print_setup(Terminal *term)\r
+{\r
+    bufchain_clear(&term->printer_buf);\r
+    term->print_job = printer_start_job(term->cfg.printer);\r
+}\r
+static void term_print_flush(Terminal *term)\r
+{\r
+    void *data;\r
+    int len;\r
+    int size;\r
+    while ((size = bufchain_size(&term->printer_buf)) > 5) {\r
+       bufchain_prefix(&term->printer_buf, &data, &len);\r
+       if (len > size-5)\r
+           len = size-5;\r
+       printer_job_data(term->print_job, data, len);\r
+       bufchain_consume(&term->printer_buf, len);\r
+    }\r
+}\r
+static void term_print_finish(Terminal *term)\r
+{\r
+    void *data;\r
+    int len, size;\r
+    char c;\r
+\r
+    if (!term->printing && !term->only_printing)\r
+       return;                        /* we need do nothing */\r
+\r
+    term_print_flush(term);\r
+    while ((size = bufchain_size(&term->printer_buf)) > 0) {\r
+       bufchain_prefix(&term->printer_buf, &data, &len);\r
+       c = *(char *)data;\r
+       if (c == '\033' || c == '\233') {\r
+           bufchain_consume(&term->printer_buf, size);\r
+           break;\r
+       } else {\r
+           printer_job_data(term->print_job, &c, 1);\r
+           bufchain_consume(&term->printer_buf, 1);\r
+       }\r
+    }\r
+    printer_finish_job(term->print_job);\r
+    term->print_job = NULL;\r
+    term->printing = term->only_printing = FALSE;\r
+}\r
+\r
+/*\r
+ * Remove everything currently in `inbuf' and stick it up on the\r
+ * in-memory display. There's a big state machine in here to\r
+ * process escape sequences...\r
+ */\r
+static void term_out(Terminal *term)\r
+{\r
+    unsigned long c;\r
+    int unget;\r
+    unsigned char localbuf[256], *chars;\r
+    int nchars = 0;\r
+\r
+    unget = -1;\r
+\r
+    chars = NULL;                     /* placate compiler warnings */\r
+    while (nchars > 0 || unget != -1 || bufchain_size(&term->inbuf) > 0) {\r
+       if (unget == -1) {\r
+           if (nchars == 0) {\r
+               void *ret;\r
+               bufchain_prefix(&term->inbuf, &ret, &nchars);\r
+               if (nchars > sizeof(localbuf))\r
+                   nchars = sizeof(localbuf);\r
+               memcpy(localbuf, ret, nchars);\r
+               bufchain_consume(&term->inbuf, nchars);\r
+               chars = localbuf;\r
+               assert(chars != NULL);\r
+           }\r
+           c = *chars++;\r
+           nchars--;\r
+\r
+           /*\r
+            * Optionally log the session traffic to a file. Useful for\r
+            * debugging and possibly also useful for actual logging.\r
+            */\r
+           if (term->cfg.logtype == LGTYP_DEBUG && term->logctx)\r
+               logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG);\r
+       } else {\r
+           c = unget;\r
+           unget = -1;\r
+       }\r
+\r
+       /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even\r
+        * be able to display 8-bit characters, but I'll let that go 'cause\r
+        * of i18n.\r
+        */\r
+\r
+       /*\r
+        * If we're printing, add the character to the printer\r
+        * buffer.\r
+        */\r
+       if (term->printing) {\r
+           bufchain_add(&term->printer_buf, &c, 1);\r
+\r
+           /*\r
+            * If we're in print-only mode, we use a much simpler\r
+            * state machine designed only to recognise the ESC[4i\r
+            * termination sequence.\r
+            */\r
+           if (term->only_printing) {\r
+               if (c == '\033')\r
+                   term->print_state = 1;\r
+               else if (c == (unsigned char)'\233')\r
+                   term->print_state = 2;\r
+               else if (c == '[' && term->print_state == 1)\r
+                   term->print_state = 2;\r
+               else if (c == '4' && term->print_state == 2)\r
+                   term->print_state = 3;\r
+               else if (c == 'i' && term->print_state == 3)\r
+                   term->print_state = 4;\r
+               else\r
+                   term->print_state = 0;\r
+               if (term->print_state == 4) {\r
+                   term_print_finish(term);\r
+               }\r
+               continue;\r
+           }\r
+       }\r
+\r
+       /* First see about all those translations. */\r
+       if (term->termstate == TOPLEVEL) {\r
+           if (in_utf(term))\r
+               switch (term->utf_state) {\r
+                 case 0:\r
+                   if (c < 0x80) {\r
+                       /* UTF-8 must be stateless so we ignore iso2022. */\r
+                       if (term->ucsdata->unitab_ctrl[c] != 0xFF) \r
+                            c = term->ucsdata->unitab_ctrl[c];\r
+                       else c = ((unsigned char)c) | CSET_ASCII;\r
+                       break;\r
+                   } else if ((c & 0xe0) == 0xc0) {\r
+                       term->utf_size = term->utf_state = 1;\r
+                       term->utf_char = (c & 0x1f);\r
+                   } else if ((c & 0xf0) == 0xe0) {\r
+                       term->utf_size = term->utf_state = 2;\r
+                       term->utf_char = (c & 0x0f);\r
+                   } else if ((c & 0xf8) == 0xf0) {\r
+                       term->utf_size = term->utf_state = 3;\r
+                       term->utf_char = (c & 0x07);\r
+                   } else if ((c & 0xfc) == 0xf8) {\r
+                       term->utf_size = term->utf_state = 4;\r
+                       term->utf_char = (c & 0x03);\r
+                   } else if ((c & 0xfe) == 0xfc) {\r
+                       term->utf_size = term->utf_state = 5;\r
+                       term->utf_char = (c & 0x01);\r
+                   } else {\r
+                       c = UCSERR;\r
+                       break;\r
+                   }\r
+                   continue;\r
+                 case 1:\r
+                 case 2:\r
+                 case 3:\r
+                 case 4:\r
+                 case 5:\r
+                   if ((c & 0xC0) != 0x80) {\r
+                       unget = c;\r
+                       c = UCSERR;\r
+                       term->utf_state = 0;\r
+                       break;\r
+                   }\r
+                   term->utf_char = (term->utf_char << 6) | (c & 0x3f);\r
+                   if (--term->utf_state)\r
+                       continue;\r
+\r
+                   c = term->utf_char;\r
+\r
+                   /* Is somebody trying to be evil! */\r
+                   if (c < 0x80 ||\r
+                       (c < 0x800 && term->utf_size >= 2) ||\r
+                       (c < 0x10000 && term->utf_size >= 3) ||\r
+                       (c < 0x200000 && term->utf_size >= 4) ||\r
+                       (c < 0x4000000 && term->utf_size >= 5))\r
+                       c = UCSERR;\r
+\r
+                   /* Unicode line separator and paragraph separator are CR-LF */\r
+                   if (c == 0x2028 || c == 0x2029)\r
+                       c = 0x85;\r
+\r
+                   /* High controls are probably a Baaad idea too. */\r
+                   if (c < 0xA0)\r
+                       c = 0xFFFD;\r
+\r
+                   /* The UTF-16 surrogates are not nice either. */\r
+                   /*       The standard give the option of decoding these: \r
+                    *       I don't want to! */\r
+                   if (c >= 0xD800 && c < 0xE000)\r
+                       c = UCSERR;\r
+\r
+                   /* ISO 10646 characters now limited to UTF-16 range. */\r
+                   if (c > 0x10FFFF)\r
+                       c = UCSERR;\r
+\r
+                   /* This is currently a TagPhobic application.. */\r
+                   if (c >= 0xE0000 && c <= 0xE007F)\r
+                       continue;\r
+\r
+                   /* U+FEFF is best seen as a null. */\r
+                   if (c == 0xFEFF)\r
+                       continue;\r
+                   /* But U+FFFE is an error. */\r
+                   if (c == 0xFFFE || c == 0xFFFF)\r
+                       c = UCSERR;\r
+\r
+                   break;\r
+           }\r
+           /* Are we in the nasty ACS mode? Note: no sco in utf mode. */\r
+           else if(term->sco_acs && \r
+                   (c!='\033' && c!='\012' && c!='\015' && c!='\b'))\r
+           {\r
+              if (term->sco_acs == 2) c |= 0x80;\r
+              c |= CSET_SCOACS;\r
+           } else {\r
+               switch (term->cset_attr[term->cset]) {\r
+                   /* \r
+                    * Linedraw characters are different from 'ESC ( B'\r
+                    * only for a small range. For ones outside that\r
+                    * range, make sure we use the same font as well as\r
+                    * the same encoding.\r
+                    */\r
+                 case CSET_LINEDRW:\r
+                   if (term->ucsdata->unitab_ctrl[c] != 0xFF)\r
+                       c = term->ucsdata->unitab_ctrl[c];\r
+                   else\r
+                       c = ((unsigned char) c) | CSET_LINEDRW;\r
+                   break;\r
+\r
+                 case CSET_GBCHR:\r
+                   /* If UK-ASCII, make the '#' a LineDraw Pound */\r
+                   if (c == '#') {\r
+                       c = '}' | CSET_LINEDRW;\r
+                       break;\r
+                   }\r
+                 /*FALLTHROUGH*/ case CSET_ASCII:\r
+                   if (term->ucsdata->unitab_ctrl[c] != 0xFF)\r
+                       c = term->ucsdata->unitab_ctrl[c];\r
+                   else\r
+                       c = ((unsigned char) c) | CSET_ASCII;\r
+                   break;\r
+               case CSET_SCOACS:\r
+                   if (c>=' ') c = ((unsigned char)c) | CSET_SCOACS;\r
+                   break;\r
+               }\r
+           }\r
+       }\r
+\r
+       /*\r
+        * How about C1 controls? \r
+        * Explicitly ignore SCI (0x9a), which we don't translate to DECID.\r
+        */\r
+       if ((c & -32) == 0x80 && term->termstate < DO_CTRLS &&\r
+           !term->vt52_mode && has_compat(VT220)) {\r
+           if (c == 0x9a)\r
+               c = 0;\r
+           else {\r
+               term->termstate = SEEN_ESC;\r
+               term->esc_query = FALSE;\r
+               c = '@' + (c & 0x1F);\r
+           }\r
+       }\r
+\r
+       /* Or the GL control. */\r
+       if (c == '\177' && term->termstate < DO_CTRLS && has_compat(OTHER)) {\r
+           if (term->curs.x && !term->wrapnext)\r
+               term->curs.x--;\r
+           term->wrapnext = FALSE;\r
+           /* destructive backspace might be disabled */\r
+           if (!term->cfg.no_dbackspace) {\r
+               check_boundary(term, term->curs.x, term->curs.y);\r
+               check_boundary(term, term->curs.x+1, term->curs.y);\r
+               copy_termchar(scrlineptr(term->curs.y),\r
+                             term->curs.x, &term->erase_char);\r
+           }\r
+       } else\r
+           /* Or normal C0 controls. */\r
+       if ((c & ~0x1F) == 0 && term->termstate < DO_CTRLS) {\r
+           switch (c) {\r
+             case '\005':             /* ENQ: terminal type query */\r
+               /* \r
+                * Strictly speaking this is VT100 but a VT100 defaults to\r
+                * no response. Other terminals respond at their option.\r
+                *\r
+                * Don't put a CR in the default string as this tends to\r
+                * upset some weird software.\r
+                */\r
+               compatibility(ANSIMIN);\r
+               if (term->ldisc) {\r
+                   char abuf[lenof(term->cfg.answerback)], *s, *d;\r
+                   for (s = term->cfg.answerback, d = abuf; *s;) {\r
+                       char *n;\r
+                       char c = ctrlparse(s, &n);\r
+                       if (n) {\r
+                           *d++ = c;\r
+                           s = n;\r
+                       } else {\r
+                           *d++ = *s++;\r
+                       }\r
+                   }\r
+                   lpage_send(term->ldisc, DEFAULT_CODEPAGE,\r
+                              abuf, d - abuf, 0);\r
+               }\r
+               break;\r
+             case '\007':            /* BEL: Bell */\r
+               {\r
+                   struct beeptime *newbeep;\r
+                   unsigned long ticks;\r
+\r
+                   ticks = GETTICKCOUNT();\r
+\r
+                   if (!term->beep_overloaded) {\r
+                       newbeep = snew(struct beeptime);\r
+                       newbeep->ticks = ticks;\r
+                       newbeep->next = NULL;\r
+                       if (!term->beephead)\r
+                           term->beephead = newbeep;\r
+                       else\r
+                           term->beeptail->next = newbeep;\r
+                       term->beeptail = newbeep;\r
+                       term->nbeeps++;\r
+                   }\r
+\r
+                   /*\r
+                    * Throw out any beeps that happened more than\r
+                    * t seconds ago.\r
+                    */\r
+                   while (term->beephead &&\r
+                          term->beephead->ticks < ticks - term->cfg.bellovl_t) {\r
+                       struct beeptime *tmp = term->beephead;\r
+                       term->beephead = tmp->next;\r
+                       sfree(tmp);\r
+                       if (!term->beephead)\r
+                           term->beeptail = NULL;\r
+                       term->nbeeps--;\r
+                   }\r
+\r
+                   if (term->cfg.bellovl && term->beep_overloaded &&\r
+                       ticks - term->lastbeep >= (unsigned)term->cfg.bellovl_s) {\r
+                       /*\r
+                        * If we're currently overloaded and the\r
+                        * last beep was more than s seconds ago,\r
+                        * leave overload mode.\r
+                        */\r
+                       term->beep_overloaded = FALSE;\r
+                   } else if (term->cfg.bellovl && !term->beep_overloaded &&\r
+                              term->nbeeps >= term->cfg.bellovl_n) {\r
+                       /*\r
+                        * Now, if we have n or more beeps\r
+                        * remaining in the queue, go into overload\r
+                        * mode.\r
+                        */\r
+                       term->beep_overloaded = TRUE;\r
+                   }\r
+                   term->lastbeep = ticks;\r
+\r
+                   /*\r
+                    * Perform an actual beep if we're not overloaded.\r
+                    */\r
+                   if (!term->cfg.bellovl || !term->beep_overloaded) {\r
+                       do_beep(term->frontend, term->cfg.beep);\r
+\r
+                       if (term->cfg.beep == BELL_VISUAL) {\r
+                           term_schedule_vbell(term, FALSE, 0);\r
+                       }\r
+                   }\r
+                   seen_disp_event(term);\r
+               }\r
+               break;\r
+             case '\b':              /* BS: Back space */\r
+               if (term->curs.x == 0 &&\r
+                   (term->curs.y == 0 || term->wrap == 0))\r
+                   /* do nothing */ ;\r
+               else if (term->curs.x == 0 && term->curs.y > 0)\r
+                   term->curs.x = term->cols - 1, term->curs.y--;\r
+               else if (term->wrapnext)\r
+                   term->wrapnext = FALSE;\r
+               else\r
+                   term->curs.x--;\r
+               seen_disp_event(term);\r
+               break;\r
+             case '\016':            /* LS1: Locking-shift one */\r
+               compatibility(VT100);\r
+               term->cset = 1;\r
+               break;\r
+             case '\017':            /* LS0: Locking-shift zero */\r
+               compatibility(VT100);\r
+               term->cset = 0;\r
+               break;\r
+             case '\033':            /* ESC: Escape */\r
+               if (term->vt52_mode)\r
+                   term->termstate = VT52_ESC;\r
+               else {\r
+                   compatibility(ANSIMIN);\r
+                   term->termstate = SEEN_ESC;\r
+                   term->esc_query = FALSE;\r
+               }\r
+               break;\r
+             case '\015':            /* CR: Carriage return */\r
+               term->curs.x = 0;\r
+               term->wrapnext = FALSE;\r
+               seen_disp_event(term);\r
+               term->paste_hold = 0;\r
+\r
+        if (term->cfg.crhaslf) {  \r
+                 if (term->curs.y == term->marg_b)\r
+                   scroll(term, term->marg_t, term->marg_b, 1, TRUE);\r
+                 else if (term->curs.y < term->rows - 1)\r
+                   term->curs.y++;\r
+        }\r
+               if (term->logctx)\r
+                   logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);\r
+               break;\r
+             case '\014':            /* FF: Form feed */\r
+               if (has_compat(SCOANSI)) {\r
+                   move(term, 0, 0, 0);\r
+                   erase_lots(term, FALSE, FALSE, TRUE);\r
+                   term->disptop = 0;\r
+                   term->wrapnext = FALSE;\r
+                   seen_disp_event(term);\r
+                   break;\r
+               }\r
+             case '\013':            /* VT: Line tabulation */\r
+               compatibility(VT100);\r
+             case '\012':            /* LF: Line feed */\r
+               if (term->curs.y == term->marg_b)\r
+                   scroll(term, term->marg_t, term->marg_b, 1, TRUE);\r
+               else if (term->curs.y < term->rows - 1)\r
+                   term->curs.y++;\r
+               if (term->cfg.lfhascr)\r
+                   term->curs.x = 0;\r
+               term->wrapnext = FALSE;\r
+               seen_disp_event(term);\r
+               term->paste_hold = 0;\r
+               if (term->logctx)\r
+                   logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);\r
+               break;\r
+             case '\t':              /* HT: Character tabulation */\r
+               {\r
+                   pos old_curs = term->curs;\r
+                   termline *ldata = scrlineptr(term->curs.y);\r
+\r
+                   do {\r
+                       term->curs.x++;\r
+                   } while (term->curs.x < term->cols - 1 &&\r
+                            !term->tabs[term->curs.x]);\r
+\r
+                   if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) {\r
+                       if (term->curs.x >= term->cols / 2)\r
+                           term->curs.x = term->cols / 2 - 1;\r
+                   } else {\r
+                       if (term->curs.x >= term->cols)\r
+                           term->curs.x = term->cols - 1;\r
+                   }\r
+\r
+                   check_selection(term, old_curs, term->curs);\r
+               }\r
+               seen_disp_event(term);\r
+               break;\r
+           }\r
+       } else\r
+           switch (term->termstate) {\r
+             case TOPLEVEL:\r
+               /* Only graphic characters get this far;\r
+                * ctrls are stripped above */\r
+               {\r
+                   termline *cline = scrlineptr(term->curs.y);\r
+                   int width = 0;\r
+                   if (DIRECT_CHAR(c))\r
+                       width = 1;\r
+                   if (!width)\r
+                       width = (term->cfg.cjk_ambig_wide ?\r
+                                mk_wcwidth_cjk((wchar_t) c) :\r
+                                mk_wcwidth((wchar_t) c));\r
+\r
+                   if (term->wrapnext && term->wrap && width > 0) {\r
+                       cline->lattr |= LATTR_WRAPPED;\r
+                       if (term->curs.y == term->marg_b)\r
+                           scroll(term, term->marg_t, term->marg_b, 1, TRUE);\r
+                       else if (term->curs.y < term->rows - 1)\r
+                           term->curs.y++;\r
+                       term->curs.x = 0;\r
+                       term->wrapnext = FALSE;\r
+                       cline = scrlineptr(term->curs.y);\r
+                   }\r
+                   if (term->insert && width > 0)\r
+                       insch(term, width);\r
+                   if (term->selstate != NO_SELECTION) {\r
+                       pos cursplus = term->curs;\r
+                       incpos(cursplus);\r
+                       check_selection(term, term->curs, cursplus);\r
+                   }\r
+                   if (((c & CSET_MASK) == CSET_ASCII ||\r
+                        (c & CSET_MASK) == 0) &&\r
+                       term->logctx)\r
+                       logtraffic(term->logctx, (unsigned char) c,\r
+                                  LGTYP_ASCII);\r
+\r
+                   switch (width) {\r
+                     case 2:\r
+                       /*\r
+                        * If we're about to display a double-width\r
+                        * character starting in the rightmost\r
+                        * column, then we do something special\r
+                        * instead. We must print a space in the\r
+                        * last column of the screen, then wrap;\r
+                        * and we also set LATTR_WRAPPED2 which\r
+                        * instructs subsequent cut-and-pasting not\r
+                        * only to splice this line to the one\r
+                        * after it, but to ignore the space in the\r
+                        * last character position as well.\r
+                        * (Because what was actually output to the\r
+                        * terminal was presumably just a sequence\r
+                        * of CJK characters, and we don't want a\r
+                        * space to be pasted in the middle of\r
+                        * those just because they had the\r
+                        * misfortune to start in the wrong parity\r
+                        * column. xterm concurs.)\r
+                        */\r
+                       check_boundary(term, term->curs.x, term->curs.y);\r
+                       check_boundary(term, term->curs.x+2, term->curs.y);\r
+                       if (term->curs.x == term->cols-1) {\r
+                           copy_termchar(cline, term->curs.x,\r
+                                         &term->erase_char);\r
+                           cline->lattr |= LATTR_WRAPPED | LATTR_WRAPPED2;\r
+                           if (term->curs.y == term->marg_b)\r
+                               scroll(term, term->marg_t, term->marg_b,\r
+                                      1, TRUE);\r
+                           else if (term->curs.y < term->rows - 1)\r
+                               term->curs.y++;\r
+                           term->curs.x = 0;\r
+                           cline = scrlineptr(term->curs.y);\r
+                           /* Now we must check_boundary again, of course. */\r
+                           check_boundary(term, term->curs.x, term->curs.y);\r
+                           check_boundary(term, term->curs.x+2, term->curs.y);\r
+                       }\r
+\r
+                       /* FULL-TERMCHAR */\r
+                       clear_cc(cline, term->curs.x);\r
+                       cline->chars[term->curs.x].chr = c;\r
+                       cline->chars[term->curs.x].attr = term->curr_attr;\r
+\r
+                       term->curs.x++;\r
+\r
+                       /* FULL-TERMCHAR */\r
+                       clear_cc(cline, term->curs.x);\r
+                       cline->chars[term->curs.x].chr = UCSWIDE;\r
+                       cline->chars[term->curs.x].attr = term->curr_attr;\r
+\r
+                       break;\r
+                     case 1:\r
+                       check_boundary(term, term->curs.x, term->curs.y);\r
+                       check_boundary(term, term->curs.x+1, term->curs.y);\r
+\r
+                       /* FULL-TERMCHAR */\r
+                       clear_cc(cline, term->curs.x);\r
+                       cline->chars[term->curs.x].chr = c;\r
+                       cline->chars[term->curs.x].attr = term->curr_attr;\r
+\r
+                       break;\r
+                     case 0:\r
+                       if (term->curs.x > 0) {\r
+                           int x = term->curs.x - 1;\r
+\r
+                           /* If we're in wrapnext state, the character\r
+                            * to combine with is _here_, not to our left. */\r
+                           if (term->wrapnext)\r
+                               x++;\r
+\r
+                           /*\r
+                            * If the previous character is\r
+                            * UCSWIDE, back up another one.\r
+                            */\r
+                           if (cline->chars[x].chr == UCSWIDE) {\r
+                               assert(x > 0);\r
+                               x--;\r
+                           }\r
+\r
+                           add_cc(cline, x, c);\r
+                           seen_disp_event(term);\r
+                       }\r
+                       continue;\r
+                     default:\r
+                       continue;\r
+                   }\r
+                   term->curs.x++;\r
+                   if (term->curs.x == term->cols) {\r
+                       term->curs.x--;\r
+                       term->wrapnext = TRUE;\r
+                       if (term->wrap && term->vt52_mode) {\r
+                           cline->lattr |= LATTR_WRAPPED;\r
+                           if (term->curs.y == term->marg_b)\r
+                               scroll(term, term->marg_t, term->marg_b, 1, TRUE);\r
+                           else if (term->curs.y < term->rows - 1)\r
+                               term->curs.y++;\r
+                           term->curs.x = 0;\r
+                           term->wrapnext = FALSE;\r
+                       }\r
+                   }\r
+                   seen_disp_event(term);\r
+               }\r
+               break;\r
+\r
+             case OSC_MAYBE_ST:\r
+               /*\r
+                * This state is virtually identical to SEEN_ESC, with the\r
+                * exception that we have an OSC sequence in the pipeline,\r
+                * and _if_ we see a backslash, we process it.\r
+                */\r
+               if (c == '\\') {\r
+                   do_osc(term);\r
+                   term->termstate = TOPLEVEL;\r
+                   break;\r
+               }\r
+               /* else fall through */\r
+             case SEEN_ESC:\r
+               if (c >= ' ' && c <= '/') {\r
+                   if (term->esc_query)\r
+                       term->esc_query = -1;\r
+                   else\r
+                       term->esc_query = c;\r
+                   break;\r
+               }\r
+               term->termstate = TOPLEVEL;\r
+               switch (ANSI(c, term->esc_query)) {\r
+                 case '[':             /* enter CSI mode */\r
+                   term->termstate = SEEN_CSI;\r
+                   term->esc_nargs = 1;\r
+                   term->esc_args[0] = ARG_DEFAULT;\r
+                   term->esc_query = FALSE;\r
+                   break;\r
+                 case ']':             /* OSC: xterm escape sequences */\r
+                   /* Compatibility is nasty here, xterm, linux, decterm yuk! */\r
+                   compatibility(OTHER);\r
+                   term->termstate = SEEN_OSC;\r
+                   term->esc_args[0] = 0;\r
+                   break;\r
+                 case '7':             /* DECSC: save cursor */\r
+                   compatibility(VT100);\r
+                   save_cursor(term, TRUE);\r
+                   break;\r
+                 case '8':             /* DECRC: restore cursor */\r
+                   compatibility(VT100);\r
+                   save_cursor(term, FALSE);\r
+                   seen_disp_event(term);\r
+                   break;\r
+                 case '=':             /* DECKPAM: Keypad application mode */\r
+                   compatibility(VT100);\r
+                   term->app_keypad_keys = TRUE;\r
+                   break;\r
+                 case '>':             /* DECKPNM: Keypad numeric mode */\r
+                   compatibility(VT100);\r
+                   term->app_keypad_keys = FALSE;\r
+                   break;\r
+                 case 'D':            /* IND: exactly equivalent to LF */\r
+                   compatibility(VT100);\r
+                   if (term->curs.y == term->marg_b)\r
+                       scroll(term, term->marg_t, term->marg_b, 1, TRUE);\r
+                   else if (term->curs.y < term->rows - 1)\r
+                       term->curs.y++;\r
+                   term->wrapnext = FALSE;\r
+                   seen_disp_event(term);\r
+                   break;\r
+                 case 'E':            /* NEL: exactly equivalent to CR-LF */\r
+                   compatibility(VT100);\r
+                   term->curs.x = 0;\r
+                   if (term->curs.y == term->marg_b)\r
+                       scroll(term, term->marg_t, term->marg_b, 1, TRUE);\r
+                   else if (term->curs.y < term->rows - 1)\r
+                       term->curs.y++;\r
+                   term->wrapnext = FALSE;\r
+                   seen_disp_event(term);\r
+                   break;\r
+                 case 'M':            /* RI: reverse index - backwards LF */\r
+                   compatibility(VT100);\r
+                   if (term->curs.y == term->marg_t)\r
+                       scroll(term, term->marg_t, term->marg_b, -1, TRUE);\r
+                   else if (term->curs.y > 0)\r
+                       term->curs.y--;\r
+                   term->wrapnext = FALSE;\r
+                   seen_disp_event(term);\r
+                   break;\r
+                 case 'Z':            /* DECID: terminal type query */\r
+                   compatibility(VT100);\r
+                   if (term->ldisc)\r
+                       ldisc_send(term->ldisc, term->id_string,\r
+                                  strlen(term->id_string), 0);\r
+                   break;\r
+                 case 'c':            /* RIS: restore power-on settings */\r
+                   compatibility(VT100);\r
+                   power_on(term, TRUE);\r
+                   if (term->ldisc)   /* cause ldisc to notice changes */\r
+                       ldisc_send(term->ldisc, NULL, 0, 0);\r
+                   if (term->reset_132) {\r
+                       if (!term->cfg.no_remote_resize)\r
+                           request_resize(term->frontend, 80, term->rows);\r
+                       term->reset_132 = 0;\r
+                   }\r
+                   term->disptop = 0;\r
+                   seen_disp_event(term);\r
+                   break;\r
+                 case 'H':            /* HTS: set a tab */\r
+                   compatibility(VT100);\r
+                   term->tabs[term->curs.x] = TRUE;\r
+                   break;\r
+\r
+                 case ANSI('8', '#'):  /* DECALN: fills screen with Es :-) */\r
+                   compatibility(VT100);\r
+                   {\r
+                       termline *ldata;\r
+                       int i, j;\r
+                       pos scrtop, scrbot;\r
+\r
+                       for (i = 0; i < term->rows; i++) {\r
+                           ldata = scrlineptr(i);\r
+                           for (j = 0; j < term->cols; j++) {\r
+                               copy_termchar(ldata, j,\r
+                                             &term->basic_erase_char);\r
+                               ldata->chars[j].chr = 'E';\r
+                           }\r
+                           ldata->lattr = LATTR_NORM;\r
+                       }\r
+                       term->disptop = 0;\r
+                       seen_disp_event(term);\r
+                       scrtop.x = scrtop.y = 0;\r
+                       scrbot.x = 0;\r
+                       scrbot.y = term->rows;\r
+                       check_selection(term, scrtop, scrbot);\r
+                   }\r
+                   break;\r
+\r
+                 case ANSI('3', '#'):\r
+                 case ANSI('4', '#'):\r
+                 case ANSI('5', '#'):\r
+                 case ANSI('6', '#'):\r
+                   compatibility(VT100);\r
+                   {\r
+                       int nlattr;\r
+\r
+                       switch (ANSI(c, term->esc_query)) {\r
+                         case ANSI('3', '#'): /* DECDHL: 2*height, top */\r
+                           nlattr = LATTR_TOP;\r
+                           break;\r
+                         case ANSI('4', '#'): /* DECDHL: 2*height, bottom */\r
+                           nlattr = LATTR_BOT;\r
+                           break;\r
+                         case ANSI('5', '#'): /* DECSWL: normal */\r
+                           nlattr = LATTR_NORM;\r
+                           break;\r
+                         default: /* case ANSI('6', '#'): DECDWL: 2*width */\r
+                           nlattr = LATTR_WIDE;\r
+                           break;\r
+                       }\r
+                       scrlineptr(term->curs.y)->lattr = nlattr;\r
+                   }\r
+                   break;\r
+                 /* GZD4: G0 designate 94-set */\r
+                 case ANSI('A', '('):\r
+                   compatibility(VT100);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[0] = CSET_GBCHR;\r
+                   break;\r
+                 case ANSI('B', '('):\r
+                   compatibility(VT100);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[0] = CSET_ASCII;\r
+                   break;\r
+                 case ANSI('0', '('):\r
+                   compatibility(VT100);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[0] = CSET_LINEDRW;\r
+                   break;\r
+                 case ANSI('U', '('): \r
+                   compatibility(OTHER);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[0] = CSET_SCOACS; \r
+                   break;\r
+                 /* G1D4: G1-designate 94-set */\r
+                 case ANSI('A', ')'):\r
+                   compatibility(VT100);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[1] = CSET_GBCHR;\r
+                   break;\r
+                 case ANSI('B', ')'):\r
+                   compatibility(VT100);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[1] = CSET_ASCII;\r
+                   break;\r
+                 case ANSI('0', ')'):\r
+                   compatibility(VT100);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[1] = CSET_LINEDRW;\r
+                   break;\r
+                 case ANSI('U', ')'): \r
+                   compatibility(OTHER);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->cset_attr[1] = CSET_SCOACS; \r
+                   break;\r
+                 /* DOCS: Designate other coding system */\r
+                 case ANSI('8', '%'):  /* Old Linux code */\r
+                 case ANSI('G', '%'):\r
+                   compatibility(OTHER);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->utf = 1;\r
+                   break;\r
+                 case ANSI('@', '%'):\r
+                   compatibility(OTHER);\r
+                   if (!term->cfg.no_remote_charset)\r
+                       term->utf = 0;\r
+                   break;\r
+               }\r
+               break;\r
+             case SEEN_CSI:\r
+               term->termstate = TOPLEVEL;  /* default */\r
+               if (isdigit(c)) {\r
+                   if (term->esc_nargs <= ARGS_MAX) {\r
+                       if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT)\r
+                           term->esc_args[term->esc_nargs - 1] = 0;\r
+                       term->esc_args[term->esc_nargs - 1] =\r
+                           10 * term->esc_args[term->esc_nargs - 1] + c - '0';\r
+                   }\r
+                   term->termstate = SEEN_CSI;\r
+               } else if (c == ';') {\r
+                   if (term->esc_nargs < ARGS_MAX)\r
+                       term->esc_args[term->esc_nargs++] = ARG_DEFAULT;\r
+                   term->termstate = SEEN_CSI;\r
+               } else if (c < '@') {\r
+                   if (term->esc_query)\r
+                       term->esc_query = -1;\r
+                   else if (c == '?')\r
+                       term->esc_query = TRUE;\r
+                   else\r
+                       term->esc_query = c;\r
+                   term->termstate = SEEN_CSI;\r
+               } else\r
+                   switch (ANSI(c, term->esc_query)) {\r
+                     case 'A':       /* CUU: move up N lines */\r
+                       move(term, term->curs.x,\r
+                            term->curs.y - def(term->esc_args[0], 1), 1);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'e':         /* VPR: move down N lines */\r
+                       compatibility(ANSI);\r
+                       /* FALLTHROUGH */\r
+                     case 'B':         /* CUD: Cursor down */\r
+                       move(term, term->curs.x,\r
+                            term->curs.y + def(term->esc_args[0], 1), 1);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case ANSI('c', '>'):      /* DA: report xterm version */\r
+                       compatibility(OTHER);\r
+                       /* this reports xterm version 136 so that VIM can\r
+                          use the drag messages from the mouse reporting */\r
+                       if (term->ldisc)\r
+                           ldisc_send(term->ldisc, "\033[>0;136;0c", 11, 0);\r
+                       break;\r
+                     case 'a':         /* HPR: move right N cols */\r
+                       compatibility(ANSI);\r
+                       /* FALLTHROUGH */\r
+                     case 'C':         /* CUF: Cursor right */ \r
+                       move(term, term->curs.x + def(term->esc_args[0], 1),\r
+                            term->curs.y, 1);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'D':       /* CUB: move left N cols */\r
+                       move(term, term->curs.x - def(term->esc_args[0], 1),\r
+                            term->curs.y, 1);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'E':       /* CNL: move down N lines and CR */\r
+                       compatibility(ANSI);\r
+                       move(term, 0,\r
+                            term->curs.y + def(term->esc_args[0], 1), 1);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'F':       /* CPL: move up N lines and CR */\r
+                       compatibility(ANSI);\r
+                       move(term, 0,\r
+                            term->curs.y - def(term->esc_args[0], 1), 1);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'G':       /* CHA */\r
+                     case '`':       /* HPA: set horizontal posn */\r
+                       compatibility(ANSI);\r
+                       move(term, def(term->esc_args[0], 1) - 1,\r
+                            term->curs.y, 0);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'd':       /* VPA: set vertical posn */\r
+                       compatibility(ANSI);\r
+                       move(term, term->curs.x,\r
+                            ((term->dec_om ? term->marg_t : 0) +\r
+                             def(term->esc_args[0], 1) - 1),\r
+                            (term->dec_om ? 2 : 0));\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'H':      /* CUP */\r
+                     case 'f':      /* HVP: set horz and vert posns at once */\r
+                       if (term->esc_nargs < 2)\r
+                           term->esc_args[1] = ARG_DEFAULT;\r
+                       move(term, def(term->esc_args[1], 1) - 1,\r
+                            ((term->dec_om ? term->marg_t : 0) +\r
+                             def(term->esc_args[0], 1) - 1),\r
+                            (term->dec_om ? 2 : 0));\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'J':       /* ED: erase screen or parts of it */\r
+                       {\r
+                           unsigned int i = def(term->esc_args[0], 0);\r
+                           if (i == 3) {\r
+                               /* Erase Saved Lines (xterm)\r
+                                * This follows Thomas Dickey's xterm. */\r
+                               term_clrsb(term);\r
+                           } else {\r
+                               i++;\r
+                               if (i > 3)\r
+                                   i = 0;\r
+                               erase_lots(term, FALSE, !!(i & 2), !!(i & 1));\r
+                           }\r
+                       }\r
+                       term->disptop = 0;\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'K':       /* EL: erase line or parts of it */\r
+                       {\r
+                           unsigned int i = def(term->esc_args[0], 0) + 1;\r
+                           if (i > 3)\r
+                               i = 0;\r
+                           erase_lots(term, TRUE, !!(i & 2), !!(i & 1));\r
+                       }\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'L':       /* IL: insert lines */\r
+                       compatibility(VT102);\r
+                       if (term->curs.y <= term->marg_b)\r
+                           scroll(term, term->curs.y, term->marg_b,\r
+                                  -def(term->esc_args[0], 1), FALSE);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'M':       /* DL: delete lines */\r
+                       compatibility(VT102);\r
+                       if (term->curs.y <= term->marg_b)\r
+                           scroll(term, term->curs.y, term->marg_b,\r
+                                  def(term->esc_args[0], 1),\r
+                                  TRUE);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case '@':       /* ICH: insert chars */\r
+                       /* XXX VTTEST says this is vt220, vt510 manual says vt102 */\r
+                       compatibility(VT102);\r
+                       insch(term, def(term->esc_args[0], 1));\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'P':       /* DCH: delete chars */\r
+                       compatibility(VT102);\r
+                       insch(term, -def(term->esc_args[0], 1));\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'c':       /* DA: terminal type query */\r
+                       compatibility(VT100);\r
+                       /* This is the response for a VT102 */\r
+                       if (term->ldisc)\r
+                           ldisc_send(term->ldisc, term->id_string,\r
+                                      strlen(term->id_string), 0);\r
+                       break;\r
+                     case 'n':       /* DSR: cursor position query */\r
+                       if (term->ldisc) {\r
+                           if (term->esc_args[0] == 6) {\r
+                               char buf[32];\r
+                               sprintf(buf, "\033[%d;%dR", term->curs.y + 1,\r
+                                       term->curs.x + 1);\r
+                               ldisc_send(term->ldisc, buf, strlen(buf), 0);\r
+                           } else if (term->esc_args[0] == 5) {\r
+                               ldisc_send(term->ldisc, "\033[0n", 4, 0);\r
+                           }\r
+                       }\r
+                       break;\r
+                     case 'h':       /* SM: toggle modes to high */\r
+                     case ANSI_QUE('h'):\r
+                       compatibility(VT100);\r
+                       {\r
+                           int i;\r
+                           for (i = 0; i < term->esc_nargs; i++)\r
+                               toggle_mode(term, term->esc_args[i],\r
+                                           term->esc_query, TRUE);\r
+                       }\r
+                       break;\r
+                     case 'i':         /* MC: Media copy */\r
+                     case ANSI_QUE('i'):\r
+                       compatibility(VT100);\r
+                       {\r
+                           if (term->esc_nargs != 1) break;\r
+                           if (term->esc_args[0] == 5 && *term->cfg.printer) {\r
+                               term->printing = TRUE;\r
+                               term->only_printing = !term->esc_query;\r
+                               term->print_state = 0;\r
+                               term_print_setup(term);\r
+                           } else if (term->esc_args[0] == 4 &&\r
+                                      term->printing) {\r
+                               term_print_finish(term);\r
+                           }\r
+                       }\r
+                       break;                  \r
+                     case 'l':       /* RM: toggle modes to low */\r
+                     case ANSI_QUE('l'):\r
+                       compatibility(VT100);\r
+                       {\r
+                           int i;\r
+                           for (i = 0; i < term->esc_nargs; i++)\r
+                               toggle_mode(term, term->esc_args[i],\r
+                                           term->esc_query, FALSE);\r
+                       }\r
+                       break;\r
+                     case 'g':       /* TBC: clear tabs */\r
+                       compatibility(VT100);\r
+                       if (term->esc_nargs == 1) {\r
+                           if (term->esc_args[0] == 0) {\r
+                               term->tabs[term->curs.x] = FALSE;\r
+                           } else if (term->esc_args[0] == 3) {\r
+                               int i;\r
+                               for (i = 0; i < term->cols; i++)\r
+                                   term->tabs[i] = FALSE;\r
+                           }\r
+                       }\r
+                       break;\r
+                     case 'r':       /* DECSTBM: set scroll margins */\r
+                       compatibility(VT100);\r
+                       if (term->esc_nargs <= 2) {\r
+                           int top, bot;\r
+                           top = def(term->esc_args[0], 1) - 1;\r
+                           bot = (term->esc_nargs <= 1\r
+                                  || term->esc_args[1] == 0 ?\r
+                                  term->rows :\r
+                                  def(term->esc_args[1], term->rows)) - 1;\r
+                           if (bot >= term->rows)\r
+                               bot = term->rows - 1;\r
+                           /* VTTEST Bug 9 - if region is less than 2 lines\r
+                            * don't change region.\r
+                            */\r
+                           if (bot - top > 0) {\r
+                               term->marg_t = top;\r
+                               term->marg_b = bot;\r
+                               term->curs.x = 0;\r
+                               /*\r
+                                * I used to think the cursor should be\r
+                                * placed at the top of the newly marginned\r
+                                * area. Apparently not: VMS TPU falls over\r
+                                * if so.\r
+                                *\r
+                                * Well actually it should for\r
+                                * Origin mode - RDB\r
+                                */\r
+                               term->curs.y = (term->dec_om ?\r
+                                               term->marg_t : 0);\r
+                               seen_disp_event(term);\r
+                           }\r
+                       }\r
+                       break;\r
+                     case 'm':       /* SGR: set graphics rendition */\r
+                       {\r
+                           /* \r
+                            * A VT100 without the AVO only had one\r
+                            * attribute, either underline or\r
+                            * reverse video depending on the\r
+                            * cursor type, this was selected by\r
+                            * CSI 7m.\r
+                            *\r
+                            * case 2:\r
+                            *  This is sometimes DIM, eg on the\r
+                            *  GIGI and Linux\r
+                            * case 8:\r
+                            *  This is sometimes INVIS various ANSI.\r
+                            * case 21:\r
+                            *  This like 22 disables BOLD, DIM and INVIS\r
+                            *\r
+                            * The ANSI colours appear on any\r
+                            * terminal that has colour (obviously)\r
+                            * but the interaction between sgr0 and\r
+                            * the colours varies but is usually\r
+                            * related to the background colour\r
+                            * erase item. The interaction between\r
+                            * colour attributes and the mono ones\r
+                            * is also very implementation\r
+                            * dependent.\r
+                            *\r
+                            * The 39 and 49 attributes are likely\r
+                            * to be unimplemented.\r
+                            */\r
+                           int i;\r
+                           for (i = 0; i < term->esc_nargs; i++) {\r
+                               switch (def(term->esc_args[i], 0)) {\r
+                                 case 0:       /* restore defaults */\r
+                                   term->curr_attr = term->default_attr;\r
+                                   break;\r
+                                 case 1:       /* enable bold */\r
+                                   compatibility(VT100AVO);\r
+                                   term->curr_attr |= ATTR_BOLD;\r
+                                   break;\r
+                                 case 21:      /* (enable double underline) */\r
+                                   compatibility(OTHER);\r
+                                 case 4:       /* enable underline */\r
+                                   compatibility(VT100AVO);\r
+                                   term->curr_attr |= ATTR_UNDER;\r
+                                   break;\r
+                                 case 5:       /* enable blink */\r
+                                   compatibility(VT100AVO);\r
+                                   term->curr_attr |= ATTR_BLINK;\r
+                                   break;\r
+                                 case 6:       /* SCO light bkgrd */\r
+                                   compatibility(SCOANSI);\r
+                                   term->blink_is_real = FALSE;\r
+                                   term->curr_attr |= ATTR_BLINK;\r
+                                   term_schedule_tblink(term);\r
+                                   break;\r
+                                 case 7:       /* enable reverse video */\r
+                                   term->curr_attr |= ATTR_REVERSE;\r
+                                   break;\r
+                                 case 10:      /* SCO acs off */\r
+                                   compatibility(SCOANSI);\r
+                                   if (term->cfg.no_remote_charset) break;\r
+                                   term->sco_acs = 0; break;\r
+                                 case 11:      /* SCO acs on */\r
+                                   compatibility(SCOANSI);\r
+                                   if (term->cfg.no_remote_charset) break;\r
+                                   term->sco_acs = 1; break;\r
+                                 case 12:      /* SCO acs on, |0x80 */\r
+                                   compatibility(SCOANSI);\r
+                                   if (term->cfg.no_remote_charset) break;\r
+                                   term->sco_acs = 2; break;\r
+                                 case 22:      /* disable bold */\r
+                                   compatibility2(OTHER, VT220);\r
+                                   term->curr_attr &= ~ATTR_BOLD;\r
+                                   break;\r
+                                 case 24:      /* disable underline */\r
+                                   compatibility2(OTHER, VT220);\r
+                                   term->curr_attr &= ~ATTR_UNDER;\r
+                                   break;\r
+                                 case 25:      /* disable blink */\r
+                                   compatibility2(OTHER, VT220);\r
+                                   term->curr_attr &= ~ATTR_BLINK;\r
+                                   break;\r
+                                 case 27:      /* disable reverse video */\r
+                                   compatibility2(OTHER, VT220);\r
+                                   term->curr_attr &= ~ATTR_REVERSE;\r
+                                   break;\r
+                                 case 30:\r
+                                 case 31:\r
+                                 case 32:\r
+                                 case 33:\r
+                                 case 34:\r
+                                 case 35:\r
+                                 case 36:\r
+                                 case 37:\r
+                                   /* foreground */\r
+                                   term->curr_attr &= ~ATTR_FGMASK;\r
+                                   term->curr_attr |=\r
+                                       (term->esc_args[i] - 30)<<ATTR_FGSHIFT;\r
+                                   break;\r
+                                 case 90:\r
+                                 case 91:\r
+                                 case 92:\r
+                                 case 93:\r
+                                 case 94:\r
+                                 case 95:\r
+                                 case 96:\r
+                                 case 97:\r
+                                   /* aixterm-style bright foreground */\r
+                                   term->curr_attr &= ~ATTR_FGMASK;\r
+                                   term->curr_attr |=\r
+                                       ((term->esc_args[i] - 90 + 8)\r
+                                         << ATTR_FGSHIFT);\r
+                                   break;\r
+                                 case 39:      /* default-foreground */\r
+                                   term->curr_attr &= ~ATTR_FGMASK;\r
+                                   term->curr_attr |= ATTR_DEFFG;\r
+                                   break;\r
+                                 case 40:\r
+                                 case 41:\r
+                                 case 42:\r
+                                 case 43:\r
+                                 case 44:\r
+                                 case 45:\r
+                                 case 46:\r
+                                 case 47:\r
+                                   /* background */\r
+                                   term->curr_attr &= ~ATTR_BGMASK;\r
+                                   term->curr_attr |=\r
+                                       (term->esc_args[i] - 40)<<ATTR_BGSHIFT;\r
+                                   break;\r
+                                 case 100:\r
+                                 case 101:\r
+                                 case 102:\r
+                                 case 103:\r
+                                 case 104:\r
+                                 case 105:\r
+                                 case 106:\r
+                                 case 107:\r
+                                   /* aixterm-style bright background */\r
+                                   term->curr_attr &= ~ATTR_BGMASK;\r
+                                   term->curr_attr |=\r
+                                       ((term->esc_args[i] - 100 + 8)\r
+                                         << ATTR_BGSHIFT);\r
+                                   break;\r
+                                 case 49:      /* default-background */\r
+                                   term->curr_attr &= ~ATTR_BGMASK;\r
+                                   term->curr_attr |= ATTR_DEFBG;\r
+                                   break;\r
+                                 case 38:   /* xterm 256-colour mode */\r
+                                   if (i+2 < term->esc_nargs &&\r
+                                       term->esc_args[i+1] == 5) {\r
+                                       term->curr_attr &= ~ATTR_FGMASK;\r
+                                       term->curr_attr |=\r
+                                           ((term->esc_args[i+2] & 0xFF)\r
+                                            << ATTR_FGSHIFT);\r
+                                       i += 2;\r
+                                   }\r
+                                   break;\r
+                                 case 48:   /* xterm 256-colour mode */\r
+                                   if (i+2 < term->esc_nargs &&\r
+                                       term->esc_args[i+1] == 5) {\r
+                                       term->curr_attr &= ~ATTR_BGMASK;\r
+                                       term->curr_attr |=\r
+                                           ((term->esc_args[i+2] & 0xFF)\r
+                                            << ATTR_BGSHIFT);\r
+                                       i += 2;\r
+                                   }\r
+                                   break;\r
+                               }\r
+                           }\r
+                           set_erase_char(term);\r
+                       }\r
+                       break;\r
+                     case 's':       /* save cursor */\r
+                       save_cursor(term, TRUE);\r
+                       break;\r
+                     case 'u':       /* restore cursor */\r
+                       save_cursor(term, FALSE);\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 't': /* DECSLPP: set page size - ie window height */\r
+                       /*\r
+                        * VT340/VT420 sequence DECSLPP, DEC only allows values\r
+                        *  24/25/36/48/72/144 other emulators (eg dtterm) use\r
+                        * illegal values (eg first arg 1..9) for window changing \r
+                        * and reports.\r
+                        */\r
+                       if (term->esc_nargs <= 1\r
+                           && (term->esc_args[0] < 1 ||\r
+                               term->esc_args[0] >= 24)) {\r
+                           compatibility(VT340TEXT);\r
+                           if (!term->cfg.no_remote_resize)\r
+                               request_resize(term->frontend, term->cols,\r
+                                              def(term->esc_args[0], 24));\r
+                           deselect(term);\r
+                       } else if (term->esc_nargs >= 1 &&\r
+                                  term->esc_args[0] >= 1 &&\r
+                                  term->esc_args[0] < 24) {\r
+                           compatibility(OTHER);\r
+\r
+                           switch (term->esc_args[0]) {\r
+                               int x, y, len;\r
+                               char buf[80], *p;\r
+                             case 1:\r
+                               set_iconic(term->frontend, FALSE);\r
+                               break;\r
+                             case 2:\r
+                               set_iconic(term->frontend, TRUE);\r
+                               break;\r
+                             case 3:\r
+                               if (term->esc_nargs >= 3) {\r
+                                   if (!term->cfg.no_remote_resize)\r
+                                       move_window(term->frontend,\r
+                                                   def(term->esc_args[1], 0),\r
+                                                   def(term->esc_args[2], 0));\r
+                               }\r
+                               break;\r
+                             case 4:\r
+                               /* We should resize the window to a given\r
+                                * size in pixels here, but currently our\r
+                                * resizing code isn't healthy enough to\r
+                                * manage it. */\r
+                               break;\r
+                             case 5:\r
+                               /* move to top */\r
+                               set_zorder(term->frontend, TRUE);\r
+                               break;\r
+                             case 6:\r
+                               /* move to bottom */\r
+                               set_zorder(term->frontend, FALSE);\r
+                               break;\r
+                             case 7:\r
+                               refresh_window(term->frontend);\r
+                               break;\r
+                             case 8:\r
+                               if (term->esc_nargs >= 3) {\r
+                                   if (!term->cfg.no_remote_resize)\r
+                                       request_resize(term->frontend,\r
+                                                      def(term->esc_args[2], term->cfg.width),\r
+                                                      def(term->esc_args[1], term->cfg.height));\r
+                               }\r
+                               break;\r
+                             case 9:\r
+                               if (term->esc_nargs >= 2)\r
+                                   set_zoomed(term->frontend,\r
+                                              term->esc_args[1] ?\r
+                                              TRUE : FALSE);\r
+                               break;\r
+                             case 11:\r
+                               if (term->ldisc)\r
+                                   ldisc_send(term->ldisc,\r
+                                              is_iconic(term->frontend) ?\r
+                                              "\033[2t" : "\033[1t", 4, 0);\r
+                               break;\r
+                             case 13:\r
+                               if (term->ldisc) {\r
+                                   get_window_pos(term->frontend, &x, &y);\r
+                                   len = sprintf(buf, "\033[3;%d;%dt", x, y);\r
+                                   ldisc_send(term->ldisc, buf, len, 0);\r
+                               }\r
+                               break;\r
+                             case 14:\r
+                               if (term->ldisc) {\r
+                                   get_window_pixels(term->frontend, &x, &y);\r
+                                   len = sprintf(buf, "\033[4;%d;%dt", y, x);\r
+                                   ldisc_send(term->ldisc, buf, len, 0);\r
+                               }\r
+                               break;\r
+                             case 18:\r
+                               if (term->ldisc) {\r
+                                   len = sprintf(buf, "\033[8;%d;%dt",\r
+                                                 term->rows, term->cols);\r
+                                   ldisc_send(term->ldisc, buf, len, 0);\r
+                               }\r
+                               break;\r
+                             case 19:\r
+                               /*\r
+                                * Hmmm. Strictly speaking we\r
+                                * should return `the size of the\r
+                                * screen in characters', but\r
+                                * that's not easy: (a) window\r
+                                * furniture being what it is it's\r
+                                * hard to compute, and (b) in\r
+                                * resize-font mode maximising the\r
+                                * window wouldn't change the\r
+                                * number of characters. *shrug*. I\r
+                                * think we'll ignore it for the\r
+                                * moment and see if anyone\r
+                                * complains, and then ask them\r
+                                * what they would like it to do.\r
+                                */\r
+                               break;\r
+                             case 20:\r
+                               if (term->ldisc &&\r
+                                   term->cfg.remote_qtitle_action != TITLE_NONE) {\r
+                                   if(term->cfg.remote_qtitle_action == TITLE_REAL)\r
+                                       p = get_window_title(term->frontend, TRUE);\r
+                                   else\r
+                                       p = EMPTY_WINDOW_TITLE;\r
+                                   len = strlen(p);\r
+                                   ldisc_send(term->ldisc, "\033]L", 3, 0);\r
+                                   ldisc_send(term->ldisc, p, len, 0);\r
+                                   ldisc_send(term->ldisc, "\033\\", 2, 0);\r
+                               }\r
+                               break;\r
+                             case 21:\r
+                               if (term->ldisc &&\r
+                                   term->cfg.remote_qtitle_action != TITLE_NONE) {\r
+                                   if(term->cfg.remote_qtitle_action == TITLE_REAL)\r
+                                       p = get_window_title(term->frontend, FALSE);\r
+                                   else\r
+                                       p = EMPTY_WINDOW_TITLE;\r
+                                   len = strlen(p);\r
+                                   ldisc_send(term->ldisc, "\033]l", 3, 0);\r
+                                   ldisc_send(term->ldisc, p, len, 0);\r
+                                   ldisc_send(term->ldisc, "\033\\", 2, 0);\r
+                               }\r
+                               break;\r
+                           }\r
+                       }\r
+                       break;\r
+                     case 'S':         /* SU: Scroll up */\r
+                       compatibility(SCOANSI);\r
+                       scroll(term, term->marg_t, term->marg_b,\r
+                              def(term->esc_args[0], 1), TRUE);\r
+                       term->wrapnext = FALSE;\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case 'T':         /* SD: Scroll down */\r
+                       compatibility(SCOANSI);\r
+                       scroll(term, term->marg_t, term->marg_b,\r
+                              -def(term->esc_args[0], 1), TRUE);\r
+                       term->wrapnext = FALSE;\r
+                       seen_disp_event(term);\r
+                       break;\r
+                     case ANSI('|', '*'): /* DECSNLS */\r
+                       /* \r
+                        * Set number of lines on screen\r
+                        * VT420 uses VGA like hardware and can\r
+                        * support any size in reasonable range\r
+                        * (24..49 AIUI) with no default specified.\r
+                        */\r
+                       compatibility(VT420);\r
+                       if (term->esc_nargs == 1 && term->esc_args[0] > 0) {\r
+                           if (!term->cfg.no_remote_resize)\r
+                               request_resize(term->frontend, term->cols,\r
+                                              def(term->esc_args[0],\r
+                                                  term->cfg.height));\r
+                           deselect(term);\r
+                       }\r
+                       break;\r
+                     case ANSI('|', '$'): /* DECSCPP */\r
+                       /*\r
+                        * Set number of columns per page\r
+                        * Docs imply range is only 80 or 132, but\r
+                        * I'll allow any.\r
+                        */\r
+                       compatibility(VT340TEXT);\r
+                       if (term->esc_nargs <= 1) {\r
+                           if (!term->cfg.no_remote_resize)\r
+                               request_resize(term->frontend,\r
+                                              def(term->esc_args[0],\r
+                                                  term->cfg.width), term->rows);\r
+                           deselect(term);\r
+                       }\r
+                       break;\r
+                     case 'X':     /* ECH: write N spaces w/o moving cursor */\r
+                       /* XXX VTTEST says this is vt220, vt510 manual\r
+                        * says vt100 */\r
+                       compatibility(ANSIMIN);\r
+                       {\r
+                           int n = def(term->esc_args[0], 1);\r
+                           pos cursplus;\r
+                           int p = term->curs.x;\r
+                           termline *cline = scrlineptr(term->curs.y);\r
+\r
+                           if (n > term->cols - term->curs.x)\r
+                               n = term->cols - term->curs.x;\r
+                           cursplus = term->curs;\r
+                           cursplus.x += n;\r
+                           check_boundary(term, term->curs.x, term->curs.y);\r
+                           check_boundary(term, term->curs.x+n, term->curs.y);\r
+                           check_selection(term, term->curs, cursplus);\r
+                           while (n--)\r
+                               copy_termchar(cline, p++,\r
+                                             &term->erase_char);\r
+                           seen_disp_event(term);\r
+                       }\r
+                       break;\r
+                     case 'x':       /* DECREQTPARM: report terminal characteristics */\r
+                       compatibility(VT100);\r
+                       if (term->ldisc) {\r
+                           char buf[32];\r
+                           int i = def(term->esc_args[0], 0);\r
+                           if (i == 0 || i == 1) {\r
+                               strcpy(buf, "\033[2;1;1;112;112;1;0x");\r
+                               buf[2] += i;\r
+                               ldisc_send(term->ldisc, buf, 20, 0);\r
+                           }\r
+                       }\r
+                       break;\r
+                     case 'Z':         /* CBT */\r
+                       compatibility(OTHER);\r
+                       {\r
+                           int i = def(term->esc_args[0], 1);\r
+                           pos old_curs = term->curs;\r
+\r
+                           for(;i>0 && term->curs.x>0; i--) {\r
+                               do {\r
+                                   term->curs.x--;\r
+                               } while (term->curs.x >0 &&\r
+                                        !term->tabs[term->curs.x]);\r
+                           }\r
+                           check_selection(term, old_curs, term->curs);\r
+                       }\r
+                       break;\r
+                     case ANSI('c', '='):      /* Hide or Show Cursor */\r
+                       compatibility(SCOANSI);\r
+                       switch(term->esc_args[0]) {\r
+                         case 0:  /* hide cursor */\r
+                           term->cursor_on = FALSE;\r
+                           break;\r
+                         case 1:  /* restore cursor */\r
+                           term->big_cursor = FALSE;\r
+                           term->cursor_on = TRUE;\r
+                           break;\r
+                         case 2:  /* block cursor */\r
+                           term->big_cursor = TRUE;\r
+                           term->cursor_on = TRUE;\r
+                           break;\r
+                       }\r
+                       break;\r
+                     case ANSI('C', '='):\r
+                       /*\r
+                        * set cursor start on scanline esc_args[0] and\r
+                        * end on scanline esc_args[1].If you set\r
+                        * the bottom scan line to a value less than\r
+                        * the top scan line, the cursor will disappear.\r
+                        */\r
+                       compatibility(SCOANSI);\r
+                       if (term->esc_nargs >= 2) {\r
+                           if (term->esc_args[0] > term->esc_args[1])\r
+                               term->cursor_on = FALSE;\r
+                           else\r
+                               term->cursor_on = TRUE;\r
+                       }\r
+                       break;\r
+                     case ANSI('D', '='):\r
+                       compatibility(SCOANSI);\r
+                       term->blink_is_real = FALSE;\r
+                       term_schedule_tblink(term);\r
+                       if (term->esc_args[0]>=1)\r
+                           term->curr_attr |= ATTR_BLINK;\r
+                       else\r
+                           term->curr_attr &= ~ATTR_BLINK;\r
+                       break;\r
+                     case ANSI('E', '='):\r
+                       compatibility(SCOANSI);\r
+                       term->blink_is_real = (term->esc_args[0] >= 1);\r
+                       term_schedule_tblink(term);\r
+                       break;\r
+                     case ANSI('F', '='):      /* set normal foreground */\r
+                       compatibility(SCOANSI);\r
+                       if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {\r
+                           long colour =\r
+                               (sco2ansicolour[term->esc_args[0] & 0x7] |\r
+                                (term->esc_args[0] & 0x8)) <<\r
+                               ATTR_FGSHIFT;\r
+                           term->curr_attr &= ~ATTR_FGMASK;\r
+                           term->curr_attr |= colour;\r
+                           term->default_attr &= ~ATTR_FGMASK;\r
+                           term->default_attr |= colour;\r
+                           set_erase_char(term);\r
+                       }\r
+                       break;\r
+                     case ANSI('G', '='):      /* set normal background */\r
+                       compatibility(SCOANSI);\r
+                       if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {\r
+                           long colour =\r
+                               (sco2ansicolour[term->esc_args[0] & 0x7] |\r
+                                (term->esc_args[0] & 0x8)) <<\r
+                               ATTR_BGSHIFT;\r
+                           term->curr_attr &= ~ATTR_BGMASK;\r
+                           term->curr_attr |= colour;\r
+                           term->default_attr &= ~ATTR_BGMASK;\r
+                           term->default_attr |= colour;\r
+                           set_erase_char(term);\r
+                       }\r
+                       break;\r
+                     case ANSI('L', '='):\r
+                       compatibility(SCOANSI);\r
+                       term->use_bce = (term->esc_args[0] <= 0);\r
+                       set_erase_char(term);\r
+                       break;\r
+                     case ANSI('p', '"'): /* DECSCL: set compat level */\r
+                       /*\r
+                        * Allow the host to make this emulator a\r
+                        * 'perfect' VT102. This first appeared in\r
+                        * the VT220, but we do need to get back to\r
+                        * PuTTY mode so I won't check it.\r
+                        *\r
+                        * The arg in 40..42,50 are a PuTTY extension.\r
+                        * The 2nd arg, 8bit vs 7bit is not checked.\r
+                        *\r
+                        * Setting VT102 mode should also change\r
+                        * the Fkeys to generate PF* codes as a\r
+                        * real VT102 has no Fkeys. The VT220 does\r
+                        * this, F11..F13 become ESC,BS,LF other\r
+                        * Fkeys send nothing.\r
+                        *\r
+                        * Note ESC c will NOT change this!\r
+                        */\r
+\r
+                       switch (term->esc_args[0]) {\r
+                         case 61:\r
+                           term->compatibility_level &= ~TM_VTXXX;\r
+                           term->compatibility_level |= TM_VT102;\r
+                           break;\r
+                         case 62:\r
+                           term->compatibility_level &= ~TM_VTXXX;\r
+                           term->compatibility_level |= TM_VT220;\r
+                           break;\r
+\r
+                         default:\r
+                           if (term->esc_args[0] > 60 &&\r
+                               term->esc_args[0] < 70)\r
+                               term->compatibility_level |= TM_VTXXX;\r
+                           break;\r
+\r
+                         case 40:\r
+                           term->compatibility_level &= TM_VTXXX;\r
+                           break;\r
+                         case 41:\r
+                           term->compatibility_level = TM_PUTTY;\r
+                           break;\r
+                         case 42:\r
+                           term->compatibility_level = TM_SCOANSI;\r
+                           break;\r
+\r
+                         case ARG_DEFAULT:\r
+                           term->compatibility_level = TM_PUTTY;\r
+                           break;\r
+                         case 50:\r
+                           break;\r
+                       }\r
+\r
+                       /* Change the response to CSI c */\r
+                       if (term->esc_args[0] == 50) {\r
+                           int i;\r
+                           char lbuf[64];\r
+                           strcpy(term->id_string, "\033[?");\r
+                           for (i = 1; i < term->esc_nargs; i++) {\r
+                               if (i != 1)\r
+                                   strcat(term->id_string, ";");\r
+                               sprintf(lbuf, "%d", term->esc_args[i]);\r
+                               strcat(term->id_string, lbuf);\r
+                           }\r
+                           strcat(term->id_string, "c");\r
+                       }\r
+#if 0\r
+                       /* Is this a good idea ? \r
+                        * Well we should do a soft reset at this point ...\r
+                        */\r
+                       if (!has_compat(VT420) && has_compat(VT100)) {\r
+                           if (!term->cfg.no_remote_resize) {\r
+                               if (term->reset_132)\r
+                                   request_resize(132, 24);\r
+                               else\r
+                                   request_resize(80, 24);\r
+                           }\r
+                       }\r
+#endif\r
+                       break;\r
+                   }\r
+               break;\r
+             case SEEN_OSC:\r
+               term->osc_w = FALSE;\r
+               switch (c) {\r
+                 case 'P':            /* Linux palette sequence */\r
+                   term->termstate = SEEN_OSC_P;\r
+                   term->osc_strlen = 0;\r
+                   break;\r
+                 case 'R':            /* Linux palette reset */\r
+                   palette_reset(term->frontend);\r
+                   term_invalidate(term);\r
+                   term->termstate = TOPLEVEL;\r
+                   break;\r
+                 case 'W':            /* word-set */\r
+                   term->termstate = SEEN_OSC_W;\r
+                   term->osc_w = TRUE;\r
+                   break;\r
+                 case '0':\r
+                 case '1':\r
+                 case '2':\r
+                 case '3':\r
+                 case '4':\r
+                 case '5':\r
+                 case '6':\r
+                 case '7':\r
+                 case '8':\r
+                 case '9':\r
+                   term->esc_args[0] = 10 * term->esc_args[0] + c - '0';\r
+                   break;\r
+                 case 'L':\r
+                   /*\r
+                    * Grotty hack to support xterm and DECterm title\r
+                    * sequences concurrently.\r
+                    */\r
+                   if (term->esc_args[0] == 2) {\r
+                       term->esc_args[0] = 1;\r
+                       break;\r
+                   }\r
+                   /* else fall through */\r
+                 default:\r
+                   term->termstate = OSC_STRING;\r
+                   term->osc_strlen = 0;\r
+               }\r
+               break;\r
+             case OSC_STRING:\r
+               /*\r
+                * This OSC stuff is EVIL. It takes just one character to get into\r
+                * sysline mode and it's not initially obvious how to get out.\r
+                * So I've added CR and LF as string aborts.\r
+                * This shouldn't effect compatibility as I believe embedded \r
+                * control characters are supposed to be interpreted (maybe?) \r
+                * and they don't display anything useful anyway.\r
+                *\r
+                * -- RDB\r
+                */\r
+               if (c == '\012' || c == '\015') {\r
+                   term->termstate = TOPLEVEL;\r
+               } else if (c == 0234 || c == '\007') {\r
+                   /*\r
+                    * These characters terminate the string; ST and BEL\r
+                    * terminate the sequence and trigger instant\r
+                    * processing of it, whereas ESC goes back to SEEN_ESC\r
+                    * mode unless it is followed by \, in which case it is\r
+                    * synonymous with ST in the first place.\r
+                    */\r
+                   do_osc(term);\r
+                   term->termstate = TOPLEVEL;\r
+               } else if (c == '\033')\r
+                   term->termstate = OSC_MAYBE_ST;\r
+               else if (term->osc_strlen < OSC_STR_MAX)\r
+                   term->osc_string[term->osc_strlen++] = (char)c;\r
+               break;\r
+             case SEEN_OSC_P:\r
+               {\r
+                   int max = (term->osc_strlen == 0 ? 21 : 15);\r
+                   int val;\r
+                   if ((int)c >= '0' && (int)c <= '9')\r
+                       val = c - '0';\r
+                   else if ((int)c >= 'A' && (int)c <= 'A' + max - 10)\r
+                       val = c - 'A' + 10;\r
+                   else if ((int)c >= 'a' && (int)c <= 'a' + max - 10)\r
+                       val = c - 'a' + 10;\r
+                   else {\r
+                       term->termstate = TOPLEVEL;\r
+                       break;\r
+                   }\r
+                   term->osc_string[term->osc_strlen++] = val;\r
+                   if (term->osc_strlen >= 7) {\r
+                       palette_set(term->frontend, term->osc_string[0],\r
+                                   term->osc_string[1] * 16 + term->osc_string[2],\r
+                                   term->osc_string[3] * 16 + term->osc_string[4],\r
+                                   term->osc_string[5] * 16 + term->osc_string[6]);\r
+                       term_invalidate(term);\r
+                       term->termstate = TOPLEVEL;\r
+                   }\r
+               }\r
+               break;\r
+             case SEEN_OSC_W:\r
+               switch (c) {\r
+                 case '0':\r
+                 case '1':\r
+                 case '2':\r
+                 case '3':\r
+                 case '4':\r
+                 case '5':\r
+                 case '6':\r
+                 case '7':\r
+                 case '8':\r
+                 case '9':\r
+                   term->esc_args[0] = 10 * term->esc_args[0] + c - '0';\r
+                   break;\r
+                 default:\r
+                   term->termstate = OSC_STRING;\r
+                   term->osc_strlen = 0;\r
+               }\r
+               break;\r
+             case VT52_ESC:\r
+               term->termstate = TOPLEVEL;\r
+               seen_disp_event(term);\r
+               switch (c) {\r
+                 case 'A':\r
+                   move(term, term->curs.x, term->curs.y - 1, 1);\r
+                   break;\r
+                 case 'B':\r
+                   move(term, term->curs.x, term->curs.y + 1, 1);\r
+                   break;\r
+                 case 'C':\r
+                   move(term, term->curs.x + 1, term->curs.y, 1);\r
+                   break;\r
+                 case 'D':\r
+                   move(term, term->curs.x - 1, term->curs.y, 1);\r
+                   break;\r
+                   /*\r
+                    * From the VT100 Manual\r
+                    * NOTE: The special graphics characters in the VT100\r
+                    *       are different from those in the VT52\r
+                    *\r
+                    * From VT102 manual:\r
+                    *       137 _  Blank             - Same\r
+                    *       140 `  Reserved          - Humm.\r
+                    *       141 a  Solid rectangle   - Similar\r
+                    *       142 b  1/                - Top half of fraction for the\r
+                    *       143 c  3/                - subscript numbers below.\r
+                    *       144 d  5/\r
+                    *       145 e  7/\r
+                    *       146 f  Degrees           - Same\r
+                    *       147 g  Plus or minus     - Same\r
+                    *       150 h  Right arrow\r
+                    *       151 i  Ellipsis (dots)\r
+                    *       152 j  Divide by\r
+                    *       153 k  Down arrow\r
+                    *       154 l  Bar at scan 0\r
+                    *       155 m  Bar at scan 1\r
+                    *       156 n  Bar at scan 2\r
+                    *       157 o  Bar at scan 3     - Similar\r
+                    *       160 p  Bar at scan 4     - Similar\r
+                    *       161 q  Bar at scan 5     - Similar\r
+                    *       162 r  Bar at scan 6     - Same\r
+                    *       163 s  Bar at scan 7     - Similar\r
+                    *       164 t  Subscript 0\r
+                    *       165 u  Subscript 1\r
+                    *       166 v  Subscript 2\r
+                    *       167 w  Subscript 3\r
+                    *       170 x  Subscript 4\r
+                    *       171 y  Subscript 5\r
+                    *       172 z  Subscript 6\r
+                    *       173 {  Subscript 7\r
+                    *       174 |  Subscript 8\r
+                    *       175 }  Subscript 9\r
+                    *       176 ~  Paragraph\r
+                    *\r
+                    */\r
+                 case 'F':\r
+                   term->cset_attr[term->cset = 0] = CSET_LINEDRW;\r
+                   break;\r
+                 case 'G':\r
+                   term->cset_attr[term->cset = 0] = CSET_ASCII;\r
+                   break;\r
+                 case 'H':\r
+                   move(term, 0, 0, 0);\r
+                   break;\r
+                 case 'I':\r
+                   if (term->curs.y == 0)\r
+                       scroll(term, 0, term->rows - 1, -1, TRUE);\r
+                   else if (term->curs.y > 0)\r
+                       term->curs.y--;\r
+                   term->wrapnext = FALSE;\r
+                   break;\r
+                 case 'J':\r
+                   erase_lots(term, FALSE, FALSE, TRUE);\r
+                   term->disptop = 0;\r
+                   break;\r
+                 case 'K':\r
+                   erase_lots(term, TRUE, FALSE, TRUE);\r
+                   break;\r
+#if 0\r
+                 case 'V':\r
+                   /* XXX Print cursor line */\r
+                   break;\r
+                 case 'W':\r
+                   /* XXX Start controller mode */\r
+                   break;\r
+                 case 'X':\r
+                   /* XXX Stop controller mode */\r
+                   break;\r
+#endif\r
+                 case 'Y':\r
+                   term->termstate = VT52_Y1;\r
+                   break;\r
+                 case 'Z':\r
+                   if (term->ldisc)\r
+                       ldisc_send(term->ldisc, "\033/Z", 3, 0);\r
+                   break;\r
+                 case '=':\r
+                   term->app_keypad_keys = TRUE;\r
+                   break;\r
+                 case '>':\r
+                   term->app_keypad_keys = FALSE;\r
+                   break;\r
+                 case '<':\r
+                   /* XXX This should switch to VT100 mode not current or default\r
+                    *     VT mode. But this will only have effect in a VT220+\r
+                    *     emulation.\r
+                    */\r
+                   term->vt52_mode = FALSE;\r
+                   term->blink_is_real = term->cfg.blinktext;\r
+                   term_schedule_tblink(term);\r
+                   break;\r
+#if 0\r
+                 case '^':\r
+                   /* XXX Enter auto print mode */\r
+                   break;\r
+                 case '_':\r
+                   /* XXX Exit auto print mode */\r
+                   break;\r
+                 case ']':\r
+                   /* XXX Print screen */\r
+                   break;\r
+#endif\r
+\r
+#ifdef VT52_PLUS\r
+                 case 'E':\r
+                   /* compatibility(ATARI) */\r
+                   move(term, 0, 0, 0);\r
+                   erase_lots(term, FALSE, FALSE, TRUE);\r
+                   term->disptop = 0;\r
+                   break;\r
+                 case 'L':\r
+                   /* compatibility(ATARI) */\r
+                   if (term->curs.y <= term->marg_b)\r
+                       scroll(term, term->curs.y, term->marg_b, -1, FALSE);\r
+                   break;\r
+                 case 'M':\r
+                   /* compatibility(ATARI) */\r
+                   if (term->curs.y <= term->marg_b)\r
+                       scroll(term, term->curs.y, term->marg_b, 1, TRUE);\r
+                   break;\r
+                 case 'b':\r
+                   /* compatibility(ATARI) */\r
+                   term->termstate = VT52_FG;\r
+                   break;\r
+                 case 'c':\r
+                   /* compatibility(ATARI) */\r
+                   term->termstate = VT52_BG;\r
+                   break;\r
+                 case 'd':\r
+                   /* compatibility(ATARI) */\r
+                   erase_lots(term, FALSE, TRUE, FALSE);\r
+                   term->disptop = 0;\r
+                   break;\r
+                 case 'e':\r
+                   /* compatibility(ATARI) */\r
+                   term->cursor_on = TRUE;\r
+                   break;\r
+                 case 'f':\r
+                   /* compatibility(ATARI) */\r
+                   term->cursor_on = FALSE;\r
+                   break;\r
+                   /* case 'j': Save cursor position - broken on ST */\r
+                   /* case 'k': Restore cursor position */\r
+                 case 'l':\r
+                   /* compatibility(ATARI) */\r
+                   erase_lots(term, TRUE, TRUE, TRUE);\r
+                   term->curs.x = 0;\r
+                   term->wrapnext = FALSE;\r
+                   break;\r
+                 case 'o':\r
+                   /* compatibility(ATARI) */\r
+                   erase_lots(term, TRUE, TRUE, FALSE);\r
+                   break;\r
+                 case 'p':\r
+                   /* compatibility(ATARI) */\r
+                   term->curr_attr |= ATTR_REVERSE;\r
+                   break;\r
+                 case 'q':\r
+                   /* compatibility(ATARI) */\r
+                   term->curr_attr &= ~ATTR_REVERSE;\r
+                   break;\r
+                 case 'v':            /* wrap Autowrap on - Wyse style */\r
+                   /* compatibility(ATARI) */\r
+                   term->wrap = 1;\r
+                   break;\r
+                 case 'w':            /* Autowrap off */\r
+                   /* compatibility(ATARI) */\r
+                   term->wrap = 0;\r
+                   break;\r
+\r
+                 case 'R':\r
+                   /* compatibility(OTHER) */\r
+                   term->vt52_bold = FALSE;\r
+                   term->curr_attr = ATTR_DEFAULT;\r
+                   set_erase_char(term);\r
+                   break;\r
+                 case 'S':\r
+                   /* compatibility(VI50) */\r
+                   term->curr_attr |= ATTR_UNDER;\r
+                   break;\r
+                 case 'W':\r
+                   /* compatibility(VI50) */\r
+                   term->curr_attr &= ~ATTR_UNDER;\r
+                   break;\r
+                 case 'U':\r
+                   /* compatibility(VI50) */\r
+                   term->vt52_bold = TRUE;\r
+                   term->curr_attr |= ATTR_BOLD;\r
+                   break;\r
+                 case 'T':\r
+                   /* compatibility(VI50) */\r
+                   term->vt52_bold = FALSE;\r
+                   term->curr_attr &= ~ATTR_BOLD;\r
+                   break;\r
+#endif\r
+               }\r
+               break;\r
+             case VT52_Y1:\r
+               term->termstate = VT52_Y2;\r
+               move(term, term->curs.x, c - ' ', 0);\r
+               break;\r
+             case VT52_Y2:\r
+               term->termstate = TOPLEVEL;\r
+               move(term, c - ' ', term->curs.y, 0);\r
+               break;\r
+\r
+#ifdef VT52_PLUS\r
+             case VT52_FG:\r
+               term->termstate = TOPLEVEL;\r
+               term->curr_attr &= ~ATTR_FGMASK;\r
+               term->curr_attr &= ~ATTR_BOLD;\r
+               term->curr_attr |= (c & 0xF) << ATTR_FGSHIFT;\r
+               set_erase_char(term);\r
+               break;\r
+             case VT52_BG:\r
+               term->termstate = TOPLEVEL;\r
+               term->curr_attr &= ~ATTR_BGMASK;\r
+               term->curr_attr &= ~ATTR_BLINK;\r
+               term->curr_attr |= (c & 0xF) << ATTR_BGSHIFT;\r
+               set_erase_char(term);\r
+               break;\r
+#endif\r
+             default: break;          /* placate gcc warning about enum use */\r
+           }\r
+       if (term->selstate != NO_SELECTION) {\r
+           pos cursplus = term->curs;\r
+           incpos(cursplus);\r
+           check_selection(term, term->curs, cursplus);\r
+       }\r
+    }\r
+\r
+    term_print_flush(term);\r
+    if (term->cfg.logflush)\r
+       logflush(term->logctx);\r
+}\r
+\r
+/*\r
+ * To prevent having to run the reasonably tricky bidi algorithm\r
+ * too many times, we maintain a cache of the last lineful of data\r
+ * fed to the algorithm on each line of the display.\r
+ */\r
+static int term_bidi_cache_hit(Terminal *term, int line,\r
+                              termchar *lbefore, int width)\r
+{\r
+    int i;\r
+\r
+    if (!term->pre_bidi_cache)\r
+       return FALSE;                  /* cache doesn't even exist yet! */\r
+\r
+    if (line >= term->bidi_cache_size)\r
+       return FALSE;                  /* cache doesn't have this many lines */\r
+\r
+    if (!term->pre_bidi_cache[line].chars)\r
+       return FALSE;                  /* cache doesn't contain _this_ line */\r
+\r
+    if (term->pre_bidi_cache[line].width != width)\r
+       return FALSE;                  /* line is wrong width */\r
+\r
+    for (i = 0; i < width; i++)\r
+       if (!termchars_equal(term->pre_bidi_cache[line].chars+i, lbefore+i))\r
+           return FALSE;              /* line doesn't match cache */\r
+\r
+    return TRUE;                      /* it didn't match. */\r
+}\r
+\r
+static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore,\r
+                                 termchar *lafter, bidi_char *wcTo,\r
+                                 int width, int size)\r
+{\r
+    int i;\r
+\r
+    if (!term->pre_bidi_cache || term->bidi_cache_size <= line) {\r
+       int j = term->bidi_cache_size;\r
+       term->bidi_cache_size = line+1;\r
+       term->pre_bidi_cache = sresize(term->pre_bidi_cache,\r
+                                      term->bidi_cache_size,\r
+                                      struct bidi_cache_entry);\r
+       term->post_bidi_cache = sresize(term->post_bidi_cache,\r
+                                       term->bidi_cache_size,\r
+                                       struct bidi_cache_entry);\r
+       while (j < term->bidi_cache_size) {\r
+           term->pre_bidi_cache[j].chars =\r
+               term->post_bidi_cache[j].chars = NULL;\r
+           term->pre_bidi_cache[j].width =\r
+               term->post_bidi_cache[j].width = -1;\r
+           term->pre_bidi_cache[j].forward =\r
+               term->post_bidi_cache[j].forward = NULL;\r
+           term->pre_bidi_cache[j].backward =\r
+               term->post_bidi_cache[j].backward = NULL;\r
+           j++;\r
+       }\r
+    }\r
+\r
+    sfree(term->pre_bidi_cache[line].chars);\r
+    sfree(term->post_bidi_cache[line].chars);\r
+    sfree(term->post_bidi_cache[line].forward);\r
+    sfree(term->post_bidi_cache[line].backward);\r
+\r
+    term->pre_bidi_cache[line].width = width;\r
+    term->pre_bidi_cache[line].chars = snewn(size, termchar);\r
+    term->post_bidi_cache[line].width = width;\r
+    term->post_bidi_cache[line].chars = snewn(size, termchar);\r
+    term->post_bidi_cache[line].forward = snewn(width, int);\r
+    term->post_bidi_cache[line].backward = snewn(width, int);\r
+\r
+    memcpy(term->pre_bidi_cache[line].chars, lbefore, size * TSIZE);\r
+    memcpy(term->post_bidi_cache[line].chars, lafter, size * TSIZE);\r
+    memset(term->post_bidi_cache[line].forward, 0, width * sizeof(int));\r
+    memset(term->post_bidi_cache[line].backward, 0, width * sizeof(int));\r
+\r
+    for (i = 0; i < width; i++) {\r
+       int p = wcTo[i].index;\r
+\r
+       assert(0 <= p && p < width);\r
+\r
+       term->post_bidi_cache[line].backward[i] = p;\r
+       term->post_bidi_cache[line].forward[p] = i;\r
+    }\r
+}\r
+\r
+/*\r
+ * Prepare the bidi information for a screen line. Returns the\r
+ * transformed list of termchars, or NULL if no transformation at\r
+ * all took place (because bidi is disabled). If return was\r
+ * non-NULL, auxiliary information such as the forward and reverse\r
+ * mappings of permutation position are available in\r
+ * term->post_bidi_cache[scr_y].*.\r
+ */\r
+static termchar *term_bidi_line(Terminal *term, struct termline *ldata,\r
+                               int scr_y)\r
+{\r
+    termchar *lchars;\r
+    int it;\r
+\r
+    /* Do Arabic shaping and bidi. */\r
+    if(!term->cfg.bidi || !term->cfg.arabicshaping) {\r
+\r
+       if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) {\r
+\r
+           if (term->wcFromTo_size < term->cols) {\r
+               term->wcFromTo_size = term->cols;\r
+               term->wcFrom = sresize(term->wcFrom, term->wcFromTo_size,\r
+                                      bidi_char);\r
+               term->wcTo = sresize(term->wcTo, term->wcFromTo_size,\r
+                                    bidi_char);\r
+           }\r
+\r
+           for(it=0; it<term->cols ; it++)\r
+           {\r
+               unsigned long uc = (ldata->chars[it].chr);\r
+\r
+               switch (uc & CSET_MASK) {\r
+                 case CSET_LINEDRW:\r
+                   if (!term->cfg.rawcnp) {\r
+                       uc = term->ucsdata->unitab_xterm[uc & 0xFF];\r
+                       break;\r
+                   }\r
+                 case CSET_ASCII:\r
+                   uc = term->ucsdata->unitab_line[uc & 0xFF];\r
+                   break;\r
+                 case CSET_SCOACS:\r
+                   uc = term->ucsdata->unitab_scoacs[uc&0xFF];\r
+                   break;\r
+               }\r
+               switch (uc & CSET_MASK) {\r
+                 case CSET_ACP:\r
+                   uc = term->ucsdata->unitab_font[uc & 0xFF];\r
+                   break;\r
+                 case CSET_OEMCP:\r
+                   uc = term->ucsdata->unitab_oemcp[uc & 0xFF];\r
+                   break;\r
+               }\r
+\r
+               term->wcFrom[it].origwc = term->wcFrom[it].wc =\r
+                   (wchar_t)uc;\r
+               term->wcFrom[it].index = it;\r
+           }\r
+\r
+           if(!term->cfg.bidi)\r
+               do_bidi(term->wcFrom, term->cols);\r
+\r
+           /* this is saved iff done from inside the shaping */\r
+           if(!term->cfg.bidi && term->cfg.arabicshaping)\r
+               for(it=0; it<term->cols; it++)\r
+                   term->wcTo[it] = term->wcFrom[it];\r
+\r
+           if(!term->cfg.arabicshaping)\r
+               do_shape(term->wcFrom, term->wcTo, term->cols);\r
+\r
+           if (term->ltemp_size < ldata->size) {\r
+               term->ltemp_size = ldata->size;\r
+               term->ltemp = sresize(term->ltemp, term->ltemp_size,\r
+                                     termchar);\r
+           }\r
+\r
+           memcpy(term->ltemp, ldata->chars, ldata->size * TSIZE);\r
+\r
+           for(it=0; it<term->cols ; it++)\r
+           {\r
+               term->ltemp[it] = ldata->chars[term->wcTo[it].index];\r
+               if (term->ltemp[it].cc_next)\r
+                   term->ltemp[it].cc_next -=\r
+                   it - term->wcTo[it].index;\r
+\r
+               if (term->wcTo[it].origwc != term->wcTo[it].wc)\r
+                   term->ltemp[it].chr = term->wcTo[it].wc;\r
+           }\r
+           term_bidi_cache_store(term, scr_y, ldata->chars,\r
+                                 term->ltemp, term->wcTo,\r
+                                  term->cols, ldata->size);\r
+\r
+           lchars = term->ltemp;\r
+       } else {\r
+           lchars = term->post_bidi_cache[scr_y].chars;\r
+       }\r
+    } else {\r
+       lchars = NULL;\r
+    }\r
+\r
+    return lchars;\r
+}\r
+\r
+/*\r
+ * Given a context, update the window. Out of paranoia, we don't\r
+ * allow WM_PAINT responses to do scrolling optimisations.\r
+ */\r
+static void do_paint(Terminal *term, Context ctx, int may_optimise)\r
+{\r
+    int i, j, our_curs_y, our_curs_x;\r
+    int rv, cursor;\r
+    pos scrpos;\r
+    wchar_t *ch;\r
+    int chlen;\r
+#ifdef OPTIMISE_SCROLL\r
+    struct scrollregion *sr;\r
+#endif /* OPTIMISE_SCROLL */\r
+    termchar *newline;\r
+\r
+    chlen = 1024;\r
+    ch = snewn(chlen, wchar_t);\r
+\r
+    newline = snewn(term->cols, termchar);\r
+\r
+    rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);\r
+\r
+    /* Depends on:\r
+     * screen array, disptop, scrtop,\r
+     * selection, rv, \r
+     * cfg.blinkpc, blink_is_real, tblinker, \r
+     * curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext\r
+     */\r
+\r
+    /* Has the cursor position or type changed ? */\r
+    if (term->cursor_on) {\r
+       if (term->has_focus) {\r
+           if (term->cblinker || !term->cfg.blink_cur)\r
+               cursor = TATTR_ACTCURS;\r
+           else\r
+               cursor = 0;\r
+       } else\r
+           cursor = TATTR_PASCURS;\r
+       if (term->wrapnext)\r
+           cursor |= TATTR_RIGHTCURS;\r
+    } else\r
+       cursor = 0;\r
+    our_curs_y = term->curs.y - term->disptop;\r
+    {\r
+       /*\r
+        * Adjust the cursor position:\r
+        *  - for bidi\r
+        *  - in the case where it's resting on the right-hand half\r
+        *    of a CJK wide character. xterm's behaviour here,\r
+        *    which seems adequate to me, is to display the cursor\r
+        *    covering the _whole_ character, exactly as if it were\r
+        *    one space to the left.\r
+        */\r
+       termline *ldata = lineptr(term->curs.y);\r
+       termchar *lchars;\r
+\r
+       our_curs_x = term->curs.x;\r
+\r
+       if ( (lchars = term_bidi_line(term, ldata, our_curs_y)) != NULL) {\r
+           our_curs_x = term->post_bidi_cache[our_curs_y].forward[our_curs_x];\r
+       } else\r
+           lchars = ldata->chars;\r
+\r
+       if (our_curs_x > 0 &&\r
+           lchars[our_curs_x].chr == UCSWIDE)\r
+           our_curs_x--;\r
+\r
+       unlineptr(ldata);\r
+    }\r
+\r
+    /*\r
+     * If the cursor is not where it was last time we painted, and\r
+     * its previous position is visible on screen, invalidate its\r
+     * previous position.\r
+     */\r
+    if (term->dispcursy >= 0 &&\r
+       (term->curstype != cursor ||\r
+        term->dispcursy != our_curs_y ||\r
+        term->dispcursx != our_curs_x)) {\r
+       termchar *dispcurs = term->disptext[term->dispcursy]->chars +\r
+           term->dispcursx;\r
+\r
+       if (term->dispcursx > 0 && dispcurs->chr == UCSWIDE)\r
+           dispcurs[-1].attr |= ATTR_INVALID;\r
+       if (term->dispcursx < term->cols-1 && dispcurs[1].chr == UCSWIDE)\r
+           dispcurs[1].attr |= ATTR_INVALID;\r
+       dispcurs->attr |= ATTR_INVALID;\r
+\r
+       term->curstype = 0;\r
+    }\r
+    term->dispcursx = term->dispcursy = -1;\r
+\r
+#ifdef OPTIMISE_SCROLL\r
+    /* Do scrolls */\r
+    sr = term->scrollhead;\r
+    while (sr) {\r
+       struct scrollregion *next = sr->next;\r
+       do_scroll(ctx, sr->topline, sr->botline, sr->lines);\r
+       sfree(sr);\r
+       sr = next;\r
+    }\r
+    term->scrollhead = term->scrolltail = NULL;\r
+#endif /* OPTIMISE_SCROLL */\r
+\r
+    /* The normal screen data */\r
+    for (i = 0; i < term->rows; i++) {\r
+       termline *ldata;\r
+       termchar *lchars;\r
+       int dirty_line, dirty_run, selected;\r
+       unsigned long attr = 0, cset = 0;\r
+       int start = 0;\r
+       int ccount = 0;\r
+       int last_run_dirty = 0;\r
+       int laststart, dirtyrect;\r
+       int *backward;\r
+\r
+       scrpos.y = i + term->disptop;\r
+       ldata = lineptr(scrpos.y);\r
+\r
+       /* Do Arabic shaping and bidi. */\r
+       lchars = term_bidi_line(term, ldata, i);\r
+       if (lchars) {\r
+           backward = term->post_bidi_cache[i].backward;\r
+       } else {\r
+           lchars = ldata->chars;\r
+           backward = NULL;\r
+       }\r
+\r
+       /*\r
+        * First loop: work along the line deciding what we want\r
+        * each character cell to look like.\r
+        */\r
+       for (j = 0; j < term->cols; j++) {\r
+           unsigned long tattr, tchar;\r
+           termchar *d = lchars + j;\r
+           scrpos.x = backward ? backward[j] : j;\r
+\r
+           tchar = d->chr;\r
+           tattr = d->attr;\r
+\r
+            if (!term->cfg.ansi_colour)\r
+                tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) | \r
+                ATTR_DEFFG | ATTR_DEFBG;\r
+\r
+           if (!term->cfg.xterm_256_colour) {\r
+               int colour;\r
+               colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;\r
+               if (colour >= 16 && colour < 256)\r
+                   tattr = (tattr &~ ATTR_FGMASK) | ATTR_DEFFG;\r
+               colour = (tattr & ATTR_BGMASK) >> ATTR_BGSHIFT;\r
+               if (colour >= 16 && colour < 256)\r
+                   tattr = (tattr &~ ATTR_BGMASK) | ATTR_DEFBG;\r
+           }\r
+\r
+           switch (tchar & CSET_MASK) {\r
+             case CSET_ASCII:\r
+               tchar = term->ucsdata->unitab_line[tchar & 0xFF];\r
+               break;\r
+             case CSET_LINEDRW:\r
+               tchar = term->ucsdata->unitab_xterm[tchar & 0xFF];\r
+               break;\r
+             case CSET_SCOACS:  \r
+               tchar = term->ucsdata->unitab_scoacs[tchar&0xFF]; \r
+               break;\r
+           }\r
+           if (j < term->cols-1 && d[1].chr == UCSWIDE)\r
+               tattr |= ATTR_WIDE;\r
+\r
+           /* Video reversing things */\r
+           if (term->selstate == DRAGGING || term->selstate == SELECTED) {\r
+               if (term->seltype == LEXICOGRAPHIC)\r
+                   selected = (posle(term->selstart, scrpos) &&\r
+                               poslt(scrpos, term->selend));\r
+               else\r
+                   selected = (posPle(term->selstart, scrpos) &&\r
+                               posPlt(scrpos, term->selend));\r
+           } else\r
+               selected = FALSE;\r
+           tattr = (tattr ^ rv\r
+                    ^ (selected ? ATTR_REVERSE : 0));\r
+\r
+           /* 'Real' blinking ? */\r
+           if (term->blink_is_real && (tattr & ATTR_BLINK)) {\r
+               if (term->has_focus && term->tblinker) {\r
+                   tchar = term->ucsdata->unitab_line[(unsigned char)' '];\r
+               }\r
+               tattr &= ~ATTR_BLINK;\r
+           }\r
+\r
+           /*\r
+            * Check the font we'll _probably_ be using to see if \r
+            * the character is wide when we don't want it to be.\r
+            */\r
+           if (tchar != term->disptext[i]->chars[j].chr ||\r
+               tattr != (term->disptext[i]->chars[j].attr &~\r
+                         (ATTR_NARROW | DATTR_MASK))) {\r
+               if ((tattr & ATTR_WIDE) == 0 && char_width(ctx, tchar) == 2)\r
+                   tattr |= ATTR_NARROW;\r
+           } else if (term->disptext[i]->chars[j].attr & ATTR_NARROW)\r
+               tattr |= ATTR_NARROW;\r
+\r
+           if (i == our_curs_y && j == our_curs_x) {\r
+               tattr |= cursor;\r
+               term->curstype = cursor;\r
+               term->dispcursx = j;\r
+               term->dispcursy = i;\r
+           }\r
+\r
+           /* FULL-TERMCHAR */\r
+           newline[j].attr = tattr;\r
+           newline[j].chr = tchar;\r
+           /* Combining characters are still read from lchars */\r
+           newline[j].cc_next = 0;\r
+       }\r
+\r
+       /*\r
+        * Now loop over the line again, noting where things have\r
+        * changed.\r
+        * \r
+        * During this loop, we keep track of where we last saw\r
+        * DATTR_STARTRUN. Any mismatch automatically invalidates\r
+        * _all_ of the containing run that was last printed: that\r
+        * is, any rectangle that was drawn in one go in the\r
+        * previous update should be either left completely alone\r
+        * or overwritten in its entirety. This, along with the\r
+        * expectation that front ends clip all text runs to their\r
+        * bounding rectangle, should solve any possible problems\r
+        * with fonts that overflow their character cells.\r
+        */\r
+       laststart = 0;\r
+       dirtyrect = FALSE;\r
+       for (j = 0; j < term->cols; j++) {\r
+           if (term->disptext[i]->chars[j].attr & DATTR_STARTRUN) {\r
+               laststart = j;\r
+               dirtyrect = FALSE;\r
+           }\r
+\r
+           if (term->disptext[i]->chars[j].chr != newline[j].chr ||\r
+               (term->disptext[i]->chars[j].attr &~ DATTR_MASK)\r
+               != newline[j].attr) {\r
+               int k;\r
+\r
+               if (!dirtyrect) {\r
+                   for (k = laststart; k < j; k++)\r
+                       term->disptext[i]->chars[k].attr |= ATTR_INVALID;\r
+\r
+                   dirtyrect = TRUE;\r
+               }\r
+           }\r
+\r
+           if (dirtyrect)\r
+               term->disptext[i]->chars[j].attr |= ATTR_INVALID;\r
+       }\r
+\r
+       /*\r
+        * Finally, loop once more and actually do the drawing.\r
+        */\r
+       dirty_run = dirty_line = (ldata->lattr !=\r
+                                 term->disptext[i]->lattr);\r
+       term->disptext[i]->lattr = ldata->lattr;\r
+\r
+       for (j = 0; j < term->cols; j++) {\r
+           unsigned long tattr, tchar;\r
+           int break_run, do_copy;\r
+           termchar *d = lchars + j;\r
+\r
+           tattr = newline[j].attr;\r
+           tchar = newline[j].chr;\r
+\r
+           if ((term->disptext[i]->chars[j].attr ^ tattr) & ATTR_WIDE)\r
+               dirty_line = TRUE;\r
+\r
+           break_run = ((tattr ^ attr) & term->attr_mask) != 0;\r
+\r
+           /* Special hack for VT100 Linedraw glyphs */\r
+           if (tchar >= 0x23BA && tchar <= 0x23BD)\r
+               break_run = TRUE;\r
+\r
+           /*\r
+            * Separate out sequences of characters that have the\r
+            * same CSET, if that CSET is a magic one.\r
+            */\r
+           if (CSET_OF(tchar) != cset)\r
+               break_run = TRUE;\r
+\r
+           /*\r
+            * Break on both sides of any combined-character cell.\r
+            */\r
+           if (d->cc_next != 0 ||\r
+               (j > 0 && d[-1].cc_next != 0))\r
+               break_run = TRUE;\r
+\r
+           if (!term->ucsdata->dbcs_screenfont && !dirty_line) {\r
+               if (term->disptext[i]->chars[j].chr == tchar &&\r
+                   (term->disptext[i]->chars[j].attr &~ DATTR_MASK) == tattr)\r
+                   break_run = TRUE;\r
+               else if (!dirty_run && ccount == 1)\r
+                   break_run = TRUE;\r
+           }\r
+\r
+           if (break_run) {\r
+               if ((dirty_run || last_run_dirty) && ccount > 0) {\r
+                   do_text(ctx, start, i, ch, ccount, attr,\r
+                           ldata->lattr);\r
+                   if (attr & (TATTR_ACTCURS | TATTR_PASCURS))\r
+                       do_cursor(ctx, start, i, ch, ccount, attr,\r
+                                 ldata->lattr);\r
+               }\r
+               start = j;\r
+               ccount = 0;\r
+               attr = tattr;\r
+               cset = CSET_OF(tchar);\r
+               if (term->ucsdata->dbcs_screenfont)\r
+                   last_run_dirty = dirty_run;\r
+               dirty_run = dirty_line;\r
+           }\r
+\r
+           do_copy = FALSE;\r
+           if (!termchars_equal_override(&term->disptext[i]->chars[j],\r
+                                         d, tchar, tattr)) {\r
+               do_copy = TRUE;\r
+               dirty_run = TRUE;\r
+           }\r
+\r
+           if (ccount >= chlen) {\r
+               chlen = ccount + 256;\r
+               ch = sresize(ch, chlen, wchar_t);\r
+           }\r
+           ch[ccount++] = (wchar_t) tchar;\r
+\r
+           if (d->cc_next) {\r
+               termchar *dd = d;\r
+\r
+               while (dd->cc_next) {\r
+                   unsigned long schar;\r
+\r
+                   dd += dd->cc_next;\r
+\r
+                   schar = dd->chr;\r
+                   switch (schar & CSET_MASK) {\r
+                     case CSET_ASCII:\r
+                       schar = term->ucsdata->unitab_line[schar & 0xFF];\r
+                       break;\r
+                     case CSET_LINEDRW:\r
+                       schar = term->ucsdata->unitab_xterm[schar & 0xFF];\r
+                       break;\r
+                     case CSET_SCOACS:\r
+                       schar = term->ucsdata->unitab_scoacs[schar&0xFF];\r
+                       break;\r
+                   }\r
+\r
+                   if (ccount >= chlen) {\r
+                       chlen = ccount + 256;\r
+                       ch = sresize(ch, chlen, wchar_t);\r
+                   }\r
+                   ch[ccount++] = (wchar_t) schar;\r
+               }\r
+\r
+               attr |= TATTR_COMBINING;\r
+           }\r
+\r
+           if (do_copy) {\r
+               copy_termchar(term->disptext[i], j, d);\r
+               term->disptext[i]->chars[j].chr = tchar;\r
+               term->disptext[i]->chars[j].attr = tattr;\r
+               if (start == j)\r
+                   term->disptext[i]->chars[j].attr |= DATTR_STARTRUN;\r
+           }\r
+\r
+           /* If it's a wide char step along to the next one. */\r
+           if (tattr & ATTR_WIDE) {\r
+               if (++j < term->cols) {\r
+                   d++;\r
+                   /*\r
+                    * By construction above, the cursor should not\r
+                    * be on the right-hand half of this character.\r
+                    * Ever.\r
+                    */\r
+                   assert(!(i == our_curs_y && j == our_curs_x));\r
+                   if (!termchars_equal(&term->disptext[i]->chars[j], d))\r
+                       dirty_run = TRUE;\r
+                   copy_termchar(term->disptext[i], j, d);\r
+               }\r
+           }\r
+       }\r
+       if (dirty_run && ccount > 0) {\r
+           do_text(ctx, start, i, ch, ccount, attr,\r
+                   ldata->lattr);\r
+           if (attr & (TATTR_ACTCURS | TATTR_PASCURS))\r
+               do_cursor(ctx, start, i, ch, ccount, attr,\r
+                         ldata->lattr);\r
+       }\r
+\r
+       unlineptr(ldata);\r
+    }\r
+\r
+    sfree(newline);\r
+    sfree(ch);\r
+}\r
+\r
+/*\r
+ * Invalidate the whole screen so it will be repainted in full.\r
+ */\r
+void term_invalidate(Terminal *term)\r
+{\r
+    int i, j;\r
+\r
+    for (i = 0; i < term->rows; i++)\r
+       for (j = 0; j < term->cols; j++)\r
+           term->disptext[i]->chars[j].attr |= ATTR_INVALID;\r
+\r
+    term_schedule_update(term);\r
+}\r
+\r
+/*\r
+ * Paint the window in response to a WM_PAINT message.\r
+ */\r
+void term_paint(Terminal *term, Context ctx,\r
+               int left, int top, int right, int bottom, int immediately)\r
+{\r
+    int i, j;\r
+    if (left < 0) left = 0;\r
+    if (top < 0) top = 0;\r
+    if (right >= term->cols) right = term->cols-1;\r
+    if (bottom >= term->rows) bottom = term->rows-1;\r
+\r
+    for (i = top; i <= bottom && i < term->rows; i++) {\r
+       if ((term->disptext[i]->lattr & LATTR_MODE) == LATTR_NORM)\r
+           for (j = left; j <= right && j < term->cols; j++)\r
+               term->disptext[i]->chars[j].attr |= ATTR_INVALID;\r
+       else\r
+           for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++)\r
+               term->disptext[i]->chars[j].attr |= ATTR_INVALID;\r
+    }\r
+\r
+    if (immediately) {\r
+        do_paint (term, ctx, FALSE);\r
+    } else {\r
+       term_schedule_update(term);\r
+    }\r
+}\r
+\r
+/*\r
+ * Attempt to scroll the scrollback. The second parameter gives the\r
+ * position we want to scroll to; the first is +1 to denote that\r
+ * this position is relative to the beginning of the scrollback, -1\r
+ * to denote it is relative to the end, and 0 to denote that it is\r
+ * relative to the current position.\r
+ */\r
+void term_scroll(Terminal *term, int rel, int where)\r
+{\r
+    int sbtop = -sblines(term);\r
+#ifdef OPTIMISE_SCROLL\r
+    int olddisptop = term->disptop;\r
+    int shift;\r
+#endif /* OPTIMISE_SCROLL */\r
+\r
+    term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where;\r
+    if (term->disptop < sbtop)\r
+       term->disptop = sbtop;\r
+    if (term->disptop > 0)\r
+       term->disptop = 0;\r
+    update_sbar(term);\r
+#ifdef OPTIMISE_SCROLL\r
+    shift = (term->disptop - olddisptop);\r
+    if (shift < term->rows && shift > -term->rows)\r
+       scroll_display(term, 0, term->rows - 1, shift);\r
+#endif /* OPTIMISE_SCROLL */\r
+    term_update(term);\r
+}\r
+\r
+/*\r
+ * Scroll the scrollback to centre it on the beginning or end of the\r
+ * current selection, if any.\r
+ */\r
+void term_scroll_to_selection(Terminal *term, int which_end)\r
+{\r
+    pos target;\r
+    int y;\r
+    int sbtop = -sblines(term);\r
+\r
+    if (term->selstate != SELECTED)\r
+       return;\r
+    if (which_end)\r
+       target = term->selend;\r
+    else\r
+       target = term->selstart;\r
+\r
+    y = target.y - term->rows/2;\r
+    if (y < sbtop)\r
+       y = sbtop;\r
+    else if (y > 0)\r
+       y = 0;\r
+    term_scroll(term, -1, y);\r
+}\r
+\r
+/*\r
+ * Helper routine for clipme(): growing buffer.\r
+ */\r
+typedef struct {\r
+    int buflen;                    /* amount of allocated space in textbuf/attrbuf */\r
+    int bufpos;                    /* amount of actual data */\r
+    wchar_t *textbuf;      /* buffer for copied text */\r
+    wchar_t *textptr;      /* = textbuf + bufpos (current insertion point) */\r
+    int *attrbuf;          /* buffer for copied attributes */\r
+    int *attrptr;          /* = attrbuf + bufpos */\r
+} clip_workbuf;\r
+\r
+static void clip_addchar(clip_workbuf *b, wchar_t chr, int attr)\r
+{\r
+    if (b->bufpos >= b->buflen) {\r
+       b->buflen += 128;\r
+       b->textbuf = sresize(b->textbuf, b->buflen, wchar_t);\r
+       b->textptr = b->textbuf + b->bufpos;\r
+       b->attrbuf = sresize(b->attrbuf, b->buflen, int);\r
+       b->attrptr = b->attrbuf + b->bufpos;\r
+    }\r
+    *b->textptr++ = chr;\r
+    *b->attrptr++ = attr;\r
+    b->bufpos++;\r
+}\r
+\r
+static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)\r
+{\r
+    clip_workbuf buf;\r
+    int old_top_x;\r
+    int attr;\r
+\r
+    buf.buflen = 5120;                 \r
+    buf.bufpos = 0;\r
+    buf.textptr = buf.textbuf = snewn(buf.buflen, wchar_t);\r
+    buf.attrptr = buf.attrbuf = snewn(buf.buflen, int);\r
+\r
+    old_top_x = top.x;                /* needed for rect==1 */\r
+\r
+    while (poslt(top, bottom)) {\r
+       int nl = FALSE;\r
+       termline *ldata = lineptr(top.y);\r
+       pos nlpos;\r
+\r
+       /*\r
+        * nlpos will point at the maximum position on this line we\r
+        * should copy up to. So we start it at the end of the\r
+        * line...\r
+        */\r
+       nlpos.y = top.y;\r
+       nlpos.x = term->cols;\r
+\r
+       /*\r
+        * ... move it backwards if there's unused space at the end\r
+        * of the line (and also set `nl' if this is the case,\r
+        * because in normal selection mode this means we need a\r
+        * newline at the end)...\r
+        */\r
+       if (!(ldata->lattr & LATTR_WRAPPED)) {\r
+           while (nlpos.x &&\r
+                  IS_SPACE_CHR(ldata->chars[nlpos.x - 1].chr) &&\r
+                  !ldata->chars[nlpos.x - 1].cc_next &&\r
+                  poslt(top, nlpos))\r
+               decpos(nlpos);\r
+           if (poslt(nlpos, bottom))\r
+               nl = TRUE;\r
+       } else if (ldata->lattr & LATTR_WRAPPED2) {\r
+           /* Ignore the last char on the line in a WRAPPED2 line. */\r
+           decpos(nlpos);\r
+       }\r
+\r
+       /*\r
+        * ... and then clip it to the terminal x coordinate if\r
+        * we're doing rectangular selection. (In this case we\r
+        * still did the above, so that copying e.g. the right-hand\r
+        * column from a table doesn't fill with spaces on the\r
+        * right.)\r
+        */\r
+       if (rect) {\r
+           if (nlpos.x > bottom.x)\r
+               nlpos.x = bottom.x;\r
+           nl = (top.y < bottom.y);\r
+       }\r
+\r
+       while (poslt(top, bottom) && poslt(top, nlpos)) {\r
+#if 0\r
+           char cbuf[16], *p;\r
+           sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));\r
+#else\r
+           wchar_t cbuf[16], *p;\r
+           int c;\r
+           int x = top.x;\r
+\r
+           if (ldata->chars[x].chr == UCSWIDE) {\r
+               top.x++;\r
+               continue;\r
+           }\r
+\r
+           while (1) {\r
+               int uc = ldata->chars[x].chr;\r
+                attr = ldata->chars[x].attr;\r
+\r
+               switch (uc & CSET_MASK) {\r
+                 case CSET_LINEDRW:\r
+                   if (!term->cfg.rawcnp) {\r
+                       uc = term->ucsdata->unitab_xterm[uc & 0xFF];\r
+                       break;\r
+                   }\r
+                 case CSET_ASCII:\r
+                   uc = term->ucsdata->unitab_line[uc & 0xFF];\r
+                   break;\r
+                 case CSET_SCOACS:\r
+                   uc = term->ucsdata->unitab_scoacs[uc&0xFF];\r
+                   break;\r
+               }\r
+               switch (uc & CSET_MASK) {\r
+                 case CSET_ACP:\r
+                   uc = term->ucsdata->unitab_font[uc & 0xFF];\r
+                   break;\r
+                 case CSET_OEMCP:\r
+                   uc = term->ucsdata->unitab_oemcp[uc & 0xFF];\r
+                   break;\r
+               }\r
+\r
+               c = (uc & ~CSET_MASK);\r
+#ifdef PLATFORM_IS_UTF16\r
+               if (uc > 0x10000 && uc < 0x110000) {\r
+                   cbuf[0] = 0xD800 | ((uc - 0x10000) >> 10);\r
+                   cbuf[1] = 0xDC00 | ((uc - 0x10000) & 0x3FF);\r
+                   cbuf[2] = 0;\r
+               } else\r
+#endif\r
+               {\r
+                   cbuf[0] = uc;\r
+                   cbuf[1] = 0;\r
+               }\r
+\r
+               if (DIRECT_FONT(uc)) {\r
+                   if (c >= ' ' && c != 0x7F) {\r
+                       char buf[4];\r
+                       WCHAR wbuf[4];\r
+                       int rv;\r
+                       if (is_dbcs_leadbyte(term->ucsdata->font_codepage, (BYTE) c)) {\r
+                           buf[0] = c;\r
+                           buf[1] = (char) (0xFF & ldata->chars[top.x + 1].chr);\r
+                           rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 2, wbuf, 4);\r
+                           top.x++;\r
+                       } else {\r
+                           buf[0] = c;\r
+                           rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 1, wbuf, 4);\r
+                       }\r
+\r
+                       if (rv > 0) {\r
+                           memcpy(cbuf, wbuf, rv * sizeof(wchar_t));\r
+                           cbuf[rv] = 0;\r
+                       }\r
+                   }\r
+               }\r
+#endif\r
+\r
+               for (p = cbuf; *p; p++)\r
+                   clip_addchar(&buf, *p, attr);\r
+\r
+               if (ldata->chars[x].cc_next)\r
+                   x += ldata->chars[x].cc_next;\r
+               else\r
+                   break;\r
+           }\r
+           top.x++;\r
+       }\r
+       if (nl) {\r
+           int i;\r
+           for (i = 0; i < sel_nl_sz; i++)\r
+               clip_addchar(&buf, sel_nl[i], 0);\r
+       }\r
+       top.y++;\r
+       top.x = rect ? old_top_x : 0;\r
+\r
+       unlineptr(ldata);\r
+    }\r
+#if SELECTION_NUL_TERMINATED\r
+    clip_addchar(&buf, 0, 0);\r
+#endif\r
+    /* Finally, transfer all that to the clipboard. */\r
+    write_clip(term->frontend, buf.textbuf, buf.attrbuf, buf.bufpos, desel);\r
+    sfree(buf.textbuf);\r
+    sfree(buf.attrbuf);\r
+}\r
+\r
+void term_copyall(Terminal *term)\r
+{\r
+    pos top;\r
+    pos bottom;\r
+    tree234 *screen = term->screen;\r
+    top.y = -sblines(term);\r
+    top.x = 0;\r
+    bottom.y = find_last_nonempty_line(term, screen);\r
+    bottom.x = term->cols;\r
+    clipme(term, top, bottom, 0, TRUE);\r
+}\r
+\r
+/*\r
+ * The wordness array is mainly for deciding the disposition of the\r
+ * US-ASCII characters.\r
+ */\r
+static int wordtype(Terminal *term, int uc)\r
+{\r
+    struct ucsword {\r
+       int start, end, ctype;\r
+    };\r
+    static const struct ucsword ucs_words[] = {\r
+       {\r
+       128, 160, 0}, {\r
+       161, 191, 1}, {\r
+       215, 215, 1}, {\r
+       247, 247, 1}, {\r
+       0x037e, 0x037e, 1},            /* Greek question mark */\r
+       {\r
+       0x0387, 0x0387, 1},            /* Greek ano teleia */\r
+       {\r
+       0x055a, 0x055f, 1},            /* Armenian punctuation */\r
+       {\r
+       0x0589, 0x0589, 1},            /* Armenian full stop */\r
+       {\r
+       0x0700, 0x070d, 1},            /* Syriac punctuation */\r
+       {\r
+       0x104a, 0x104f, 1},            /* Myanmar punctuation */\r
+       {\r
+       0x10fb, 0x10fb, 1},            /* Georgian punctuation */\r
+       {\r
+       0x1361, 0x1368, 1},            /* Ethiopic punctuation */\r
+       {\r
+       0x166d, 0x166e, 1},            /* Canadian Syl. punctuation */\r
+       {\r
+       0x17d4, 0x17dc, 1},            /* Khmer punctuation */\r
+       {\r
+       0x1800, 0x180a, 1},            /* Mongolian punctuation */\r
+       {\r
+       0x2000, 0x200a, 0},            /* Various spaces */\r
+       {\r
+       0x2070, 0x207f, 2},            /* superscript */\r
+       {\r
+       0x2080, 0x208f, 2},            /* subscript */\r
+       {\r
+       0x200b, 0x27ff, 1},            /* punctuation and symbols */\r
+       {\r
+       0x3000, 0x3000, 0},            /* ideographic space */\r
+       {\r
+       0x3001, 0x3020, 1},            /* ideographic punctuation */\r
+       {\r
+       0x303f, 0x309f, 3},            /* Hiragana */\r
+       {\r
+       0x30a0, 0x30ff, 3},            /* Katakana */\r
+       {\r
+       0x3300, 0x9fff, 3},            /* CJK Ideographs */\r
+       {\r
+       0xac00, 0xd7a3, 3},            /* Hangul Syllables */\r
+       {\r
+       0xf900, 0xfaff, 3},            /* CJK Ideographs */\r
+       {\r
+       0xfe30, 0xfe6b, 1},            /* punctuation forms */\r
+       {\r
+       0xff00, 0xff0f, 1},            /* half/fullwidth ASCII */\r
+       {\r
+       0xff1a, 0xff20, 1},            /* half/fullwidth ASCII */\r
+       {\r
+       0xff3b, 0xff40, 1},            /* half/fullwidth ASCII */\r
+       {\r
+       0xff5b, 0xff64, 1},            /* half/fullwidth ASCII */\r
+       {\r
+       0xfff0, 0xffff, 0},            /* half/fullwidth ASCII */\r
+       {\r
+       0, 0, 0}\r
+    };\r
+    const struct ucsword *wptr;\r
+\r
+    switch (uc & CSET_MASK) {\r
+      case CSET_LINEDRW:\r
+       uc = term->ucsdata->unitab_xterm[uc & 0xFF];\r
+       break;\r
+      case CSET_ASCII:\r
+       uc = term->ucsdata->unitab_line[uc & 0xFF];\r
+       break;\r
+      case CSET_SCOACS:  \r
+       uc = term->ucsdata->unitab_scoacs[uc&0xFF]; \r
+       break;\r
+    }\r
+    switch (uc & CSET_MASK) {\r
+      case CSET_ACP:\r
+       uc = term->ucsdata->unitab_font[uc & 0xFF];\r
+       break;\r
+      case CSET_OEMCP:\r
+       uc = term->ucsdata->unitab_oemcp[uc & 0xFF];\r
+       break;\r
+    }\r
+\r
+    /* For DBCS fonts I can't do anything useful. Even this will sometimes\r
+     * fail as there's such a thing as a double width space. :-(\r
+     */\r
+    if (term->ucsdata->dbcs_screenfont &&\r
+       term->ucsdata->font_codepage == term->ucsdata->line_codepage)\r
+       return (uc != ' ');\r
+\r
+    if (uc < 0x80)\r
+       return term->wordness[uc];\r
+\r
+    for (wptr = ucs_words; wptr->start; wptr++) {\r
+       if (uc >= wptr->start && uc <= wptr->end)\r
+           return wptr->ctype;\r
+    }\r
+\r
+    return 2;\r
+}\r
+\r
+/*\r
+ * Spread the selection outwards according to the selection mode.\r
+ */\r
+static pos sel_spread_half(Terminal *term, pos p, int dir)\r
+{\r
+    termline *ldata;\r
+    short wvalue;\r
+    int topy = -sblines(term);\r
+\r
+    ldata = lineptr(p.y);\r
+\r
+    switch (term->selmode) {\r
+      case SM_CHAR:\r
+       /*\r
+        * In this mode, every character is a separate unit, except\r
+        * for runs of spaces at the end of a non-wrapping line.\r
+        */\r
+       if (!(ldata->lattr & LATTR_WRAPPED)) {\r
+           termchar *q = ldata->chars + term->cols;\r
+           while (q > ldata->chars &&\r
+                  IS_SPACE_CHR(q[-1].chr) && !q[-1].cc_next)\r
+               q--;\r
+           if (q == ldata->chars + term->cols)\r
+               q--;\r
+           if (p.x >= q - ldata->chars)\r
+               p.x = (dir == -1 ? q - ldata->chars : term->cols - 1);\r
+       }\r
+       break;\r
+      case SM_WORD:\r
+       /*\r
+        * In this mode, the units are maximal runs of characters\r
+        * whose `wordness' has the same value.\r
+        */\r
+       wvalue = wordtype(term, UCSGET(ldata->chars, p.x));\r
+       if (dir == +1) {\r
+           while (1) {\r
+               int maxcols = (ldata->lattr & LATTR_WRAPPED2 ?\r
+                              term->cols-1 : term->cols);\r
+               if (p.x < maxcols-1) {\r
+                   if (wordtype(term, UCSGET(ldata->chars, p.x+1)) == wvalue)\r
+                       p.x++;\r
+                   else\r
+                       break;\r
+               } else {\r
+                   if (ldata->lattr & LATTR_WRAPPED) {\r
+                       termline *ldata2;\r
+                       ldata2 = lineptr(p.y+1);\r
+                       if (wordtype(term, UCSGET(ldata2->chars, 0))\r
+                           == wvalue) {\r
+                           p.x = 0;\r
+                           p.y++;\r
+                           unlineptr(ldata);\r
+                           ldata = ldata2;\r
+                       } else {\r
+                           unlineptr(ldata2);\r
+                           break;\r
+                       }\r
+                   } else\r
+                       break;\r
+               }\r
+           }\r
+       } else {\r
+           while (1) {\r
+               if (p.x > 0) {\r
+                   if (wordtype(term, UCSGET(ldata->chars, p.x-1)) == wvalue)\r
+                       p.x--;\r
+                   else\r
+                       break;\r
+               } else {\r
+                   termline *ldata2;\r
+                   int maxcols;\r
+                   if (p.y <= topy)\r
+                       break;\r
+                   ldata2 = lineptr(p.y-1);\r
+                   maxcols = (ldata2->lattr & LATTR_WRAPPED2 ?\r
+                             term->cols-1 : term->cols);\r
+                   if (ldata2->lattr & LATTR_WRAPPED) {\r
+                       if (wordtype(term, UCSGET(ldata2->chars, maxcols-1))\r
+                           == wvalue) {\r
+                           p.x = maxcols-1;\r
+                           p.y--;\r
+                           unlineptr(ldata);\r
+                           ldata = ldata2;\r
+                       } else {\r
+                           unlineptr(ldata2);\r
+                           break;\r
+                       }\r
+                   } else\r
+                       break;\r
+               }\r
+           }\r
+       }\r
+       break;\r
+      case SM_LINE:\r
+       /*\r
+        * In this mode, every line is a unit.\r
+        */\r
+       p.x = (dir == -1 ? 0 : term->cols - 1);\r
+       break;\r
+    }\r
+\r
+    unlineptr(ldata);\r
+    return p;\r
+}\r
+\r
+static void sel_spread(Terminal *term)\r
+{\r
+    if (term->seltype == LEXICOGRAPHIC) {\r
+       term->selstart = sel_spread_half(term, term->selstart, -1);\r
+       decpos(term->selend);\r
+       term->selend = sel_spread_half(term, term->selend, +1);\r
+       incpos(term->selend);\r
+    }\r
+}\r
+\r
+void term_do_paste(Terminal *term)\r
+{\r
+    wchar_t *data;\r
+    int len;\r
+\r
+    get_clip(term->frontend, &data, &len);\r
+    if (data && len > 0) {\r
+        wchar_t *p, *q;\r
+\r
+       term_seen_key_event(term);     /* pasted data counts */\r
+\r
+        if (term->paste_buffer)\r
+            sfree(term->paste_buffer);\r
+        term->paste_pos = term->paste_hold = term->paste_len = 0;\r
+        term->paste_buffer = snewn(len, wchar_t);\r
+\r
+        p = q = data;\r
+        while (p < data + len) {\r
+            while (p < data + len &&\r
+                   !(p <= data + len - sel_nl_sz &&\r
+                     !memcmp(p, sel_nl, sizeof(sel_nl))))\r
+                p++;\r
+\r
+            {\r
+                int i;\r
+                for (i = 0; i < p - q; i++) {\r
+                    term->paste_buffer[term->paste_len++] = q[i];\r
+                }\r
+            }\r
+\r
+            if (p <= data + len - sel_nl_sz &&\r
+                !memcmp(p, sel_nl, sizeof(sel_nl))) {\r
+                term->paste_buffer[term->paste_len++] = '\015';\r
+                p += sel_nl_sz;\r
+            }\r
+            q = p;\r
+        }\r
+\r
+        /* Assume a small paste will be OK in one go. */\r
+        if (term->paste_len < 256) {\r
+            if (term->ldisc)\r
+               luni_send(term->ldisc, term->paste_buffer, term->paste_len, 0);\r
+            if (term->paste_buffer)\r
+                sfree(term->paste_buffer);\r
+            term->paste_buffer = 0;\r
+            term->paste_pos = term->paste_hold = term->paste_len = 0;\r
+        }\r
+    }\r
+    get_clip(term->frontend, NULL, NULL);\r
+}\r
+\r
+void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,\r
+               Mouse_Action a, int x, int y, int shift, int ctrl, int alt)\r
+{\r
+    pos selpoint;\r
+    termline *ldata;\r
+    int raw_mouse = (term->xterm_mouse &&\r
+                    !term->cfg.no_mouse_rep &&\r
+                    !(term->cfg.mouse_override && shift));\r
+    int default_seltype;\r
+\r
+    if (y < 0) {\r
+       y = 0;\r
+       if (a == MA_DRAG && !raw_mouse)\r
+           term_scroll(term, 0, -1);\r
+    }\r
+    if (y >= term->rows) {\r
+       y = term->rows - 1;\r
+       if (a == MA_DRAG && !raw_mouse)\r
+           term_scroll(term, 0, +1);\r
+    }\r
+    if (x < 0) {\r
+       if (y > 0) {\r
+           x = term->cols - 1;\r
+           y--;\r
+       } else\r
+           x = 0;\r
+    }\r
+    if (x >= term->cols)\r
+       x = term->cols - 1;\r
+\r
+    selpoint.y = y + term->disptop;\r
+    ldata = lineptr(selpoint.y);\r
+\r
+    if ((ldata->lattr & LATTR_MODE) != LATTR_NORM)\r
+       x /= 2;\r
+\r
+    /*\r
+     * Transform x through the bidi algorithm to find the _logical_\r
+     * click point from the physical one.\r
+     */\r
+    if (term_bidi_line(term, ldata, y) != NULL) {\r
+       x = term->post_bidi_cache[y].backward[x];\r
+    }\r
+\r
+    selpoint.x = x;\r
+    unlineptr(ldata);\r
+\r
+    /*\r
+     * If we're in the middle of a selection operation, we ignore raw\r
+     * mouse mode until it's done (we must have been not in raw mouse\r
+     * mode when it started).\r
+     * This makes use of Shift for selection reliable, and avoids the\r
+     * host seeing mouse releases for which they never saw corresponding\r
+     * presses.\r
+     */\r
+    if (raw_mouse &&\r
+       (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) {\r
+       int encstate = 0, r, c;\r
+       char abuf[16];\r
+\r
+       if (term->ldisc) {\r
+\r
+           switch (braw) {\r
+             case MBT_LEFT:\r
+               encstate = 0x20;               /* left button down */\r
+               break;\r
+             case MBT_MIDDLE:\r
+               encstate = 0x21;\r
+               break;\r
+             case MBT_RIGHT:\r
+               encstate = 0x22;\r
+               break;\r
+             case MBT_WHEEL_UP:\r
+               encstate = 0x60;\r
+               break;\r
+             case MBT_WHEEL_DOWN:\r
+               encstate = 0x61;\r
+               break;\r
+             default: break;          /* placate gcc warning about enum use */\r
+           }\r
+           switch (a) {\r
+             case MA_DRAG:\r
+               if (term->xterm_mouse == 1)\r
+                   return;\r
+               encstate += 0x20;\r
+               break;\r
+             case MA_RELEASE:\r
+               encstate = 0x23;\r
+               term->mouse_is_down = 0;\r
+               break;\r
+             case MA_CLICK:\r
+               if (term->mouse_is_down == braw)\r
+                   return;\r
+               term->mouse_is_down = braw;\r
+               break;\r
+             default: break;          /* placate gcc warning about enum use */\r
+           }\r
+           if (shift)\r
+               encstate += 0x04;\r
+           if (ctrl)\r
+               encstate += 0x10;\r
+           r = y + 33;\r
+           c = x + 33;\r
+\r
+           sprintf(abuf, "\033[M%c%c%c", encstate, c, r);\r
+           ldisc_send(term->ldisc, abuf, 6, 0);\r
+       }\r
+       return;\r
+    }\r
+\r
+    /*\r
+     * Set the selection type (rectangular or normal) at the start\r
+     * of a selection attempt, from the state of Alt.\r
+     */\r
+    if (!alt ^ !term->cfg.rect_select)\r
+       default_seltype = RECTANGULAR;\r
+    else\r
+       default_seltype = LEXICOGRAPHIC;\r
+       \r
+    if (term->selstate == NO_SELECTION) {\r
+       term->seltype = default_seltype;\r
+    }\r
+\r
+    if (bcooked == MBT_SELECT && a == MA_CLICK) {\r
+       deselect(term);\r
+       term->selstate = ABOUT_TO;\r
+       term->seltype = default_seltype;\r
+       term->selanchor = selpoint;\r
+       term->selmode = SM_CHAR;\r
+    } else if (bcooked == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {\r
+       deselect(term);\r
+       term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);\r
+       term->selstate = DRAGGING;\r
+       term->selstart = term->selanchor = selpoint;\r
+       term->selend = term->selstart;\r
+       incpos(term->selend);\r
+       sel_spread(term);\r
+    } else if ((bcooked == MBT_SELECT && a == MA_DRAG) ||\r
+              (bcooked == MBT_EXTEND && a != MA_RELEASE)) {\r
+       if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))\r
+           return;\r
+       if (bcooked == MBT_EXTEND && a != MA_DRAG &&\r
+           term->selstate == SELECTED) {\r
+           if (term->seltype == LEXICOGRAPHIC) {\r
+               /*\r
+                * For normal selection, we extend by moving\r
+                * whichever end of the current selection is closer\r
+                * to the mouse.\r
+                */\r
+               if (posdiff(selpoint, term->selstart) <\r
+                   posdiff(term->selend, term->selstart) / 2) {\r
+                   term->selanchor = term->selend;\r
+                   decpos(term->selanchor);\r
+               } else {\r
+                   term->selanchor = term->selstart;\r
+               }\r
+           } else {\r
+               /*\r
+                * For rectangular selection, we have a choice of\r
+                * _four_ places to put selanchor and selpoint: the\r
+                * four corners of the selection.\r
+                */\r
+               if (2*selpoint.x < term->selstart.x + term->selend.x)\r
+                   term->selanchor.x = term->selend.x-1;\r
+               else\r
+                   term->selanchor.x = term->selstart.x;\r
+\r
+               if (2*selpoint.y < term->selstart.y + term->selend.y)\r
+                   term->selanchor.y = term->selend.y;\r
+               else\r
+                   term->selanchor.y = term->selstart.y;\r
+           }\r
+           term->selstate = DRAGGING;\r
+       }\r
+       if (term->selstate != ABOUT_TO && term->selstate != DRAGGING)\r
+           term->selanchor = selpoint;\r
+       term->selstate = DRAGGING;\r
+       if (term->seltype == LEXICOGRAPHIC) {\r
+           /*\r
+            * For normal selection, we set (selstart,selend) to\r
+            * (selpoint,selanchor) in some order.\r
+            */\r
+           if (poslt(selpoint, term->selanchor)) {\r
+               term->selstart = selpoint;\r
+               term->selend = term->selanchor;\r
+               incpos(term->selend);\r
+           } else {\r
+               term->selstart = term->selanchor;\r
+               term->selend = selpoint;\r
+               incpos(term->selend);\r
+           }\r
+       } else {\r
+           /*\r
+            * For rectangular selection, we may need to\r
+            * interchange x and y coordinates (if the user has\r
+            * dragged in the -x and +y directions, or vice versa).\r
+            */\r
+           term->selstart.x = min(term->selanchor.x, selpoint.x);\r
+           term->selend.x = 1+max(term->selanchor.x, selpoint.x);\r
+           term->selstart.y = min(term->selanchor.y, selpoint.y);\r
+           term->selend.y =   max(term->selanchor.y, selpoint.y);\r
+       }\r
+       sel_spread(term);\r
+    } else if ((bcooked == MBT_SELECT || bcooked == MBT_EXTEND) &&\r
+              a == MA_RELEASE) {\r
+       if (term->selstate == DRAGGING) {\r
+           /*\r
+            * We've completed a selection. We now transfer the\r
+            * data to the clipboard.\r
+            */\r
+           clipme(term, term->selstart, term->selend,\r
+                  (term->seltype == RECTANGULAR), FALSE);\r
+           term->selstate = SELECTED;\r
+       } else\r
+           term->selstate = NO_SELECTION;\r
+    } else if (bcooked == MBT_PASTE\r
+              && (a == MA_CLICK\r
+#if MULTICLICK_ONLY_EVENT\r
+                  || a == MA_2CLK || a == MA_3CLK\r
+#endif\r
+                  )) {\r
+       request_paste(term->frontend);\r
+    }\r
+\r
+    term_update(term);\r
+}\r
+\r
+int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl)\r
+{\r
+    char *p = buf;\r
+\r
+    if (term->vt52_mode)\r
+       p += sprintf((char *) p, "\x1B%c", xkey);\r
+    else {\r
+       int app_flg = (term->app_cursor_keys && !term->cfg.no_applic_c);\r
+#if 0\r
+       /*\r
+        * RDB: VT100 & VT102 manuals both state the app cursor\r
+        * keys only work if the app keypad is on.\r
+        *\r
+        * SGT: That may well be true, but xterm disagrees and so\r
+        * does at least one application, so I've #if'ed this out\r
+        * and the behaviour is back to PuTTY's original: app\r
+        * cursor and app keypad are independently switchable\r
+        * modes. If anyone complains about _this_ I'll have to\r
+        * put in a configurable option.\r
+        */\r
+       if (!term->app_keypad_keys)\r
+           app_flg = 0;\r
+#endif\r
+       /* Useful mapping of Ctrl-arrows */\r
+       if (ctrl)\r
+           app_flg = !app_flg;\r
+\r
+       if (app_flg)\r
+           p += sprintf((char *) p, "\x1BO%c", xkey);\r
+       else\r
+           p += sprintf((char *) p, "\x1B[%c", xkey);\r
+    }\r
+\r
+    return p - buf;\r
+}\r
+\r
+void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,\r
+             unsigned int modifiers, unsigned int flags)\r
+{\r
+    char output[10];\r
+    char *p = output;\r
+    int prependesc = FALSE;\r
+#if 0\r
+    int i;\r
+\r
+    fprintf(stderr, "keysym = %d, %d chars:", keysym, tlen);\r
+    for (i = 0; i < tlen; i++)\r
+       fprintf(stderr, " %04x", (unsigned)text[i]);\r
+    fprintf(stderr, "\n");\r
+#endif\r
+\r
+    /* XXX Num Lock */\r
+    if ((flags & PKF_REPEAT) && term->repeat_off)\r
+       return;\r
+\r
+    /* Currently, Meta always just prefixes everything with ESC. */\r
+    if (modifiers & PKM_META)\r
+       prependesc = TRUE;\r
+    modifiers &= ~PKM_META;\r
+\r
+    /*\r
+     * Alt is only used for Alt+keypad, which isn't supported yet, so\r
+     * ignore it.\r
+     */\r
+    modifiers &= ~PKM_ALT;\r
+\r
+    /* Standard local function keys */\r
+    switch (modifiers & (PKM_SHIFT | PKM_CONTROL)) {\r
+      case PKM_SHIFT:\r
+       if (keysym == PK_PAGEUP)\r
+           /* scroll up one page */;\r
+       if (keysym == PK_PAGEDOWN)\r
+           /* scroll down on page */;\r
+       if (keysym == PK_INSERT)\r
+           term_do_paste(term);\r
+       break;\r
+      case PKM_CONTROL:\r
+       if (keysym == PK_PAGEUP)\r
+           /* scroll up one line */;\r
+       if (keysym == PK_PAGEDOWN)\r
+           /* scroll down one line */;\r
+       /* Control-Numlock for app-keypad mode switch */\r
+       if (keysym == PK_PF1)\r
+           term->app_keypad_keys ^= 1;\r
+       break;\r
+    }\r
+\r
+    if (modifiers & PKM_ALT) {\r
+       /* Alt+F4 (close) */\r
+       /* Alt+Return (full screen) */\r
+       /* Alt+Space (system menu) */\r
+    }\r
+\r
+    if (keysym == PK_NULL && (modifiers & PKM_CONTROL) && tlen == 1 &&\r
+       text[0] >= 0x20 && text[0] <= 0x7e) {\r
+       /* ASCII chars + Control */\r
+       if ((text[0] >= 0x40 && text[0] <= 0x5f) ||\r
+           (text[0] >= 0x61 && text[0] <= 0x7a))\r
+           text[0] &= 0x1f;\r
+       else {\r
+           /*\r
+            * Control-2 should return ^@ (0x00), Control-6 should return\r
+            * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since\r
+            * the DOS keyboard handling did it, and we have nothing better\r
+            * to do with the key combo in question, we'll also map\r
+            * Control-Backquote to ^\ (0x1C).\r
+            */\r
+           switch (text[0]) {\r
+             case ' ': text[0] = 0x00; break;\r
+             case '-': text[0] = 0x1f; break;\r
+             case '/': text[0] = 0x1f; break;\r
+             case '2': text[0] = 0x00; break;\r
+             case '3': text[0] = 0x1b; break;\r
+             case '4': text[0] = 0x1c; break;\r
+             case '5': text[0] = 0x1d; break;\r
+             case '6': text[0] = 0x1e; break;\r
+             case '7': text[0] = 0x1f; break;\r
+             case '8': text[0] = 0x7f; break;\r
+             case '`': text[0] = 0x1c; break;\r
+           }\r
+       }\r
+    }\r
+\r
+    /* Nethack keypad */\r
+    if (term->cfg.nethack_keypad) {\r
+       char c = 0;\r
+       switch (keysym) {\r
+         case PK_KP1: c = 'b'; break;\r
+         case PK_KP2: c = 'j'; break;\r
+         case PK_KP3: c = 'n'; break;\r
+         case PK_KP4: c = 'h'; break;\r
+         case PK_KP5: c = '.'; break;\r
+         case PK_KP6: c = 'l'; break;\r
+         case PK_KP7: c = 'y'; break;\r
+         case PK_KP8: c = 'k'; break;\r
+         case PK_KP9: c = 'u'; break;\r
+         default: break; /* else gcc warns `enum value not used' */\r
+       }\r
+       if (c != 0) {\r
+           if (c != '.') {\r
+               if (modifiers & PKM_CONTROL)\r
+                   c &= 0x1f;\r
+               else if (modifiers & PKM_SHIFT)\r
+                       c = toupper((unsigned char)c);\r
+           }\r
+           *p++ = c;\r
+           goto done;\r
+       }\r
+    }\r
+\r
+    /* Numeric Keypad */\r
+    if (PK_ISKEYPAD(keysym)) {\r
+       int xkey = 0;\r
+\r
+       /*\r
+        * In VT400 mode, PFn always emits an escape sequence.  In\r
+        * Linux and tilde modes, this only happens in app keypad mode.\r
+        */\r
+       if (term->cfg.funky_type == FUNKY_VT400 ||\r
+           ((term->cfg.funky_type == FUNKY_LINUX ||\r
+             term->cfg.funky_type == FUNKY_TILDE) &&\r
+            term->app_keypad_keys && !term->cfg.no_applic_k)) {\r
+           switch (keysym) {\r
+             case PK_PF1: xkey = 'P'; break;\r
+             case PK_PF2: xkey = 'Q'; break;\r
+             case PK_PF3: xkey = 'R'; break;\r
+             case PK_PF4: xkey = 'S'; break;\r
+             default: break; /* else gcc warns `enum value not used' */\r
+           }\r
+       }\r
+       if (term->app_keypad_keys && !term->cfg.no_applic_k) {\r
+           switch (keysym) {\r
+             case PK_KP0: xkey = 'p'; break;\r
+             case PK_KP1: xkey = 'q'; break;\r
+             case PK_KP2: xkey = 'r'; break;\r
+             case PK_KP3: xkey = 's'; break;\r
+             case PK_KP4: xkey = 't'; break;\r
+             case PK_KP5: xkey = 'u'; break;\r
+             case PK_KP6: xkey = 'v'; break;\r
+             case PK_KP7: xkey = 'w'; break;\r
+             case PK_KP8: xkey = 'x'; break;\r
+             case PK_KP9: xkey = 'y'; break;\r
+             case PK_KPDECIMAL: xkey = 'n'; break;\r
+             case PK_KPENTER: xkey = 'M'; break;\r
+             default: break; /* else gcc warns `enum value not used' */\r
+           }\r
+           if (term->cfg.funky_type == FUNKY_XTERM && tlen > 0) {\r
+               /*\r
+                * xterm can't see the layout of the keypad, so it has\r
+                * to rely on the X keysyms returned by the keys.\r
+                * Hence, we look at the strings here, not the PuTTY\r
+                * keysyms (which describe the layout).\r
+                */\r
+               switch (text[0]) {\r
+                 case '+':\r
+                   if (modifiers & PKM_SHIFT)\r
+                       xkey = 'l';\r
+                   else\r
+                       xkey = 'k';\r
+                   break;\r
+                 case '/': xkey = 'o'; break;\r
+                 case '*': xkey = 'j'; break;\r
+                 case '-': xkey = 'm'; break;\r
+               }\r
+           } else {\r
+               /*\r
+                * In all other modes, we try to retain the layout of\r
+                * the DEC keypad in application mode.\r
+                */\r
+               switch (keysym) {\r
+                 case PK_KPBIGPLUS:\r
+                   /* This key covers the '-' and ',' keys on a VT220 */\r
+                   if (modifiers & PKM_SHIFT)\r
+                       xkey = 'm'; /* VT220 '-' */\r
+                   else\r
+                       xkey = 'l'; /* VT220 ',' */\r
+                   break;\r
+                 case PK_KPMINUS: xkey = 'm'; break;\r
+                 case PK_KPCOMMA: xkey = 'l'; break;\r
+                 default: break; /* else gcc warns `enum value not used' */\r
+               }\r
+           }\r
+       }\r
+       if (xkey) {\r
+           if (term->vt52_mode) {\r
+               if (xkey >= 'P' && xkey <= 'S')\r
+                   p += sprintf((char *) p, "\x1B%c", xkey);\r
+               else\r
+                   p += sprintf((char *) p, "\x1B?%c", xkey);\r
+           } else\r
+               p += sprintf((char *) p, "\x1BO%c", xkey);\r
+           goto done;\r
+       }\r
+       /* Not in application mode -- treat the number pad as arrow keys? */\r
+       if ((flags & PKF_NUMLOCK) == 0) {\r
+           switch (keysym) {\r
+             case PK_KP0: keysym = PK_INSERT; break;\r
+             case PK_KP1: keysym = PK_END; break;\r
+             case PK_KP2: keysym = PK_DOWN; break;\r
+             case PK_KP3: keysym = PK_PAGEDOWN; break;\r
+             case PK_KP4: keysym = PK_LEFT; break;\r
+             case PK_KP5: keysym = PK_REST; break;\r
+             case PK_KP6: keysym = PK_RIGHT; break;\r
+             case PK_KP7: keysym = PK_HOME; break;\r
+             case PK_KP8: keysym = PK_UP; break;\r
+             case PK_KP9: keysym = PK_PAGEUP; break;\r
+             default: break; /* else gcc warns `enum value not used' */\r
+           }\r
+       }\r
+    }\r
+\r
+    /* Miscellaneous keys */\r
+    switch (keysym) {\r
+      case PK_ESCAPE:\r
+       *p++ = 0x1b;\r
+       goto done;\r
+      case PK_BACKSPACE:\r
+           if (modifiers == 0)\r
+               *p++ = (term->cfg.bksp_is_delete ? 0x7F : 0x08);\r
+           else if (modifiers == PKM_SHIFT)\r
+               /* We do the opposite of what is configured */\r
+               *p++ = (term->cfg.bksp_is_delete ? 0x08 : 0x7F);\r
+           else break;\r
+           goto done;\r
+      case PK_TAB:\r
+       if (modifiers == 0)\r
+           *p++ = 0x09;\r
+       else if (modifiers == PKM_SHIFT)\r
+           *p++ = 0x1B, *p++ = '[', *p++ = 'Z';\r
+       else break;\r
+       goto done;\r
+       /* XXX window.c has ctrl+shift+space sending 0xa0 */\r
+      case PK_PAUSE:\r
+       if (modifiers == PKM_CONTROL)\r
+           *p++ = 26;\r
+       else break;\r
+       goto done;\r
+      case PK_RETURN:\r
+      case PK_KPENTER: /* Odd keypad modes handled above */\r
+       if (modifiers == 0) {\r
+           *p++ = 0x0d;\r
+           if (term->cr_lf_return)\r
+               *p++ = 0x0a;\r
+           goto done;\r
+       }\r
+      default: break; /* else gcc warns `enum value not used' */\r
+    }\r
+\r
+    /* SCO function keys and editing keys */\r
+    if (term->cfg.funky_type == FUNKY_SCO) {\r
+       if (PK_ISFKEY(keysym) && keysym <= PK_F12) {\r
+           static char const codes[] =\r
+               "MNOPQRSTUVWX" "YZabcdefghij" "klmnopqrstuv" "wxyz@[\\]^_`{";\r
+           int index = keysym - PK_F1;\r
+\r
+           if (modifiers & PKM_SHIFT) index += 12;\r
+           if (modifiers & PKM_CONTROL) index += 24;\r
+           p += sprintf((char *) p, "\x1B[%c", codes[index]);\r
+           goto done;\r
+       }\r
+       if (PK_ISEDITING(keysym)) {\r
+           int xkey = 0;\r
+\r
+           switch (keysym) {\r
+             case PK_DELETE:   *p++ = 0x7f; goto done;\r
+             case PK_HOME:     xkey = 'H'; break;\r
+             case PK_INSERT:   xkey = 'L'; break;\r
+             case PK_END:      xkey = 'F'; break;\r
+             case PK_PAGEUP:   xkey = 'I'; break;\r
+             case PK_PAGEDOWN: xkey = 'G'; break;\r
+             default: break; /* else gcc warns `enum value not used' */\r
+           }\r
+           p += sprintf((char *) p, "\x1B[%c", xkey);\r
+       }\r
+    }\r
+\r
+    if (PK_ISEDITING(keysym) && (modifiers & PKM_SHIFT) == 0) {\r
+       int code;\r
+\r
+       if (term->cfg.funky_type == FUNKY_XTERM) {\r
+           /* Xterm shuffles these keys, apparently. */\r
+           switch (keysym) {\r
+             case PK_HOME:     keysym = PK_INSERT;   break;\r
+             case PK_INSERT:   keysym = PK_HOME;     break;\r
+             case PK_DELETE:   keysym = PK_END;      break;\r
+             case PK_END:      keysym = PK_PAGEUP;   break;\r
+             case PK_PAGEUP:   keysym = PK_DELETE;   break;\r
+             case PK_PAGEDOWN: keysym = PK_PAGEDOWN; break;\r
+             default: break; /* else gcc warns `enum value not used' */\r
+           }\r
+       }\r
+\r
+       /* RXVT Home/End */\r
+       if (term->cfg.rxvt_homeend &&\r
+           (keysym == PK_HOME || keysym == PK_END)) {\r
+           p += sprintf((char *) p, keysym == PK_HOME ? "\x1B[H" : "\x1BOw");\r
+           goto done;\r
+       }\r
+\r
+       if (term->vt52_mode) {\r
+           int xkey;\r
+\r
+           /*\r
+            * A real VT52 doesn't have these, and a VT220 doesn't\r
+            * send anything for them in VT52 mode.\r
+            */\r
+           switch (keysym) {\r
+             case PK_HOME:     xkey = 'H'; break;\r
+             case PK_INSERT:   xkey = 'L'; break;\r
+             case PK_DELETE:   xkey = 'M'; break;\r
+             case PK_END:      xkey = 'E'; break;\r
+             case PK_PAGEUP:   xkey = 'I'; break;\r
+             case PK_PAGEDOWN: xkey = 'G'; break;\r
+             default: xkey=0; break; /* else gcc warns `enum value not used'*/\r
+           }\r
+           p += sprintf((char *) p, "\x1B%c", xkey);\r
+           goto done;\r
+       }\r
+\r
+       switch (keysym) {\r
+         case PK_HOME:     code = 1; break;\r
+         case PK_INSERT:   code = 2; break;\r
+         case PK_DELETE:   code = 3; break;\r
+         case PK_END:      code = 4; break;\r
+         case PK_PAGEUP:   code = 5; break;\r
+         case PK_PAGEDOWN: code = 6; break;\r
+         default: code = 0; break; /* else gcc warns `enum value not used' */\r
+       }\r
+       p += sprintf((char *) p, "\x1B[%d~", code);\r
+       goto done;\r
+    }\r
+\r
+    if (PK_ISFKEY(keysym)) {\r
+       /* Map Shift+F1-F10 to F11-F20 */\r
+       if (keysym >= PK_F1 && keysym <= PK_F10 && (modifiers & PKM_SHIFT))\r
+           keysym += 10;\r
+       if ((term->vt52_mode || term->cfg.funky_type == FUNKY_VT100P) &&\r
+           keysym <= PK_F14) {\r
+           /* XXX This overrides the XTERM/VT52 mode below */\r
+           int offt = 0;\r
+           if (keysym >= PK_F6)  offt++;\r
+           if (keysym >= PK_F12) offt++;\r
+           p += sprintf((char *) p, term->vt52_mode ? "\x1B%c" : "\x1BO%c",\r
+                        'P' + keysym - PK_F1 - offt);\r
+           goto done;\r
+       }\r
+       if (term->cfg.funky_type == FUNKY_LINUX && keysym <= PK_F5) {\r
+           p += sprintf((char *) p, "\x1B[[%c", 'A' + keysym - PK_F1);\r
+           goto done;\r
+       }\r
+       if (term->cfg.funky_type == FUNKY_XTERM && keysym <= PK_F4) {\r
+           if (term->vt52_mode)\r
+               p += sprintf((char *) p, "\x1B%c", 'P' + keysym - PK_F1);\r
+           else\r
+               p += sprintf((char *) p, "\x1BO%c", 'P' + keysym - PK_F1);\r
+           goto done;\r
+       }\r
+       p += sprintf((char *) p, "\x1B[%d~", 11 + keysym - PK_F1);\r
+       goto done;\r
+    }\r
+\r
+    if (PK_ISCURSOR(keysym)) {\r
+       int xkey;\r
+\r
+       switch (keysym) {\r
+         case PK_UP:    xkey = 'A'; break;\r
+         case PK_DOWN:  xkey = 'B'; break;\r
+         case PK_RIGHT: xkey = 'C'; break;\r
+         case PK_LEFT:  xkey = 'D'; break;\r
+         case PK_REST:  xkey = 'G'; break; /* centre key on number pad */\r
+         default: xkey = 0; break; /* else gcc warns `enum value not used' */\r
+       }\r
+       p += format_arrow_key(p, term, xkey, modifiers == PKM_CONTROL);\r
+       goto done;\r
+    }\r
+\r
+  done:\r
+    if (p > output || tlen > 0) {\r
+       /*\r
+        * Interrupt an ongoing paste. I'm not sure\r
+        * this is sensible, but for the moment it's\r
+        * preferable to having to faff about buffering\r
+        * things.\r
+        */\r
+       term_nopaste(term);\r
+\r
+       /*\r
+        * We need not bother about stdin backlogs\r
+        * here, because in GUI PuTTY we can't do\r
+        * anything about it anyway; there's no means\r
+        * of asking Windows to hold off on KEYDOWN\r
+        * messages. We _have_ to buffer everything\r
+        * we're sent.\r
+        */\r
+       term_seen_key_event(term);\r
+\r
+       if (prependesc) {\r
+#if 0\r
+           fprintf(stderr, "sending ESC\n");\r
+#endif\r
+           ldisc_send(term->ldisc, "\x1b", 1, 1);\r
+       }\r
+\r
+       if (p > output) {\r
+#if 0\r
+           fprintf(stderr, "sending %d bytes:", p - output);\r
+           for (i = 0; i < p - output; i++)\r
+               fprintf(stderr, " %02x", output[i]);\r
+           fprintf(stderr, "\n");\r
+#endif\r
+           ldisc_send(term->ldisc, output, p - output, 1);\r
+       } else if (tlen > 0) {\r
+#if 0\r
+           fprintf(stderr, "sending %d unichars:", tlen);\r
+           for (i = 0; i < tlen; i++)\r
+               fprintf(stderr, " %04x", (unsigned) text[i]);\r
+           fprintf(stderr, "\n");\r
+#endif\r
+           luni_send(term->ldisc, text, tlen, 1);\r
+       }\r
+    }\r
+}\r
+\r
+void term_nopaste(Terminal *term)\r
+{\r
+    if (term->paste_len == 0)\r
+       return;\r
+    sfree(term->paste_buffer);\r
+    term->paste_buffer = NULL;\r
+    term->paste_len = 0;\r
+}\r
+\r
+int term_paste_pending(Terminal *term)\r
+{\r
+    return term->paste_len != 0;\r
+}\r
+\r
+void term_paste(Terminal *term)\r
+{\r
+    long now, paste_diff;\r
+\r
+    if (term->paste_len == 0)\r
+       return;\r
+\r
+    /* Don't wait forever to paste */\r
+    if (term->paste_hold) {\r
+       now = GETTICKCOUNT();\r
+       paste_diff = now - term->last_paste;\r
+       if (paste_diff >= 0 && paste_diff < 450)\r
+           return;\r
+    }\r
+    term->paste_hold = 0;\r
+\r
+    while (term->paste_pos < term->paste_len) {\r
+       int n = 0;\r
+       while (n + term->paste_pos < term->paste_len) {\r
+           if (term->paste_buffer[term->paste_pos + n++] == '\015')\r
+               break;\r
+       }\r
+       if (term->ldisc)\r
+           luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0);\r
+       term->paste_pos += n;\r
+\r
+       if (term->paste_pos < term->paste_len) {\r
+           term->paste_hold = 1;\r
+           return;\r
+       }\r
+    }\r
+    sfree(term->paste_buffer);\r
+    term->paste_buffer = NULL;\r
+    term->paste_len = 0;\r
+}\r
+\r
+static void deselect(Terminal *term)\r
+{\r
+    term->selstate = NO_SELECTION;\r
+    term->selstart.x = term->selstart.y = term->selend.x = term->selend.y = 0;\r
+}\r
+\r
+void term_deselect(Terminal *term)\r
+{\r
+    deselect(term);\r
+    term_update(term);\r
+}\r
+\r
+int term_ldisc(Terminal *term, int option)\r
+{\r
+    if (option == LD_ECHO)\r
+       return term->term_echoing;\r
+    if (option == LD_EDIT)\r
+       return term->term_editing;\r
+    return FALSE;\r
+}\r
+\r
+int term_data(Terminal *term, int is_stderr, const char *data, int len)\r
+{\r
+    bufchain_add(&term->inbuf, data, len);\r
+\r
+    if (!term->in_term_out) {\r
+       term->in_term_out = TRUE;\r
+       term_reset_cblink(term);\r
+       /*\r
+        * During drag-selects, we do not process terminal input,\r
+        * because the user will want the screen to hold still to\r
+        * be selected.\r
+        */\r
+       if (term->selstate != DRAGGING)\r
+           term_out(term);\r
+       term->in_term_out = FALSE;\r
+    }\r
+\r
+    /*\r
+     * term_out() always completely empties inbuf. Therefore,\r
+     * there's no reason at all to return anything other than zero\r
+     * from this function, because there _can't_ be a question of\r
+     * the remote side needing to wait until term_out() has cleared\r
+     * a backlog.\r
+     *\r
+     * This is a slightly suboptimal way to deal with SSH-2 - in\r
+     * principle, the window mechanism would allow us to continue\r
+     * to accept data on forwarded ports and X connections even\r
+     * while the terminal processing was going slowly - but we\r
+     * can't do the 100% right thing without moving the terminal\r
+     * processing into a separate thread, and that might hurt\r
+     * portability. So we manage stdout buffering the old SSH-1 way:\r
+     * if the terminal processing goes slowly, the whole SSH\r
+     * connection stops accepting data until it's ready.\r
+     *\r
+     * In practice, I can't imagine this causing serious trouble.\r
+     */\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Write untrusted data to the terminal.\r
+ * The only control character that should be honoured is \n (which\r
+ * will behave as a CRLF).\r
+ */\r
+int term_data_untrusted(Terminal *term, const char *data, int len)\r
+{\r
+    int i;\r
+    /* FIXME: more sophisticated checking? */\r
+    for (i = 0; i < len; i++) {\r
+       if (data[i] == '\n')\r
+           term_data(term, 1, "\r\n", 2);\r
+       else if (data[i] & 0x60)\r
+           term_data(term, 1, data + i, 1);\r
+    }\r
+    return 0; /* assumes that term_data() always returns 0 */\r
+}\r
+\r
+void term_provide_logctx(Terminal *term, void *logctx)\r
+{\r
+    term->logctx = logctx;\r
+}\r
+\r
+void term_set_focus(Terminal *term, int has_focus)\r
+{\r
+    term->has_focus = has_focus;\r
+    term_schedule_cblink(term);\r
+}\r
+\r
+/*\r
+ * Provide "auto" settings for remote tty modes, suitable for an\r
+ * application with a terminal window.\r
+ */\r
+char *term_get_ttymode(Terminal *term, const char *mode)\r
+{\r
+    char *val = NULL;\r
+    if (strcmp(mode, "ERASE") == 0) {\r
+       val = term->cfg.bksp_is_delete ? "^?" : "^H";\r
+    }\r
+    /* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */\r
+    /* FIXME: or ECHO and friends based on local echo state? */\r
+    return dupstr(val);\r
+}\r
+\r
+struct term_userpass_state {\r
+    size_t curr_prompt;\r
+    int done_prompt;   /* printed out prompt yet? */\r
+    size_t pos;                /* cursor position */\r
+};\r
+\r
+/*\r
+ * Process some terminal data in the course of username/password\r
+ * input.\r
+ */\r
+int term_get_userpass_input(Terminal *term, prompts_t *p,\r
+                           unsigned char *in, int inlen)\r
+{\r
+    struct term_userpass_state *s = (struct term_userpass_state *)p->data;\r
+    if (!s) {\r
+       /*\r
+        * First call. Set some stuff up.\r
+        */\r
+       p->data = s = snew(struct term_userpass_state);\r
+       s->curr_prompt = 0;\r
+       s->done_prompt = 0;\r
+       /* We only print the `name' caption if we have to... */\r
+       if (p->name_reqd && p->name) {\r
+           size_t l = strlen(p->name);\r
+           term_data_untrusted(term, p->name, l);\r
+           if (p->name[l-1] != '\n')\r
+               term_data_untrusted(term, "\n", 1);\r
+       }\r
+       /* ...but we always print any `instruction'. */\r
+       if (p->instruction) {\r
+           size_t l = strlen(p->instruction);\r
+           term_data_untrusted(term, p->instruction, l);\r
+           if (p->instruction[l-1] != '\n')\r
+               term_data_untrusted(term, "\n", 1);\r
+       }\r
+       /*\r
+        * Zero all the results, in case we abort half-way through.\r
+        */\r
+       {\r
+           int i;\r
+           for (i = 0; i < (int)p->n_prompts; i++)\r
+               memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);\r
+       }\r
+    }\r
+\r
+    while (s->curr_prompt < p->n_prompts) {\r
+\r
+       prompt_t *pr = p->prompts[s->curr_prompt];\r
+       int finished_prompt = 0;\r
+\r
+       if (!s->done_prompt) {\r
+           term_data_untrusted(term, pr->prompt, strlen(pr->prompt));\r
+           s->done_prompt = 1;\r
+           s->pos = 0;\r
+       }\r
+\r
+       /* Breaking out here ensures that the prompt is printed even\r
+        * if we're now waiting for user data. */\r
+       if (!in || !inlen) break;\r
+\r
+       /* FIXME: should we be using local-line-editing code instead? */\r
+       while (!finished_prompt && inlen) {\r
+           char c = *in++;\r
+           inlen--;\r
+           switch (c) {\r
+             case 10:\r
+             case 13:\r
+               term_data(term, 0, "\r\n", 2);\r
+               pr->result[s->pos] = '\0';\r
+               pr->result[pr->result_len - 1] = '\0';\r
+               /* go to next prompt, if any */\r
+               s->curr_prompt++;\r
+               s->done_prompt = 0;\r
+               finished_prompt = 1; /* break out */\r
+               break;\r
+             case 8:\r
+             case 127:\r
+               if (s->pos > 0) {\r
+                   if (pr->echo)\r
+                       term_data(term, 0, "\b \b", 3);\r
+                   s->pos--;\r
+               }\r
+               break;\r
+             case 21:\r
+             case 27:\r
+               while (s->pos > 0) {\r
+                   if (pr->echo)\r
+                       term_data(term, 0, "\b \b", 3);\r
+                   s->pos--;\r
+               }\r
+               break;\r
+             case 3:\r
+             case 4:\r
+               /* Immediate abort. */\r
+               term_data(term, 0, "\r\n", 2);\r
+               sfree(s);\r
+               p->data = NULL;\r
+               return 0; /* user abort */\r
+             default:\r
+               /*\r
+                * This simplistic check for printability is disabled\r
+                * when we're doing password input, because some people\r
+                * have control characters in their passwords.\r
+                */\r
+               if ((!pr->echo ||\r
+                    (c >= ' ' && c <= '~') ||\r
+                    ((unsigned char) c >= 160))\r
+                   && s->pos < pr->result_len - 1) {\r
+                   pr->result[s->pos++] = c;\r
+                   if (pr->echo)\r
+                       term_data(term, 0, &c, 1);\r
+               }\r
+               break;\r
+           }\r
+       }\r
+       \r
+    }\r
+\r
+    if (s->curr_prompt < p->n_prompts) {\r
+       return -1; /* more data required */\r
+    } else {\r
+       sfree(s);\r
+       p->data = NULL;\r
+       return +1; /* all done */\r
+    }\r
+}\r
diff --git a/putty/TERMINAL.H b/putty/TERMINAL.H
new file mode 100644 (file)
index 0000000..6d3b1c5
--- /dev/null
@@ -0,0 +1,280 @@
+/*\r
+ * Internals of the Terminal structure, for those other modules\r
+ * which need to look inside it. It would be nice if this could be\r
+ * folded back into terminal.c in future, with an abstraction layer\r
+ * to handle everything that other modules need to know about it;\r
+ * but for the moment, this will do.\r
+ */\r
+\r
+#ifndef PUTTY_TERMINAL_H\r
+#define PUTTY_TERMINAL_H\r
+\r
+#include "tree234.h"\r
+\r
+struct beeptime {\r
+    struct beeptime *next;\r
+    unsigned long ticks;\r
+};\r
+\r
+typedef struct {\r
+    int y, x;\r
+} pos;\r
+\r
+#ifdef OPTIMISE_SCROLL\r
+struct scrollregion {\r
+    struct scrollregion *next;\r
+    int topline; /* Top line of scroll region. */\r
+    int botline; /* Bottom line of scroll region. */\r
+    int lines; /* Number of lines to scroll by - +ve is forwards. */\r
+};\r
+#endif /* OPTIMISE_SCROLL */\r
+\r
+typedef struct termchar termchar;\r
+typedef struct termline termline;\r
+\r
+struct termchar {\r
+    /*\r
+     * Any code in terminal.c which definitely needs to be changed\r
+     * when extra fields are added here is labelled with a comment\r
+     * saying FULL-TERMCHAR.\r
+     */\r
+    unsigned long chr;\r
+    unsigned long attr;\r
+\r
+    /*\r
+     * The cc_next field is used to link multiple termchars\r
+     * together into a list, so as to fit more than one character\r
+     * into a character cell (Unicode combining characters).\r
+     * \r
+     * cc_next is a relative offset into the current array of\r
+     * termchars. I.e. to advance to the next character in a list,\r
+     * one does `tc += tc->next'.\r
+     * \r
+     * Zero means end of list.\r
+     */\r
+    int cc_next;\r
+};\r
+\r
+struct termline {\r
+    unsigned short lattr;\r
+    int cols;                         /* number of real columns on the line */\r
+    int size;                         /* number of allocated termchars\r
+                                       * (cc-lists may make this > cols) */\r
+    int temporary;                    /* TRUE if decompressed from scrollback */\r
+    int cc_free;                      /* offset to first cc in free list */\r
+    struct termchar *chars;\r
+};\r
+\r
+struct bidi_cache_entry {\r
+    int width;\r
+    struct termchar *chars;\r
+    int *forward, *backward;          /* the permutations of line positions */\r
+};\r
+\r
+struct terminal_tag {\r
+\r
+    int compatibility_level;\r
+\r
+    tree234 *scrollback;              /* lines scrolled off top of screen */\r
+    tree234 *screen;                  /* lines on primary screen */\r
+    tree234 *alt_screen;              /* lines on alternate screen */\r
+    int disptop;                      /* distance scrolled back (0 or -ve) */\r
+    int tempsblines;                  /* number of lines of .scrollback that\r
+                                         can be retrieved onto the terminal\r
+                                         ("temporary scrollback") */\r
+\r
+    termline **disptext;              /* buffer of text on real screen */\r
+    int dispcursx, dispcursy;         /* location of cursor on real screen */\r
+    int curstype;                     /* type of cursor on real screen */\r
+\r
+#define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */\r
+\r
+    struct beeptime *beephead, *beeptail;\r
+    int nbeeps;\r
+    int beep_overloaded;\r
+    long lastbeep;\r
+\r
+#define TTYPE termchar\r
+#define TSIZE (sizeof(TTYPE))\r
+\r
+#ifdef OPTIMISE_SCROLL\r
+    struct scrollregion *scrollhead, *scrolltail;\r
+#endif /* OPTIMISE_SCROLL */\r
+\r
+    int default_attr, curr_attr, save_attr;\r
+    termchar basic_erase_char, erase_char;\r
+\r
+    bufchain inbuf;                   /* terminal input buffer */\r
+    pos curs;                         /* cursor */\r
+    pos savecurs;                     /* saved cursor position */\r
+    int marg_t, marg_b;                       /* scroll margins */\r
+    int dec_om;                               /* DEC origin mode flag */\r
+    int wrap, wrapnext;                       /* wrap flags */\r
+    int insert;                               /* insert-mode flag */\r
+    int cset;                         /* 0 or 1: which char set */\r
+    int save_cset, save_csattr;               /* saved with cursor position */\r
+    int save_utf, save_wnext;         /* saved with cursor position */\r
+    int rvideo;                               /* global reverse video flag */\r
+    unsigned long rvbell_startpoint;   /* for ESC[?5hESC[?5l vbell */\r
+    int cursor_on;                    /* cursor enabled flag */\r
+    int reset_132;                    /* Flag ESC c resets to 80 cols */\r
+    int use_bce;                      /* Use Background coloured erase */\r
+    int cblinker;                     /* When blinking is the cursor on ? */\r
+    int tblinker;                     /* When the blinking text is on */\r
+    int blink_is_real;                /* Actually blink blinking text */\r
+    int term_echoing;                 /* Does terminal want local echo? */\r
+    int term_editing;                 /* Does terminal want local edit? */\r
+    int sco_acs, save_sco_acs;        /* CSI 10,11,12m -> OEM charset */\r
+    int vt52_bold;                    /* Force bold on non-bold colours */\r
+    int utf;                          /* Are we in toggleable UTF-8 mode? */\r
+    int utf_state;                    /* Is there a pending UTF-8 character */\r
+    int utf_char;                     /* and what is it so far. */\r
+    int utf_size;                     /* The size of the UTF character. */\r
+    int printing, only_printing;       /* Are we doing ANSI printing? */\r
+    int print_state;                  /* state of print-end-sequence scan */\r
+    bufchain printer_buf;             /* buffered data for printer */\r
+    printer_job *print_job;\r
+\r
+    /* ESC 7 saved state for the alternate screen */\r
+    pos alt_savecurs;\r
+    int alt_save_attr;\r
+    int alt_save_cset, alt_save_csattr;\r
+    int alt_save_utf, alt_save_wnext;\r
+    int alt_save_sco_acs;\r
+\r
+    int rows, cols, savelines;\r
+    int has_focus;\r
+    int in_vbell;\r
+    long vbell_end;\r
+    int app_cursor_keys, app_keypad_keys, vt52_mode;\r
+    int repeat_off, cr_lf_return;\r
+    int seen_disp_event;\r
+    int big_cursor;\r
+\r
+    int xterm_mouse;                  /* send mouse messages to host */\r
+    int mouse_is_down;                /* used while tracking mouse buttons */\r
+\r
+    int cset_attr[2];\r
+\r
+/*\r
+ * Saved settings on the alternate screen.\r
+ */\r
+    int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins;\r
+    int alt_cset, alt_sco_acs, alt_utf;\r
+    int alt_t, alt_b;\r
+    int alt_which;\r
+    int alt_sblines; /* # of lines on alternate screen that should be used for scrollback. */\r
+\r
+#define ARGS_MAX 32                   /* max # of esc sequence arguments */\r
+#define ARG_DEFAULT 0                 /* if an arg isn't specified */\r
+#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )\r
+    int esc_args[ARGS_MAX];\r
+    int esc_nargs;\r
+    int esc_query;\r
+#define ANSI(x,y)      ((x)+((y)<<8))\r
+#define ANSI_QUE(x)    ANSI(x,TRUE)\r
+\r
+#define OSC_STR_MAX 2048\r
+    int osc_strlen;\r
+    char osc_string[OSC_STR_MAX + 1];\r
+    int osc_w;\r
+\r
+    char id_string[1024];\r
+\r
+    unsigned char *tabs;\r
+\r
+    enum {\r
+       TOPLEVEL,\r
+       SEEN_ESC,\r
+       SEEN_CSI,\r
+       SEEN_OSC,\r
+       SEEN_OSC_W,\r
+\r
+       DO_CTRLS,\r
+\r
+       SEEN_OSC_P,\r
+       OSC_STRING, OSC_MAYBE_ST,\r
+       VT52_ESC,\r
+       VT52_Y1,\r
+       VT52_Y2,\r
+       VT52_FG,\r
+       VT52_BG\r
+    } termstate;\r
+\r
+    enum {\r
+       NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED\r
+    } selstate;\r
+    enum {\r
+       LEXICOGRAPHIC, RECTANGULAR\r
+    } seltype;\r
+    enum {\r
+       SM_CHAR, SM_WORD, SM_LINE\r
+    } selmode;\r
+    pos selstart, selend, selanchor;\r
+\r
+    short wordness[256];\r
+\r
+    /* Mask of attributes to pay attention to when painting. */\r
+    int attr_mask;\r
+\r
+    wchar_t *paste_buffer;\r
+    int paste_len, paste_pos, paste_hold;\r
+    long last_paste;\r
+\r
+    void (*resize_fn)(void *, int, int);\r
+    void *resize_ctx;\r
+\r
+    void *ldisc;\r
+\r
+    void *frontend;\r
+\r
+    void *logctx;\r
+\r
+    struct unicode_data *ucsdata;\r
+\r
+    /*\r
+     * We maintain a full _copy_ of a Config structure here, not\r
+     * merely a pointer to it. That way, when we're passed a new\r
+     * one for reconfiguration, we can check the differences and\r
+     * adjust the _current_ setting of (e.g.) auto wrap mode rather\r
+     * than only the default.\r
+     */\r
+    Config cfg;\r
+\r
+    /*\r
+     * from_backend calls term_out, but it can also be called from\r
+     * the ldisc if the ldisc is called _within_ term_out. So we\r
+     * have to guard against re-entrancy - if from_backend is\r
+     * called recursively like this, it will simply add data to the\r
+     * end of the buffer term_out is in the process of working\r
+     * through.\r
+     */\r
+    int in_term_out;\r
+\r
+    /*\r
+     * We schedule a window update shortly after receiving terminal\r
+     * data. This tracks whether one is currently pending.\r
+     */\r
+    int window_update_pending;\r
+    long next_update;\r
+\r
+    /*\r
+     * Track pending blinks and tblinks.\r
+     */\r
+    int tblink_pending, cblink_pending;\r
+    long next_tblink, next_cblink;\r
+\r
+    /*\r
+     * These are buffers used by the bidi and Arabic shaping code.\r
+     */\r
+    termchar *ltemp;\r
+    int ltemp_size;\r
+    bidi_char *wcFrom, *wcTo;\r
+    int wcFromTo_size;\r
+    struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache;\r
+    int bidi_cache_size;\r
+};\r
+\r
+#define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)\r
+\r
+#endif\r
diff --git a/putty/TESTBACK.C b/putty/TESTBACK.C
new file mode 100644 (file)
index 0000000..0dd26d9
--- /dev/null
@@ -0,0 +1,180 @@
+/* $Id: testback.c 7628 2007-06-30 21:56:44Z jacob $ */\r
+/*\r
+ * Copyright (c) 1999 Simon Tatham\r
+ * Copyright (c) 1999 Ben Harris\r
+ * All rights reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person\r
+ * obtaining a copy of this software and associated documentation\r
+ * files (the "Software"), to deal in the Software without\r
+ * restriction, including without limitation the rights to use,\r
+ * copy, modify, merge, publish, distribute, sublicense, and/or\r
+ * sell copies of the Software, and to permit persons to whom the\r
+ * Software is furnished to do so, subject to the following\r
+ * conditions:\r
+ * \r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ * \r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+\r
+/* PuTTY test backends */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+\r
+static const char *null_init(void *, void **, Config *, char *, int, char **,\r
+                            int, int);\r
+static const char *loop_init(void *, void **, Config *, char *, int, char **,\r
+                            int, int);\r
+static void null_free(void *);\r
+static void loop_free(void *);\r
+static void null_reconfig(void *, Config *);\r
+static int null_send(void *, char *, int);\r
+static int loop_send(void *, char *, int);\r
+static int null_sendbuffer(void *);\r
+static void null_size(void *, int, int);\r
+static void null_special(void *, Telnet_Special);\r
+static const struct telnet_special *null_get_specials(void *handle);\r
+static int null_connected(void *);\r
+static int null_exitcode(void *);\r
+static int null_sendok(void *);\r
+static int null_ldisc(void *, int);\r
+static void null_provide_ldisc(void *, void *);\r
+static void null_provide_logctx(void *, void *);\r
+static void null_unthrottle(void *, int);\r
+static int null_cfg_info(void *);\r
+\r
+Backend null_backend = {\r
+    null_init, null_free, null_reconfig, null_send, null_sendbuffer, null_size,\r
+    null_special, null_get_specials, null_connected, null_exitcode, null_sendok,\r
+    null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle,\r
+    null_cfg_info, "null", -1, 0\r
+};\r
+\r
+Backend loop_backend = {\r
+    loop_init, loop_free, null_reconfig, loop_send, null_sendbuffer, null_size,\r
+    null_special, null_get_specials, null_connected, null_exitcode, null_sendok,\r
+    null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle,\r
+    null_cfg_info, "loop", -1, 0\r
+};\r
+\r
+struct loop_state {\r
+    Terminal *term;\r
+};\r
+\r
+static const char *null_init(void *frontend_handle, void **backend_handle,\r
+                            Config *cfg, char *host, int port,\r
+                            char **realhost, int nodelay, int keepalive) {\r
+\r
+    return NULL;\r
+}\r
+\r
+static const char *loop_init(void *frontend_handle, void **backend_handle,\r
+                            Config *cfg, char *host, int port,\r
+                            char **realhost, int nodelay, int keepalive) {\r
+    struct loop_state *st = snew(struct loop_state);\r
+\r
+    st->term = frontend_handle;\r
+    *backend_handle = st;\r
+    return NULL;\r
+}\r
+\r
+static void null_free(void *handle)\r
+{\r
+\r
+}\r
+\r
+static void loop_free(void *handle)\r
+{\r
+\r
+    sfree(handle);\r
+}\r
+\r
+static void null_reconfig(void *handle, Config *cfg) {\r
+\r
+}\r
+\r
+static int null_send(void *handle, char *buf, int len) {\r
+\r
+    return 0;\r
+}\r
+\r
+static int loop_send(void *handle, char *buf, int len) {\r
+    struct loop_state *st = handle;\r
+\r
+    return from_backend(st->term, 0, buf, len);\r
+}\r
+\r
+static int null_sendbuffer(void *handle) {\r
+\r
+    return 0;\r
+}\r
+\r
+static void null_size(void *handle, int width, int height) {\r
+\r
+}\r
+\r
+static void null_special(void *handle, Telnet_Special code) {\r
+\r
+}\r
+\r
+static const struct telnet_special *null_get_specials (void *handle) {\r
+\r
+    return NULL;\r
+}\r
+\r
+static int null_connected(void *handle) {\r
+\r
+    return 0;\r
+}\r
+\r
+static int null_exitcode(void *handle) {\r
+\r
+    return 0;\r
+}\r
+\r
+static int null_sendok(void *handle) {\r
+\r
+    return 1;\r
+}\r
+\r
+static void null_unthrottle(void *handle, int backlog) {\r
+\r
+}\r
+\r
+static int null_ldisc(void *handle, int option) {\r
+\r
+    return 0;\r
+}\r
+\r
+static void null_provide_ldisc (void *handle, void *ldisc) {\r
+\r
+}\r
+\r
+static void null_provide_logctx(void *handle, void *logctx) {\r
+\r
+}\r
+\r
+static int null_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+\r
+/*\r
+ * Emacs magic:\r
+ * Local Variables:\r
+ * c-file-style: "simon"\r
+ * End:\r
+ */\r
diff --git a/putty/TESTDATA/BIGNUM.PY b/putty/TESTDATA/BIGNUM.PY
new file mode 100644 (file)
index 0000000..0a24780
--- /dev/null
@@ -0,0 +1,115 @@
+# Generate test cases for a bignum implementation.\r
+\r
+import sys\r
+\r
+# integer square roots\r
+def sqrt(n):\r
+    d = long(n)\r
+    a = 0L\r
+    # b must start off as a power of 4 at least as large as n\r
+    ndigits = len(hex(long(n)))\r
+    b = 1L << (ndigits*4)\r
+    while 1:\r
+        a = a >> 1\r
+        di = 2*a + b\r
+        if di <= d:\r
+            d = d - di\r
+            a = a + b\r
+        b = b >> 2\r
+        if b == 0: break\r
+    return a\r
+\r
+# continued fraction convergents of a rational\r
+def confrac(n, d):\r
+    coeffs = [(1,0),(0,1)]\r
+    while d != 0:\r
+        i = n / d\r
+        n, d = d, n % d\r
+        coeffs.append((coeffs[-2][0]-i*coeffs[-1][0],\r
+                       coeffs[-2][1]-i*coeffs[-1][1]))\r
+    return coeffs\r
+\r
+def findprod(target, dir = +1, ratio=(1,1)):\r
+    # Return two numbers whose product is as close as we can get to\r
+    # 'target', with any deviation having the sign of 'dir', and in\r
+    # the same approximate ratio as 'ratio'.\r
+\r
+    r = sqrt(target * ratio[0] * ratio[1])\r
+    a = r / ratio[1]\r
+    b = r / ratio[0]\r
+    if a*b * dir < target * dir:\r
+        a = a + 1\r
+        b = b + 1\r
+    assert a*b * dir >= target * dir\r
+\r
+    best = (a,b,a*b)\r
+\r
+    while 1:\r
+        improved = 0\r
+        a, b = best[:2]\r
+\r
+        coeffs = confrac(a, b)\r
+        for c in coeffs:\r
+            # a*c[0]+b*c[1] is as close as we can get it to zero. So\r
+            # if we replace a and b with a+c[1] and b+c[0], then that\r
+            # will be added to our product, along with c[0]*c[1].\r
+            da, db = c[1], c[0]\r
+\r
+            # Flip signs as appropriate.\r
+            if (a+da) * (b+db) * dir < target * dir:\r
+                da, db = -da, -db\r
+\r
+            # Multiply up. We want to get as close as we can to a\r
+            # solution of the quadratic equation in n\r
+            #\r
+            #    (a + n da) (b + n db) = target\r
+            # => n^2 da db + n (b da + a db) + (a b - target) = 0\r
+            A,B,C = da*db, b*da+a*db, a*b-target\r
+            discrim = B^2-4*A*C\r
+            if discrim > 0 and A != 0:\r
+                root = sqrt(discrim)\r
+                vals = []\r
+                vals.append((-B + root) / (2*A))\r
+                vals.append((-B - root) / (2*A))\r
+                if root * root != discrim:\r
+                    root = root + 1\r
+                    vals.append((-B + root) / (2*A))\r
+                    vals.append((-B - root) / (2*A))\r
+\r
+                for n in vals:\r
+                    ap = a + da*n\r
+                    bp = b + db*n\r
+                    pp = ap*bp\r
+                    if pp * dir >= target * dir and pp * dir < best[2]*dir:\r
+                        best = (ap, bp, pp)\r
+                        improved = 1\r
+\r
+        if not improved:\r
+            break\r
+\r
+    return best\r
+\r
+def hexstr(n):\r
+    s = hex(n)\r
+    if s[:2] == "0x": s = s[2:]\r
+    if s[-1:] == "L": s = s[:-1]\r
+    return s\r
+\r
+# Tests of multiplication which exercise the propagation of the last\r
+# carry to the very top of the number.\r
+for i in range(1,4200):\r
+    a, b, p = findprod((1<<i)+1, +1, (i, i*i+1))\r
+    print "mul", hexstr(a), hexstr(b), hexstr(p)\r
+    a, b, p = findprod((1<<i)+1, +1, (i, i+1))\r
+    print "mul", hexstr(a), hexstr(b), hexstr(p)\r
+\r
+# Simple tests of modpow.\r
+for i in range(64, 4097, 63):\r
+    modulus = sqrt(1<<(2*i-1)) | 1\r
+    base = sqrt(3*modulus*modulus) % modulus\r
+    expt = sqrt(modulus*modulus*2/5)\r
+    print "pow", hexstr(base), hexstr(expt), hexstr(modulus), hexstr(pow(base, expt, modulus))\r
+    if i <= 1024:\r
+        # Test even moduli, which can't be done by Montgomery.\r
+        modulus = modulus - 1\r
+        print "pow", hexstr(base), hexstr(expt), hexstr(modulus), hexstr(pow(base, expt, modulus))\r
diff --git a/putty/TESTDATA/COLOURS.TXT b/putty/TESTDATA/COLOURS.TXT
new file mode 100644 (file)
index 0000000..bd9fd27
--- /dev/null
@@ -0,0 +1,22 @@
+Test of most colour rendering. Omits the SCO fg and bg sequences,\r
+since they are destructive.\r
+Normal text \e[1mand bold\e[m; \e[7mreverse video \e[1mand bold\e[m\r
+ANSI plus bold: \e[30m0 \e[1mbold\e[m \e[31m1 \e[1mbold\e[m \e[32m2 \e[1mbold\e[m \e[33m3 \e[1mbold\e[m \e[34m4 \e[1mbold\e[m \e[35m5 \e[1mbold\e[m \e[36m6 \e[1mbold\e[m \e[37m7 \e[1mbold\e[m\r
+xterm bright: \e[90mfg0\e[m \e[100mbg0\e[m \e[91mfg1\e[m \e[101mbg1\e[m \e[92mfg2\e[m \e[102mbg2\e[m \e[93mfg3\e[m \e[103mbg3\e[m \e[94mfg4\e[m \e[104mbg4\e[m \e[95mfg5\e[m \e[105mbg5\e[m \e[96mfg6\e[m \e[106mbg6\e[m \e[97mfg7\e[m \e[107mbg7\e[m\r
+xterm 256:\r
+\e[38;5;0m   0\e[m\e[38;5;1m   1\e[m\e[38;5;2m   2\e[m\e[38;5;3m   3\e[m\e[38;5;4m   4\e[m\e[38;5;5m   5\e[m\e[38;5;6m   6\e[m\e[38;5;7m   7\e[m\e[38;5;8m   8\e[m\e[38;5;9m   9\e[m\e[38;5;10m  10\e[m\e[38;5;11m  11\e[m\e[38;5;12m  12\e[m\e[38;5;13m  13\e[m\e[38;5;14m  14\e[m\e[38;5;15m  15\e[m\r
+\e[38;5;16m  16\e[m\e[38;5;17m  17\e[m\e[38;5;18m  18\e[m\e[38;5;19m  19\e[m\e[38;5;20m  20\e[m\e[38;5;21m  21\e[m\e[38;5;22m  22\e[m\e[38;5;23m  23\e[m\e[38;5;24m  24\e[m\e[38;5;25m  25\e[m\e[38;5;26m  26\e[m\e[38;5;27m  27\e[m\e[38;5;28m  28\e[m\e[38;5;29m  29\e[m\e[38;5;30m  30\e[m\e[38;5;31m  31\e[m\r
+\e[38;5;32m  32\e[m\e[38;5;33m  33\e[m\e[38;5;34m  34\e[m\e[38;5;35m  35\e[m\e[38;5;36m  36\e[m\e[38;5;37m  37\e[m\e[38;5;38m  38\e[m\e[38;5;39m  39\e[m\e[38;5;40m  40\e[m\e[38;5;41m  41\e[m\e[38;5;42m  42\e[m\e[38;5;43m  43\e[m\e[38;5;44m  44\e[m\e[38;5;45m  45\e[m\e[38;5;46m  46\e[m\e[38;5;47m  47\e[m\r
+\e[38;5;48m  48\e[m\e[38;5;49m  49\e[m\e[38;5;50m  50\e[m\e[38;5;51m  51\e[m\e[38;5;52m  52\e[m\e[38;5;53m  53\e[m\e[38;5;54m  54\e[m\e[38;5;55m  55\e[m\e[38;5;56m  56\e[m\e[38;5;57m  57\e[m\e[38;5;58m  58\e[m\e[38;5;59m  59\e[m\e[38;5;60m  60\e[m\e[38;5;61m  61\e[m\e[38;5;62m  62\e[m\e[38;5;63m  63\e[m\r
+\e[38;5;64m  64\e[m\e[38;5;65m  65\e[m\e[38;5;66m  66\e[m\e[38;5;67m  67\e[m\e[38;5;68m  68\e[m\e[38;5;69m  69\e[m\e[38;5;70m  70\e[m\e[38;5;71m  71\e[m\e[38;5;72m  72\e[m\e[38;5;73m  73\e[m\e[38;5;74m  74\e[m\e[38;5;75m  75\e[m\e[38;5;76m  76\e[m\e[38;5;77m  77\e[m\e[38;5;78m  78\e[m\e[38;5;79m  79\e[m\r
+\e[38;5;80m  80\e[m\e[38;5;81m  81\e[m\e[38;5;82m  82\e[m\e[38;5;83m  83\e[m\e[38;5;84m  84\e[m\e[38;5;85m  85\e[m\e[38;5;86m  86\e[m\e[38;5;87m  87\e[m\e[38;5;88m  88\e[m\e[38;5;89m  89\e[m\e[38;5;90m  90\e[m\e[38;5;91m  91\e[m\e[38;5;92m  92\e[m\e[38;5;93m  93\e[m\e[38;5;94m  94\e[m\e[38;5;95m  95\e[m\r
+\e[38;5;96m  96\e[m\e[38;5;97m  97\e[m\e[38;5;98m  98\e[m\e[38;5;99m  99\e[m\e[38;5;100m 100\e[m\e[38;5;101m 101\e[m\e[38;5;102m 102\e[m\e[38;5;103m 103\e[m\e[38;5;104m 104\e[m\e[38;5;105m 105\e[m\e[38;5;106m 106\e[m\e[38;5;107m 107\e[m\e[38;5;108m 108\e[m\e[38;5;109m 109\e[m\e[38;5;110m 110\e[m\e[38;5;111m 111\e[m\r
+\e[38;5;112m 112\e[m\e[38;5;113m 113\e[m\e[38;5;114m 114\e[m\e[38;5;115m 115\e[m\e[38;5;116m 116\e[m\e[38;5;117m 117\e[m\e[38;5;118m 118\e[m\e[38;5;119m 119\e[m\e[38;5;120m 120\e[m\e[38;5;121m 121\e[m\e[38;5;122m 122\e[m\e[38;5;123m 123\e[m\e[38;5;124m 124\e[m\e[38;5;125m 125\e[m\e[38;5;126m 126\e[m\e[38;5;127m 127\e[m\r
+\e[38;5;128m 128\e[m\e[38;5;129m 129\e[m\e[38;5;130m 130\e[m\e[38;5;131m 131\e[m\e[38;5;132m 132\e[m\e[38;5;133m 133\e[m\e[38;5;134m 134\e[m\e[38;5;135m 135\e[m\e[38;5;136m 136\e[m\e[38;5;137m 137\e[m\e[38;5;138m 138\e[m\e[38;5;139m 139\e[m\e[38;5;140m 140\e[m\e[38;5;141m 141\e[m\e[38;5;142m 142\e[m\e[38;5;143m 143\e[m\r
+\e[38;5;144m 144\e[m\e[38;5;145m 145\e[m\e[38;5;146m 146\e[m\e[38;5;147m 147\e[m\e[38;5;148m 148\e[m\e[38;5;149m 149\e[m\e[38;5;150m 150\e[m\e[38;5;151m 151\e[m\e[38;5;152m 152\e[m\e[38;5;153m 153\e[m\e[38;5;154m 154\e[m\e[38;5;155m 155\e[m\e[38;5;156m 156\e[m\e[38;5;157m 157\e[m\e[38;5;158m 158\e[m\e[38;5;159m 159\e[m\r
+\e[38;5;160m 160\e[m\e[38;5;161m 161\e[m\e[38;5;162m 162\e[m\e[38;5;163m 163\e[m\e[38;5;164m 164\e[m\e[38;5;165m 165\e[m\e[38;5;166m 166\e[m\e[38;5;167m 167\e[m\e[38;5;168m 168\e[m\e[38;5;169m 169\e[m\e[38;5;170m 170\e[m\e[38;5;171m 171\e[m\e[38;5;172m 172\e[m\e[38;5;173m 173\e[m\e[38;5;174m 174\e[m\e[38;5;175m 175\e[m\r
+\e[38;5;176m 176\e[m\e[38;5;177m 177\e[m\e[38;5;178m 178\e[m\e[38;5;179m 179\e[m\e[38;5;180m 180\e[m\e[38;5;181m 181\e[m\e[38;5;182m 182\e[m\e[38;5;183m 183\e[m\e[38;5;184m 184\e[m\e[38;5;185m 185\e[m\e[38;5;186m 186\e[m\e[38;5;187m 187\e[m\e[38;5;188m 188\e[m\e[38;5;189m 189\e[m\e[38;5;190m 190\e[m\e[38;5;191m 191\e[m\r
+\e[38;5;192m 192\e[m\e[38;5;193m 193\e[m\e[38;5;194m 194\e[m\e[38;5;195m 195\e[m\e[38;5;196m 196\e[m\e[38;5;197m 197\e[m\e[38;5;198m 198\e[m\e[38;5;199m 199\e[m\e[38;5;200m 200\e[m\e[38;5;201m 201\e[m\e[38;5;202m 202\e[m\e[38;5;203m 203\e[m\e[38;5;204m 204\e[m\e[38;5;205m 205\e[m\e[38;5;206m 206\e[m\e[38;5;207m 207\e[m\r
+\e[38;5;208m 208\e[m\e[38;5;209m 209\e[m\e[38;5;210m 210\e[m\e[38;5;211m 211\e[m\e[38;5;212m 212\e[m\e[38;5;213m 213\e[m\e[38;5;214m 214\e[m\e[38;5;215m 215\e[m\e[38;5;216m 216\e[m\e[38;5;217m 217\e[m\e[38;5;218m 218\e[m\e[38;5;219m 219\e[m\e[38;5;220m 220\e[m\e[38;5;221m 221\e[m\e[38;5;222m 222\e[m\e[38;5;223m 223\e[m\r
+\e[38;5;224m 224\e[m\e[38;5;225m 225\e[m\e[38;5;226m 226\e[m\e[38;5;227m 227\e[m\e[38;5;228m 228\e[m\e[38;5;229m 229\e[m\e[38;5;230m 230\e[m\e[38;5;231m 231\e[m\e[38;5;232m 232\e[m\e[38;5;233m 233\e[m\e[38;5;234m 234\e[m\e[38;5;235m 235\e[m\e[38;5;236m 236\e[m\e[38;5;237m 237\e[m\e[38;5;238m 238\e[m\e[38;5;239m 239\e[m\r
+\e[38;5;240m 240\e[m\e[38;5;241m 241\e[m\e[38;5;242m 242\e[m\e[38;5;243m 243\e[m\e[38;5;244m 244\e[m\e[38;5;245m 245\e[m\e[38;5;246m 246\e[m\e[38;5;247m 247\e[m\e[38;5;248m 248\e[m\e[38;5;249m 249\e[m\e[38;5;250m 250\e[m\e[38;5;251m 251\e[m\e[38;5;252m 252\e[m\e[38;5;253m 253\e[m\e[38;5;254m 254\e[m\e[38;5;255m 255\e[m\r
diff --git a/putty/TESTDATA/LATTRS.TXT b/putty/TESTDATA/LATTRS.TXT
new file mode 100644 (file)
index 0000000..229da78
--- /dev/null
@@ -0,0 +1,6 @@
+Test of line attributes:\r
+\r
+\e#3Double-height top\r
+\e#4Double-height bottom\r
+\e#5Normal text (#5)\r
+\e#6Double-width only\r
diff --git a/putty/TESTDATA/SCOCOLS.TXT b/putty/TESTDATA/SCOCOLS.TXT
new file mode 100644 (file)
index 0000000..d09ef01
--- /dev/null
@@ -0,0 +1,3 @@
+Test of (destructive) SCO colour rendering.\r
+SCO fg: \e[=0F0\e[=7F \e[=1F1\e[=7F \e[=2F2\e[=7F \e[=3F3\e[=7F \e[=4F4\e[=7F \e[=5F5\e[=7F \e[=6F6\e[=7F \e[=7F7\e[=7F \e[=8F8\e[=7F \e[=9F9\e[=7F \e[=10F10\e[=7F \e[=11F11\e[=7F \e[=12F12\e[=7F \e[=13F13\e[=7F \e[=14F14\e[=7F \e[=15F15\e[=7F\r
+SCO bg: \e[=0G0\e[=0G \e[=1G1\e[=0G \e[=2G2\e[=0G \e[=3G3\e[=0G \e[=4G4\e[=0G \e[=5G5\e[=0G \e[=6G6\e[=0G \e[=7G7\e[=0G \e[=8G8\e[=0G \e[=9G9\e[=0G \e[=10G10\e[=0G \e[=11G11\e[=0G \e[=12G12\e[=0G \e[=13G13\e[=0G \e[=14G14\e[=0G \e[=15G15\e[=0G\r
diff --git a/putty/TESTDATA/UTF8.TXT b/putty/TESTDATA/UTF8.TXT
new file mode 100644 (file)
index 0000000..51a6f61
--- /dev/null
@@ -0,0 +1,23 @@
+Test of UTF-8 output in a terminal emulator\r
+‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\r
+\r
+Some basic Unicode:\r
+  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),\r
+  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B),\r
+\r
+Combining characters:\r
+  STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑\r
+  [----------------------------|------------------------]\r
+    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่\r
+  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา\r
+\r
+Wide characters with difficult wrapping:\r
+  Here we go then: コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ コンニチハ\r
+\r
+Arabic and bidirectional text:\r
+            (من مجمع الزوائد ومنبع الفوائد للهيثمي ، ج 1 ، ص  74-84)           \r
+               عن \e[44mجرير\e[m \e[41mرضي\e[m الله عنه قال قال رسول الله صلى الله عليه\r
+          وسلم: بني الاسلام على خمس شهادة ان لا اله الا الله واقام              \r
+Mixed LTR and RTL text: \e[44mجرير\e[m \e[41mرضي\e[m back to LTR.\r
+\r
+East Asian Ambiguous characters: ¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾\r
diff --git a/putty/TESTDATA/VT100.TXT b/putty/TESTDATA/VT100.TXT
new file mode 100644 (file)
index 0000000..b206770
--- /dev/null
@@ -0,0 +1,12 @@
+VT100 line drawing characters, actually using the VT100 escapes
+\e(B\e)0\ eooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\ f
+
+\ elqqqqqqqqqqpoopqrssrqqqqqqqqqqwqqqqqqqqqqpoopqrssrqqqqqqqqqqk\ f
+\ ex\ f                             \ ex\ f                             \ ex\ f
+\ ex\ f      ooh, swirly!           \ ex\ f       top right corner      \ ex\ f
+\ ex\ f                             \ ex\ f                             \ ex\ f
+\ etqqqqqqqqqqpoopqrssrqqqqqqqqqqnqqqqqqqqqqpoopqrssrqqqqqqqqqqu\ f
+\ ex\ f                             \ ex\ f                             \ ex\ f
+\ ex\ f     stuff down here         \ ex\ f       is quite inane        \ ex\ f
+\ ex\ f                             \ ex\ f                             \ ex\ f
+\ emqqqqqqqqqqpoopqrssrqqqqqqqqqqvqqqqqqqqqqpoopqrssrqqqqqqqqqqj\ f
diff --git a/putty/TIME.C b/putty/TIME.C
new file mode 100644 (file)
index 0000000..d873d44
--- /dev/null
@@ -0,0 +1,16 @@
+/*\r
+ * Portable implementation of ltime() for any ISO-C platform where\r
+ * time_t behaves. (In practice, we've found that platforms such as\r
+ * Windows and Mac have needed their own specialised implementations.)\r
+ */\r
+\r
+#include <time.h>\r
+#include <assert.h>\r
+\r
+struct tm ltime(void)\r
+{\r
+    time_t t;\r
+    time(&t);\r
+    assert (t != ((time_t)-1));\r
+    return *localtime(&t);\r
+}\r
diff --git a/putty/TIMING.C b/putty/TIMING.C
new file mode 100644 (file)
index 0000000..2b7b70c
--- /dev/null
@@ -0,0 +1,243 @@
+/*\r
+ * timing.c\r
+ * \r
+ * This module tracks any timers set up by schedule_timer(). It\r
+ * keeps all the currently active timers in a list; it informs the\r
+ * front end of when the next timer is due to go off if that\r
+ * changes; and, very importantly, it tracks the context pointers\r
+ * passed to schedule_timer(), so that if a context is freed all\r
+ * the timers associated with it can be immediately annulled.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+\r
+#include "putty.h"\r
+#include "tree234.h"\r
+\r
+struct timer {\r
+    timer_fn_t fn;\r
+    void *ctx;\r
+    long now;\r
+};\r
+\r
+static tree234 *timers = NULL;\r
+static tree234 *timer_contexts = NULL;\r
+static long now = 0L;\r
+\r
+static int compare_timers(void *av, void *bv)\r
+{\r
+    struct timer *a = (struct timer *)av;\r
+    struct timer *b = (struct timer *)bv;\r
+    long at = a->now - now;\r
+    long bt = b->now - now;\r
+\r
+    if (at < bt)\r
+       return -1;\r
+    else if (at > bt)\r
+       return +1;\r
+\r
+    /*\r
+     * Failing that, compare on the other two fields, just so that\r
+     * we don't get unwanted equality.\r
+     */\r
+#ifdef __LCC__\r
+    /* lcc won't let us compare function pointers. Legal, but annoying. */\r
+    {\r
+       int c = memcmp(&a->fn, &b->fn, sizeof(a->fn));\r
+       if (c < 0)\r
+           return -1;\r
+       else if (c > 0)\r
+           return +1;\r
+    }\r
+#else    \r
+    if (a->fn < b->fn)\r
+       return -1;\r
+    else if (a->fn > b->fn)\r
+       return +1;\r
+#endif\r
+\r
+    if (a->ctx < b->ctx)\r
+       return -1;\r
+    else if (a->ctx > b->ctx)\r
+       return +1;\r
+\r
+    /*\r
+     * Failing _that_, the two entries genuinely are equal, and we\r
+     * never have a need to store them separately in the tree.\r
+     */\r
+    return 0;\r
+}\r
+\r
+static int compare_timer_contexts(void *av, void *bv)\r
+{\r
+    char *a = (char *)av;\r
+    char *b = (char *)bv;\r
+    if (a < b)\r
+       return -1;\r
+    else if (a > b)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static void init_timers(void)\r
+{\r
+    if (!timers) {\r
+       timers = newtree234(compare_timers);\r
+       timer_contexts = newtree234(compare_timer_contexts);\r
+       now = GETTICKCOUNT();\r
+    }\r
+}\r
+\r
+long schedule_timer(int ticks, timer_fn_t fn, void *ctx)\r
+{\r
+    long when;\r
+    struct timer *t, *first;\r
+\r
+    init_timers();\r
+\r
+    when = ticks + GETTICKCOUNT();\r
+\r
+    /*\r
+     * Just in case our various defences against timing skew fail\r
+     * us: if we try to schedule a timer that's already in the\r
+     * past, we instead schedule it for the immediate future.\r
+     */\r
+    if (when - now <= 0)\r
+       when = now + 1;\r
+\r
+    t = snew(struct timer);\r
+    t->fn = fn;\r
+    t->ctx = ctx;\r
+    t->now = when;\r
+\r
+    if (t != add234(timers, t)) {\r
+       sfree(t);                      /* identical timer already exists */\r
+    } else {\r
+       add234(timer_contexts, t->ctx);/* don't care if this fails */\r
+    }\r
+\r
+    first = (struct timer *)index234(timers, 0);\r
+    if (first == t) {\r
+       /*\r
+        * This timer is the very first on the list, so we must\r
+        * notify the front end.\r
+        */\r
+       timer_change_notify(first->now);\r
+    }\r
+\r
+    return when;\r
+}\r
+\r
+/*\r
+ * Call to run any timers whose time has reached the present.\r
+ * Returns the time (in ticks) expected until the next timer after\r
+ * that triggers.\r
+ */\r
+int run_timers(long anow, long *next)\r
+{\r
+    struct timer *first;\r
+\r
+    init_timers();\r
+\r
+#ifdef TIMING_SYNC\r
+    /*\r
+     * In this ifdef I put some code which deals with the\r
+     * possibility that `anow' disagrees with GETTICKCOUNT by a\r
+     * significant margin. Our strategy for dealing with it differs\r
+     * depending on platform, because on some platforms\r
+     * GETTICKCOUNT is more likely to be right whereas on others\r
+     * `anow' is a better gold standard.\r
+     */\r
+    {\r
+       long tnow = GETTICKCOUNT();\r
+\r
+       if (tnow + TICKSPERSEC/50 - anow < 0 ||\r
+           anow + TICKSPERSEC/50 - tnow < 0\r
+           ) {\r
+#if defined TIMING_SYNC_ANOW\r
+           /*\r
+            * If anow is accurate and the tick count is wrong,\r
+            * this is likely to be because the tick count is\r
+            * derived from the system clock which has changed (as\r
+            * can occur on Unix). Therefore, we resolve this by\r
+            * inventing an offset which is used to adjust all\r
+            * future output from GETTICKCOUNT.\r
+            * \r
+            * A platform which defines TIMING_SYNC_ANOW is\r
+            * expected to have also defined this offset variable\r
+            * in (its platform-specific adjunct to) putty.h.\r
+            * Therefore we can simply reference it here and assume\r
+            * that it will exist.\r
+            */\r
+           tickcount_offset += anow - tnow;\r
+#elif defined TIMING_SYNC_TICKCOUNT\r
+           /*\r
+            * If the tick count is more likely to be accurate, we\r
+            * simply use that as our time value, which may mean we\r
+            * run no timers in this call (because we got called\r
+            * early), or alternatively it may mean we run lots of\r
+            * timers in a hurry because we were called late.\r
+            */\r
+           anow = tnow;\r
+#else\r
+/*\r
+ * Any platform which defines TIMING_SYNC must also define one of the two\r
+ * auxiliary symbols TIMING_SYNC_ANOW and TIMING_SYNC_TICKCOUNT, to\r
+ * indicate which measurement to trust when the two disagree.\r
+ */\r
+#error TIMING_SYNC definition incomplete\r
+#endif\r
+       }\r
+    }\r
+#endif\r
+\r
+    now = anow;\r
+\r
+    while (1) {\r
+       first = (struct timer *)index234(timers, 0);\r
+\r
+       if (!first)\r
+           return FALSE;              /* no timers remaining */\r
+\r
+       if (find234(timer_contexts, first->ctx, NULL) == NULL) {\r
+           /*\r
+            * This timer belongs to a context that has been\r
+            * expired. Delete it without running.\r
+            */\r
+           delpos234(timers, 0);\r
+           sfree(first);\r
+       } else if (first->now - now <= 0) {\r
+           /*\r
+            * This timer is active and has reached its running\r
+            * time. Run it.\r
+            */\r
+           delpos234(timers, 0);\r
+           first->fn(first->ctx, first->now);\r
+           sfree(first);\r
+       } else {\r
+           /*\r
+            * This is the first still-active timer that is in the\r
+            * future. Return how long it has yet to go.\r
+            */\r
+           *next = first->now;\r
+           return TRUE;\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Call to expire all timers associated with a given context.\r
+ */\r
+void expire_timer_context(void *ctx)\r
+{\r
+    init_timers();\r
+\r
+    /*\r
+     * We don't bother to check the return value; if the context\r
+     * already wasn't in the tree (presumably because no timers\r
+     * ever actually got scheduled for it) then that's fine and we\r
+     * simply don't need to do anything.\r
+     */\r
+    del234(timer_contexts, ctx);\r
+}\r
diff --git a/putty/TREE234.C b/putty/TREE234.C
new file mode 100644 (file)
index 0000000..4e2da9d
--- /dev/null
@@ -0,0 +1,1479 @@
+/*\r
+ * tree234.c: reasonably generic counted 2-3-4 tree routines.\r
+ * \r
+ * This file is copyright 1999-2001 Simon Tatham.\r
+ * \r
+ * Permission is hereby granted, free of charge, to any person\r
+ * obtaining a copy of this software and associated documentation\r
+ * files (the "Software"), to deal in the Software without\r
+ * restriction, including without limitation the rights to use,\r
+ * copy, modify, merge, publish, distribute, sublicense, and/or\r
+ * sell copies of the Software, and to permit persons to whom the\r
+ * Software is furnished to do so, subject to the following\r
+ * conditions:\r
+ * \r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ * \r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+ * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+#include "puttymem.h"\r
+#include "tree234.h"\r
+\r
+#ifdef TEST\r
+#define LOG(x) (printf x)\r
+#else\r
+#define LOG(x)\r
+#endif\r
+\r
+typedef struct node234_Tag node234;\r
+\r
+struct tree234_Tag {\r
+    node234 *root;\r
+    cmpfn234 cmp;\r
+};\r
+\r
+struct node234_Tag {\r
+    node234 *parent;\r
+    node234 *kids[4];\r
+    int counts[4];\r
+    void *elems[3];\r
+};\r
+\r
+/*\r
+ * Create a 2-3-4 tree.\r
+ */\r
+tree234 *newtree234(cmpfn234 cmp)\r
+{\r
+    tree234 *ret = snew(tree234);\r
+    LOG(("created tree %p\n", ret));\r
+    ret->root = NULL;\r
+    ret->cmp = cmp;\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Free a 2-3-4 tree (not including freeing the elements).\r
+ */\r
+static void freenode234(node234 * n)\r
+{\r
+    if (!n)\r
+       return;\r
+    freenode234(n->kids[0]);\r
+    freenode234(n->kids[1]);\r
+    freenode234(n->kids[2]);\r
+    freenode234(n->kids[3]);\r
+    sfree(n);\r
+}\r
+\r
+void freetree234(tree234 * t)\r
+{\r
+    freenode234(t->root);\r
+    sfree(t);\r
+}\r
+\r
+/*\r
+ * Internal function to count a node.\r
+ */\r
+static int countnode234(node234 * n)\r
+{\r
+    int count = 0;\r
+    int i;\r
+    if (!n)\r
+       return 0;\r
+    for (i = 0; i < 4; i++)\r
+       count += n->counts[i];\r
+    for (i = 0; i < 3; i++)\r
+       if (n->elems[i])\r
+           count++;\r
+    return count;\r
+}\r
+\r
+/*\r
+ * Count the elements in a tree.\r
+ */\r
+int count234(tree234 * t)\r
+{\r
+    if (t->root)\r
+       return countnode234(t->root);\r
+    else\r
+       return 0;\r
+}\r
+\r
+/*\r
+ * Add an element e to a 2-3-4 tree t. Returns e on success, or if\r
+ * an existing element compares equal, returns that.\r
+ */\r
+static void *add234_internal(tree234 * t, void *e, int index)\r
+{\r
+    node234 *n, **np, *left, *right;\r
+    void *orig_e = e;\r
+    int c, lcount, rcount;\r
+\r
+    LOG(("adding node %p to tree %p\n", e, t));\r
+    if (t->root == NULL) {\r
+       t->root = snew(node234);\r
+       t->root->elems[1] = t->root->elems[2] = NULL;\r
+       t->root->kids[0] = t->root->kids[1] = NULL;\r
+       t->root->kids[2] = t->root->kids[3] = NULL;\r
+       t->root->counts[0] = t->root->counts[1] = 0;\r
+       t->root->counts[2] = t->root->counts[3] = 0;\r
+       t->root->parent = NULL;\r
+       t->root->elems[0] = e;\r
+       LOG(("  created root %p\n", t->root));\r
+       return orig_e;\r
+    }\r
+\r
+    n = NULL; /* placate gcc; will always be set below since t->root != NULL */\r
+    np = &t->root;\r
+    while (*np) {\r
+       int childnum;\r
+       n = *np;\r
+       LOG(("  node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n",\r
+            n,\r
+            n->kids[0], n->counts[0], n->elems[0],\r
+            n->kids[1], n->counts[1], n->elems[1],\r
+            n->kids[2], n->counts[2], n->elems[2],\r
+            n->kids[3], n->counts[3]));\r
+       if (index >= 0) {\r
+           if (!n->kids[0]) {\r
+               /*\r
+                * Leaf node. We want to insert at kid position\r
+                * equal to the index:\r
+                * \r
+                *   0 A 1 B 2 C 3\r
+                */\r
+               childnum = index;\r
+           } else {\r
+               /*\r
+                * Internal node. We always descend through it (add\r
+                * always starts at the bottom, never in the\r
+                * middle).\r
+                */\r
+               do {                   /* this is a do ... while (0) to allow `break' */\r
+                   if (index <= n->counts[0]) {\r
+                       childnum = 0;\r
+                       break;\r
+                   }\r
+                   index -= n->counts[0] + 1;\r
+                   if (index <= n->counts[1]) {\r
+                       childnum = 1;\r
+                       break;\r
+                   }\r
+                   index -= n->counts[1] + 1;\r
+                   if (index <= n->counts[2]) {\r
+                       childnum = 2;\r
+                       break;\r
+                   }\r
+                   index -= n->counts[2] + 1;\r
+                   if (index <= n->counts[3]) {\r
+                       childnum = 3;\r
+                       break;\r
+                   }\r
+                   return NULL;       /* error: index out of range */\r
+               } while (0);\r
+           }\r
+       } else {\r
+           if ((c = t->cmp(e, n->elems[0])) < 0)\r
+               childnum = 0;\r
+           else if (c == 0)\r
+               return n->elems[0];    /* already exists */\r
+           else if (n->elems[1] == NULL\r
+                    || (c = t->cmp(e, n->elems[1])) < 0) childnum = 1;\r
+           else if (c == 0)\r
+               return n->elems[1];    /* already exists */\r
+           else if (n->elems[2] == NULL\r
+                    || (c = t->cmp(e, n->elems[2])) < 0) childnum = 2;\r
+           else if (c == 0)\r
+               return n->elems[2];    /* already exists */\r
+           else\r
+               childnum = 3;\r
+       }\r
+       np = &n->kids[childnum];\r
+       LOG(("  moving to child %d (%p)\n", childnum, *np));\r
+    }\r
+\r
+    /*\r
+     * We need to insert the new element in n at position np.\r
+     */\r
+    left = NULL;\r
+    lcount = 0;\r
+    right = NULL;\r
+    rcount = 0;\r
+    while (n) {\r
+       LOG(("  at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n",\r
+            n,\r
+            n->kids[0], n->counts[0], n->elems[0],\r
+            n->kids[1], n->counts[1], n->elems[1],\r
+            n->kids[2], n->counts[2], n->elems[2],\r
+            n->kids[3], n->counts[3]));\r
+       LOG(("  need to insert %p/%d [%p] %p/%d at position %d\n",\r
+            left, lcount, e, right, rcount, np - n->kids));\r
+       if (n->elems[1] == NULL) {\r
+           /*\r
+            * Insert in a 2-node; simple.\r
+            */\r
+           if (np == &n->kids[0]) {\r
+               LOG(("  inserting on left of 2-node\n"));\r
+               n->kids[2] = n->kids[1];\r
+               n->counts[2] = n->counts[1];\r
+               n->elems[1] = n->elems[0];\r
+               n->kids[1] = right;\r
+               n->counts[1] = rcount;\r
+               n->elems[0] = e;\r
+               n->kids[0] = left;\r
+               n->counts[0] = lcount;\r
+           } else {                   /* np == &n->kids[1] */\r
+               LOG(("  inserting on right of 2-node\n"));\r
+               n->kids[2] = right;\r
+               n->counts[2] = rcount;\r
+               n->elems[1] = e;\r
+               n->kids[1] = left;\r
+               n->counts[1] = lcount;\r
+           }\r
+           if (n->kids[0])\r
+               n->kids[0]->parent = n;\r
+           if (n->kids[1])\r
+               n->kids[1]->parent = n;\r
+           if (n->kids[2])\r
+               n->kids[2]->parent = n;\r
+           LOG(("  done\n"));\r
+           break;\r
+       } else if (n->elems[2] == NULL) {\r
+           /*\r
+            * Insert in a 3-node; simple.\r
+            */\r
+           if (np == &n->kids[0]) {\r
+               LOG(("  inserting on left of 3-node\n"));\r
+               n->kids[3] = n->kids[2];\r
+               n->counts[3] = n->counts[2];\r
+               n->elems[2] = n->elems[1];\r
+               n->kids[2] = n->kids[1];\r
+               n->counts[2] = n->counts[1];\r
+               n->elems[1] = n->elems[0];\r
+               n->kids[1] = right;\r
+               n->counts[1] = rcount;\r
+               n->elems[0] = e;\r
+               n->kids[0] = left;\r
+               n->counts[0] = lcount;\r
+           } else if (np == &n->kids[1]) {\r
+               LOG(("  inserting in middle of 3-node\n"));\r
+               n->kids[3] = n->kids[2];\r
+               n->counts[3] = n->counts[2];\r
+               n->elems[2] = n->elems[1];\r
+               n->kids[2] = right;\r
+               n->counts[2] = rcount;\r
+               n->elems[1] = e;\r
+               n->kids[1] = left;\r
+               n->counts[1] = lcount;\r
+           } else {                   /* np == &n->kids[2] */\r
+               LOG(("  inserting on right of 3-node\n"));\r
+               n->kids[3] = right;\r
+               n->counts[3] = rcount;\r
+               n->elems[2] = e;\r
+               n->kids[2] = left;\r
+               n->counts[2] = lcount;\r
+           }\r
+           if (n->kids[0])\r
+               n->kids[0]->parent = n;\r
+           if (n->kids[1])\r
+               n->kids[1]->parent = n;\r
+           if (n->kids[2])\r
+               n->kids[2]->parent = n;\r
+           if (n->kids[3])\r
+               n->kids[3]->parent = n;\r
+           LOG(("  done\n"));\r
+           break;\r
+       } else {\r
+           node234 *m = snew(node234);\r
+           m->parent = n->parent;\r
+           LOG(("  splitting a 4-node; created new node %p\n", m));\r
+           /*\r
+            * Insert in a 4-node; split into a 2-node and a\r
+            * 3-node, and move focus up a level.\r
+            * \r
+            * I don't think it matters which way round we put the\r
+            * 2 and the 3. For simplicity, we'll put the 3 first\r
+            * always.\r
+            */\r
+           if (np == &n->kids[0]) {\r
+               m->kids[0] = left;\r
+               m->counts[0] = lcount;\r
+               m->elems[0] = e;\r
+               m->kids[1] = right;\r
+               m->counts[1] = rcount;\r
+               m->elems[1] = n->elems[0];\r
+               m->kids[2] = n->kids[1];\r
+               m->counts[2] = n->counts[1];\r
+               e = n->elems[1];\r
+               n->kids[0] = n->kids[2];\r
+               n->counts[0] = n->counts[2];\r
+               n->elems[0] = n->elems[2];\r
+               n->kids[1] = n->kids[3];\r
+               n->counts[1] = n->counts[3];\r
+           } else if (np == &n->kids[1]) {\r
+               m->kids[0] = n->kids[0];\r
+               m->counts[0] = n->counts[0];\r
+               m->elems[0] = n->elems[0];\r
+               m->kids[1] = left;\r
+               m->counts[1] = lcount;\r
+               m->elems[1] = e;\r
+               m->kids[2] = right;\r
+               m->counts[2] = rcount;\r
+               e = n->elems[1];\r
+               n->kids[0] = n->kids[2];\r
+               n->counts[0] = n->counts[2];\r
+               n->elems[0] = n->elems[2];\r
+               n->kids[1] = n->kids[3];\r
+               n->counts[1] = n->counts[3];\r
+           } else if (np == &n->kids[2]) {\r
+               m->kids[0] = n->kids[0];\r
+               m->counts[0] = n->counts[0];\r
+               m->elems[0] = n->elems[0];\r
+               m->kids[1] = n->kids[1];\r
+               m->counts[1] = n->counts[1];\r
+               m->elems[1] = n->elems[1];\r
+               m->kids[2] = left;\r
+               m->counts[2] = lcount;\r
+               /* e = e; */\r
+               n->kids[0] = right;\r
+               n->counts[0] = rcount;\r
+               n->elems[0] = n->elems[2];\r
+               n->kids[1] = n->kids[3];\r
+               n->counts[1] = n->counts[3];\r
+           } else {                   /* np == &n->kids[3] */\r
+               m->kids[0] = n->kids[0];\r
+               m->counts[0] = n->counts[0];\r
+               m->elems[0] = n->elems[0];\r
+               m->kids[1] = n->kids[1];\r
+               m->counts[1] = n->counts[1];\r
+               m->elems[1] = n->elems[1];\r
+               m->kids[2] = n->kids[2];\r
+               m->counts[2] = n->counts[2];\r
+               n->kids[0] = left;\r
+               n->counts[0] = lcount;\r
+               n->elems[0] = e;\r
+               n->kids[1] = right;\r
+               n->counts[1] = rcount;\r
+               e = n->elems[2];\r
+           }\r
+           m->kids[3] = n->kids[3] = n->kids[2] = NULL;\r
+           m->counts[3] = n->counts[3] = n->counts[2] = 0;\r
+           m->elems[2] = n->elems[2] = n->elems[1] = NULL;\r
+           if (m->kids[0])\r
+               m->kids[0]->parent = m;\r
+           if (m->kids[1])\r
+               m->kids[1]->parent = m;\r
+           if (m->kids[2])\r
+               m->kids[2]->parent = m;\r
+           if (n->kids[0])\r
+               n->kids[0]->parent = n;\r
+           if (n->kids[1])\r
+               n->kids[1]->parent = n;\r
+           LOG(("  left (%p): %p/%d [%p] %p/%d [%p] %p/%d\n", m,\r
+                m->kids[0], m->counts[0], m->elems[0],\r
+                m->kids[1], m->counts[1], m->elems[1],\r
+                m->kids[2], m->counts[2]));\r
+           LOG(("  right (%p): %p/%d [%p] %p/%d\n", n,\r
+                n->kids[0], n->counts[0], n->elems[0],\r
+                n->kids[1], n->counts[1]));\r
+           left = m;\r
+           lcount = countnode234(left);\r
+           right = n;\r
+           rcount = countnode234(right);\r
+       }\r
+       if (n->parent)\r
+           np = (n->parent->kids[0] == n ? &n->parent->kids[0] :\r
+                 n->parent->kids[1] == n ? &n->parent->kids[1] :\r
+                 n->parent->kids[2] == n ? &n->parent->kids[2] :\r
+                 &n->parent->kids[3]);\r
+       n = n->parent;\r
+    }\r
+\r
+    /*\r
+     * If we've come out of here by `break', n will still be\r
+     * non-NULL and all we need to do is go back up the tree\r
+     * updating counts. If we've come here because n is NULL, we\r
+     * need to create a new root for the tree because the old one\r
+     * has just split into two. */\r
+    if (n) {\r
+       while (n->parent) {\r
+           int count = countnode234(n);\r
+           int childnum;\r
+           childnum = (n->parent->kids[0] == n ? 0 :\r
+                       n->parent->kids[1] == n ? 1 :\r
+                       n->parent->kids[2] == n ? 2 : 3);\r
+           n->parent->counts[childnum] = count;\r
+           n = n->parent;\r
+       }\r
+    } else {\r
+       LOG(("  root is overloaded, split into two\n"));\r
+       t->root = snew(node234);\r
+       t->root->kids[0] = left;\r
+       t->root->counts[0] = lcount;\r
+       t->root->elems[0] = e;\r
+       t->root->kids[1] = right;\r
+       t->root->counts[1] = rcount;\r
+       t->root->elems[1] = NULL;\r
+       t->root->kids[2] = NULL;\r
+       t->root->counts[2] = 0;\r
+       t->root->elems[2] = NULL;\r
+       t->root->kids[3] = NULL;\r
+       t->root->counts[3] = 0;\r
+       t->root->parent = NULL;\r
+       if (t->root->kids[0])\r
+           t->root->kids[0]->parent = t->root;\r
+       if (t->root->kids[1])\r
+           t->root->kids[1]->parent = t->root;\r
+       LOG(("  new root is %p/%d [%p] %p/%d\n",\r
+            t->root->kids[0], t->root->counts[0],\r
+            t->root->elems[0], t->root->kids[1], t->root->counts[1]));\r
+    }\r
+\r
+    return orig_e;\r
+}\r
+\r
+void *add234(tree234 * t, void *e)\r
+{\r
+    if (!t->cmp)                      /* tree is unsorted */\r
+       return NULL;\r
+\r
+    return add234_internal(t, e, -1);\r
+}\r
+void *addpos234(tree234 * t, void *e, int index)\r
+{\r
+    if (index < 0 ||                  /* index out of range */\r
+       t->cmp)                        /* tree is sorted */\r
+       return NULL;                   /* return failure */\r
+\r
+    return add234_internal(t, e, index);       /* this checks the upper bound */\r
+}\r
+\r
+/*\r
+ * Look up the element at a given numeric index in a 2-3-4 tree.\r
+ * Returns NULL if the index is out of range.\r
+ */\r
+void *index234(tree234 * t, int index)\r
+{\r
+    node234 *n;\r
+\r
+    if (!t->root)\r
+       return NULL;                   /* tree is empty */\r
+\r
+    if (index < 0 || index >= countnode234(t->root))\r
+       return NULL;                   /* out of range */\r
+\r
+    n = t->root;\r
+\r
+    while (n) {\r
+       if (index < n->counts[0])\r
+           n = n->kids[0];\r
+       else if (index -= n->counts[0] + 1, index < 0)\r
+           return n->elems[0];\r
+       else if (index < n->counts[1])\r
+           n = n->kids[1];\r
+       else if (index -= n->counts[1] + 1, index < 0)\r
+           return n->elems[1];\r
+       else if (index < n->counts[2])\r
+           n = n->kids[2];\r
+       else if (index -= n->counts[2] + 1, index < 0)\r
+           return n->elems[2];\r
+       else\r
+           n = n->kids[3];\r
+    }\r
+\r
+    /* We shouldn't ever get here. I wonder how we did. */\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not\r
+ * found. e is always passed as the first argument to cmp, so cmp\r
+ * can be an asymmetric function if desired. cmp can also be passed\r
+ * as NULL, in which case the compare function from the tree proper\r
+ * will be used.\r
+ */\r
+void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp,\r
+                   int relation, int *index)\r
+{\r
+    node234 *n;\r
+    void *ret;\r
+    int c;\r
+    int idx, ecount, kcount, cmpret;\r
+\r
+    if (t->root == NULL)\r
+       return NULL;\r
+\r
+    if (cmp == NULL)\r
+       cmp = t->cmp;\r
+\r
+    n = t->root;\r
+    /*\r
+     * Attempt to find the element itself.\r
+     */\r
+    idx = 0;\r
+    ecount = -1;\r
+    /*\r
+     * Prepare a fake `cmp' result if e is NULL.\r
+     */\r
+    cmpret = 0;\r
+    if (e == NULL) {\r
+       assert(relation == REL234_LT || relation == REL234_GT);\r
+       if (relation == REL234_LT)\r
+           cmpret = +1;               /* e is a max: always greater */\r
+       else if (relation == REL234_GT)\r
+           cmpret = -1;               /* e is a min: always smaller */\r
+    }\r
+    while (1) {\r
+       for (kcount = 0; kcount < 4; kcount++) {\r
+           if (kcount >= 3 || n->elems[kcount] == NULL ||\r
+               (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) {\r
+               break;\r
+           }\r
+           if (n->kids[kcount])\r
+               idx += n->counts[kcount];\r
+           if (c == 0) {\r
+               ecount = kcount;\r
+               break;\r
+           }\r
+           idx++;\r
+       }\r
+       if (ecount >= 0)\r
+           break;\r
+       if (n->kids[kcount])\r
+           n = n->kids[kcount];\r
+       else\r
+           break;\r
+    }\r
+\r
+    if (ecount >= 0) {\r
+       /*\r
+        * We have found the element we're looking for. It's\r
+        * n->elems[ecount], at tree index idx. If our search\r
+        * relation is EQ, LE or GE we can now go home.\r
+        */\r
+       if (relation != REL234_LT && relation != REL234_GT) {\r
+           if (index)\r
+               *index = idx;\r
+           return n->elems[ecount];\r
+       }\r
+\r
+       /*\r
+        * Otherwise, we'll do an indexed lookup for the previous\r
+        * or next element. (It would be perfectly possible to\r
+        * implement these search types in a non-counted tree by\r
+        * going back up from where we are, but far more fiddly.)\r
+        */\r
+       if (relation == REL234_LT)\r
+           idx--;\r
+       else\r
+           idx++;\r
+    } else {\r
+       /*\r
+        * We've found our way to the bottom of the tree and we\r
+        * know where we would insert this node if we wanted to:\r
+        * we'd put it in in place of the (empty) subtree\r
+        * n->kids[kcount], and it would have index idx\r
+        * \r
+        * But the actual element isn't there. So if our search\r
+        * relation is EQ, we're doomed.\r
+        */\r
+       if (relation == REL234_EQ)\r
+           return NULL;\r
+\r
+       /*\r
+        * Otherwise, we must do an index lookup for index idx-1\r
+        * (if we're going left - LE or LT) or index idx (if we're\r
+        * going right - GE or GT).\r
+        */\r
+       if (relation == REL234_LT || relation == REL234_LE) {\r
+           idx--;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * We know the index of the element we want; just call index234\r
+     * to do the rest. This will return NULL if the index is out of\r
+     * bounds, which is exactly what we want.\r
+     */\r
+    ret = index234(t, idx);\r
+    if (ret && index)\r
+       *index = idx;\r
+    return ret;\r
+}\r
+void *find234(tree234 * t, void *e, cmpfn234 cmp)\r
+{\r
+    return findrelpos234(t, e, cmp, REL234_EQ, NULL);\r
+}\r
+void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation)\r
+{\r
+    return findrelpos234(t, e, cmp, relation, NULL);\r
+}\r
+void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index)\r
+{\r
+    return findrelpos234(t, e, cmp, REL234_EQ, index);\r
+}\r
+\r
+/*\r
+ * Delete an element e in a 2-3-4 tree. Does not free the element,\r
+ * merely removes all links to it from the tree nodes.\r
+ */\r
+static void *delpos234_internal(tree234 * t, int index)\r
+{\r
+    node234 *n;\r
+    void *retval;\r
+    int ei = -1;\r
+\r
+    retval = 0;\r
+\r
+    n = t->root;\r
+    LOG(("deleting item %d from tree %p\n", index, t));\r
+    while (1) {\r
+       while (n) {\r
+           int ki;\r
+           node234 *sub;\r
+\r
+           LOG(\r
+               ("  node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%d\n",\r
+                n, n->kids[0], n->counts[0], n->elems[0], n->kids[1],\r
+                n->counts[1], n->elems[1], n->kids[2], n->counts[2],\r
+                n->elems[2], n->kids[3], n->counts[3], index));\r
+           if (index < n->counts[0]) {\r
+               ki = 0;\r
+           } else if (index -= n->counts[0] + 1, index < 0) {\r
+               ei = 0;\r
+               break;\r
+           } else if (index < n->counts[1]) {\r
+               ki = 1;\r
+           } else if (index -= n->counts[1] + 1, index < 0) {\r
+               ei = 1;\r
+               break;\r
+           } else if (index < n->counts[2]) {\r
+               ki = 2;\r
+           } else if (index -= n->counts[2] + 1, index < 0) {\r
+               ei = 2;\r
+               break;\r
+           } else {\r
+               ki = 3;\r
+           }\r
+           /*\r
+            * Recurse down to subtree ki. If it has only one element,\r
+            * we have to do some transformation to start with.\r
+            */\r
+           LOG(("  moving to subtree %d\n", ki));\r
+           sub = n->kids[ki];\r
+           if (!sub->elems[1]) {\r
+               LOG(("  subtree has only one element!\n", ki));\r
+               if (ki > 0 && n->kids[ki - 1]->elems[1]) {\r
+                   /*\r
+                    * Case 3a, left-handed variant. Child ki has\r
+                    * only one element, but child ki-1 has two or\r
+                    * more. So we need to move a subtree from ki-1\r
+                    * to ki.\r
+                    * \r
+                    *                . C .                     . B .\r
+                    *               /     \     ->            /     \\r
+                    * [more] a A b B c   d D e      [more] a A b   c C d D e\r
+                    */\r
+                   node234 *sib = n->kids[ki - 1];\r
+                   int lastelem = (sib->elems[2] ? 2 :\r
+                                   sib->elems[1] ? 1 : 0);\r
+                   sub->kids[2] = sub->kids[1];\r
+                   sub->counts[2] = sub->counts[1];\r
+                   sub->elems[1] = sub->elems[0];\r
+                   sub->kids[1] = sub->kids[0];\r
+                   sub->counts[1] = sub->counts[0];\r
+                   sub->elems[0] = n->elems[ki - 1];\r
+                   sub->kids[0] = sib->kids[lastelem + 1];\r
+                   sub->counts[0] = sib->counts[lastelem + 1];\r
+                   if (sub->kids[0])\r
+                       sub->kids[0]->parent = sub;\r
+                   n->elems[ki - 1] = sib->elems[lastelem];\r
+                   sib->kids[lastelem + 1] = NULL;\r
+                   sib->counts[lastelem + 1] = 0;\r
+                   sib->elems[lastelem] = NULL;\r
+                   n->counts[ki] = countnode234(sub);\r
+                   LOG(("  case 3a left\n"));\r
+                   LOG(\r
+                       ("  index and left subtree count before adjustment: %d, %d\n",\r
+                        index, n->counts[ki - 1]));\r
+                   index += n->counts[ki - 1];\r
+                   n->counts[ki - 1] = countnode234(sib);\r
+                   index -= n->counts[ki - 1];\r
+                   LOG(\r
+                       ("  index and left subtree count after adjustment: %d, %d\n",\r
+                        index, n->counts[ki - 1]));\r
+               } else if (ki < 3 && n->kids[ki + 1]\r
+                          && n->kids[ki + 1]->elems[1]) {\r
+                   /*\r
+                    * Case 3a, right-handed variant. ki has only\r
+                    * one element but ki+1 has two or more. Move a\r
+                    * subtree from ki+1 to ki.\r
+                    * \r
+                    *      . B .                             . C .\r
+                    *     /     \                ->         /     \\r
+                    *  a A b   c C d D e [more]      a A b B c   d D e [more]\r
+                    */\r
+                   node234 *sib = n->kids[ki + 1];\r
+                   int j;\r
+                   sub->elems[1] = n->elems[ki];\r
+                   sub->kids[2] = sib->kids[0];\r
+                   sub->counts[2] = sib->counts[0];\r
+                   if (sub->kids[2])\r
+                       sub->kids[2]->parent = sub;\r
+                   n->elems[ki] = sib->elems[0];\r
+                   sib->kids[0] = sib->kids[1];\r
+                   sib->counts[0] = sib->counts[1];\r
+                   for (j = 0; j < 2 && sib->elems[j + 1]; j++) {\r
+                       sib->kids[j + 1] = sib->kids[j + 2];\r
+                       sib->counts[j + 1] = sib->counts[j + 2];\r
+                       sib->elems[j] = sib->elems[j + 1];\r
+                   }\r
+                   sib->kids[j + 1] = NULL;\r
+                   sib->counts[j + 1] = 0;\r
+                   sib->elems[j] = NULL;\r
+                   n->counts[ki] = countnode234(sub);\r
+                   n->counts[ki + 1] = countnode234(sib);\r
+                   LOG(("  case 3a right\n"));\r
+               } else {\r
+                   /*\r
+                    * Case 3b. ki has only one element, and has no\r
+                    * neighbour with more than one. So pick a\r
+                    * neighbour and merge it with ki, taking an\r
+                    * element down from n to go in the middle.\r
+                    *\r
+                    *      . B .                .\r
+                    *     /     \     ->        |\r
+                    *  a A b   c C d      a A b B c C d\r
+                    * \r
+                    * (Since at all points we have avoided\r
+                    * descending to a node with only one element,\r
+                    * we can be sure that n is not reduced to\r
+                    * nothingness by this move, _unless_ it was\r
+                    * the very first node, ie the root of the\r
+                    * tree. In that case we remove the now-empty\r
+                    * root and replace it with its single large\r
+                    * child as shown.)\r
+                    */\r
+                   node234 *sib;\r
+                   int j;\r
+\r
+                   if (ki > 0) {\r
+                       ki--;\r
+                       index += n->counts[ki] + 1;\r
+                   }\r
+                   sib = n->kids[ki];\r
+                   sub = n->kids[ki + 1];\r
+\r
+                   sub->kids[3] = sub->kids[1];\r
+                   sub->counts[3] = sub->counts[1];\r
+                   sub->elems[2] = sub->elems[0];\r
+                   sub->kids[2] = sub->kids[0];\r
+                   sub->counts[2] = sub->counts[0];\r
+                   sub->elems[1] = n->elems[ki];\r
+                   sub->kids[1] = sib->kids[1];\r
+                   sub->counts[1] = sib->counts[1];\r
+                   if (sub->kids[1])\r
+                       sub->kids[1]->parent = sub;\r
+                   sub->elems[0] = sib->elems[0];\r
+                   sub->kids[0] = sib->kids[0];\r
+                   sub->counts[0] = sib->counts[0];\r
+                   if (sub->kids[0])\r
+                       sub->kids[0]->parent = sub;\r
+\r
+                   n->counts[ki + 1] = countnode234(sub);\r
+\r
+                   sfree(sib);\r
+\r
+                   /*\r
+                    * That's built the big node in sub. Now we\r
+                    * need to remove the reference to sib in n.\r
+                    */\r
+                   for (j = ki; j < 3 && n->kids[j + 1]; j++) {\r
+                       n->kids[j] = n->kids[j + 1];\r
+                       n->counts[j] = n->counts[j + 1];\r
+                       n->elems[j] = j < 2 ? n->elems[j + 1] : NULL;\r
+                   }\r
+                   n->kids[j] = NULL;\r
+                   n->counts[j] = 0;\r
+                   if (j < 3)\r
+                       n->elems[j] = NULL;\r
+                   LOG(("  case 3b ki=%d\n", ki));\r
+\r
+                   if (!n->elems[0]) {\r
+                       /*\r
+                        * The root is empty and needs to be\r
+                        * removed.\r
+                        */\r
+                       LOG(("  shifting root!\n"));\r
+                       t->root = sub;\r
+                       sub->parent = NULL;\r
+                       sfree(n);\r
+                   }\r
+               }\r
+           }\r
+           n = sub;\r
+       }\r
+       if (!retval)\r
+           retval = n->elems[ei];\r
+\r
+       if (ei == -1)\r
+           return NULL;               /* although this shouldn't happen */\r
+\r
+       /*\r
+        * Treat special case: this is the one remaining item in\r
+        * the tree. n is the tree root (no parent), has one\r
+        * element (no elems[1]), and has no kids (no kids[0]).\r
+        */\r
+       if (!n->parent && !n->elems[1] && !n->kids[0]) {\r
+           LOG(("  removed last element in tree\n"));\r
+           sfree(n);\r
+           t->root = NULL;\r
+           return retval;\r
+       }\r
+\r
+       /*\r
+        * Now we have the element we want, as n->elems[ei], and we\r
+        * have also arranged for that element not to be the only\r
+        * one in its node. So...\r
+        */\r
+\r
+       if (!n->kids[0] && n->elems[1]) {\r
+           /*\r
+            * Case 1. n is a leaf node with more than one element,\r
+            * so it's _really easy_. Just delete the thing and\r
+            * we're done.\r
+            */\r
+           int i;\r
+           LOG(("  case 1\n"));\r
+           for (i = ei; i < 2 && n->elems[i + 1]; i++)\r
+               n->elems[i] = n->elems[i + 1];\r
+           n->elems[i] = NULL;\r
+           /*\r
+            * Having done that to the leaf node, we now go back up\r
+            * the tree fixing the counts.\r
+            */\r
+           while (n->parent) {\r
+               int childnum;\r
+               childnum = (n->parent->kids[0] == n ? 0 :\r
+                           n->parent->kids[1] == n ? 1 :\r
+                           n->parent->kids[2] == n ? 2 : 3);\r
+               n->parent->counts[childnum]--;\r
+               n = n->parent;\r
+           }\r
+           return retval;             /* finished! */\r
+       } else if (n->kids[ei]->elems[1]) {\r
+           /*\r
+            * Case 2a. n is an internal node, and the root of the\r
+            * subtree to the left of e has more than one element.\r
+            * So find the predecessor p to e (ie the largest node\r
+            * in that subtree), place it where e currently is, and\r
+            * then start the deletion process over again on the\r
+            * subtree with p as target.\r
+            */\r
+           node234 *m = n->kids[ei];\r
+           void *target;\r
+           LOG(("  case 2a\n"));\r
+           while (m->kids[0]) {\r
+               m = (m->kids[3] ? m->kids[3] :\r
+                    m->kids[2] ? m->kids[2] :\r
+                    m->kids[1] ? m->kids[1] : m->kids[0]);\r
+           }\r
+           target = (m->elems[2] ? m->elems[2] :\r
+                     m->elems[1] ? m->elems[1] : m->elems[0]);\r
+           n->elems[ei] = target;\r
+           index = n->counts[ei] - 1;\r
+           n = n->kids[ei];\r
+       } else if (n->kids[ei + 1]->elems[1]) {\r
+           /*\r
+            * Case 2b, symmetric to 2a but s/left/right/ and\r
+            * s/predecessor/successor/. (And s/largest/smallest/).\r
+            */\r
+           node234 *m = n->kids[ei + 1];\r
+           void *target;\r
+           LOG(("  case 2b\n"));\r
+           while (m->kids[0]) {\r
+               m = m->kids[0];\r
+           }\r
+           target = m->elems[0];\r
+           n->elems[ei] = target;\r
+           n = n->kids[ei + 1];\r
+           index = 0;\r
+       } else {\r
+           /*\r
+            * Case 2c. n is an internal node, and the subtrees to\r
+            * the left and right of e both have only one element.\r
+            * So combine the two subnodes into a single big node\r
+            * with their own elements on the left and right and e\r
+            * in the middle, then restart the deletion process on\r
+            * that subtree, with e still as target.\r
+            */\r
+           node234 *a = n->kids[ei], *b = n->kids[ei + 1];\r
+           int j;\r
+\r
+           LOG(("  case 2c\n"));\r
+           a->elems[1] = n->elems[ei];\r
+           a->kids[2] = b->kids[0];\r
+           a->counts[2] = b->counts[0];\r
+           if (a->kids[2])\r
+               a->kids[2]->parent = a;\r
+           a->elems[2] = b->elems[0];\r
+           a->kids[3] = b->kids[1];\r
+           a->counts[3] = b->counts[1];\r
+           if (a->kids[3])\r
+               a->kids[3]->parent = a;\r
+           sfree(b);\r
+           n->counts[ei] = countnode234(a);\r
+           /*\r
+            * That's built the big node in a, and destroyed b. Now\r
+            * remove the reference to b (and e) in n.\r
+            */\r
+           for (j = ei; j < 2 && n->elems[j + 1]; j++) {\r
+               n->elems[j] = n->elems[j + 1];\r
+               n->kids[j + 1] = n->kids[j + 2];\r
+               n->counts[j + 1] = n->counts[j + 2];\r
+           }\r
+           n->elems[j] = NULL;\r
+           n->kids[j + 1] = NULL;\r
+           n->counts[j + 1] = 0;\r
+           /*\r
+            * It's possible, in this case, that we've just removed\r
+            * the only element in the root of the tree. If so,\r
+            * shift the root.\r
+            */\r
+           if (n->elems[0] == NULL) {\r
+               LOG(("  shifting root!\n"));\r
+               t->root = a;\r
+               a->parent = NULL;\r
+               sfree(n);\r
+           }\r
+           /*\r
+            * Now go round the deletion process again, with n\r
+            * pointing at the new big node and e still the same.\r
+            */\r
+           n = a;\r
+           index = a->counts[0] + a->counts[1] + 1;\r
+       }\r
+    }\r
+}\r
+void *delpos234(tree234 * t, int index)\r
+{\r
+    if (index < 0 || index >= countnode234(t->root))\r
+       return NULL;\r
+    return delpos234_internal(t, index);\r
+}\r
+void *del234(tree234 * t, void *e)\r
+{\r
+    int index;\r
+    if (!findrelpos234(t, e, NULL, REL234_EQ, &index))\r
+       return NULL;                   /* it wasn't in there anyway */\r
+    return delpos234_internal(t, index);       /* it's there; delete it. */\r
+}\r
+\r
+#ifdef TEST\r
+\r
+/*\r
+ * Test code for the 2-3-4 tree. This code maintains an alternative\r
+ * representation of the data in the tree, in an array (using the\r
+ * obvious and slow insert and delete functions). After each tree\r
+ * operation, the verify() function is called, which ensures all\r
+ * the tree properties are preserved:\r
+ *  - node->child->parent always equals node\r
+ *  - tree->root->parent always equals NULL\r
+ *  - number of kids == 0 or number of elements + 1;\r
+ *  - tree has the same depth everywhere\r
+ *  - every node has at least one element\r
+ *  - subtree element counts are accurate\r
+ *  - any NULL kid pointer is accompanied by a zero count\r
+ *  - in a sorted tree: ordering property between elements of a\r
+ *    node and elements of its children is preserved\r
+ * and also ensures the list represented by the tree is the same\r
+ * list it should be. (This last check also doubly verifies the\r
+ * ordering properties, because the `same list it should be' is by\r
+ * definition correctly ordered. It also ensures all nodes are\r
+ * distinct, because the enum functions would get caught in a loop\r
+ * if not.)\r
+ */\r
+\r
+#include <stdarg.h>\r
+\r
+/*\r
+ * Error reporting function.\r
+ */\r
+void error(char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    printf("ERROR: ");\r
+    va_start(ap, fmt);\r
+    vfprintf(stdout, fmt, ap);\r
+    va_end(ap);\r
+    printf("\n");\r
+}\r
+\r
+/* The array representation of the data. */\r
+void **array;\r
+int arraylen, arraysize;\r
+cmpfn234 cmp;\r
+\r
+/* The tree representation of the same data. */\r
+tree234 *tree;\r
+\r
+typedef struct {\r
+    int treedepth;\r
+    int elemcount;\r
+} chkctx;\r
+\r
+int chknode(chkctx * ctx, int level, node234 * node,\r
+           void *lowbound, void *highbound)\r
+{\r
+    int nkids, nelems;\r
+    int i;\r
+    int count;\r
+\r
+    /* Count the non-NULL kids. */\r
+    for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++);\r
+    /* Ensure no kids beyond the first NULL are non-NULL. */\r
+    for (i = nkids; i < 4; i++)\r
+       if (node->kids[i]) {\r
+           error("node %p: nkids=%d but kids[%d] non-NULL",\r
+                 node, nkids, i);\r
+       } else if (node->counts[i]) {\r
+           error("node %p: kids[%d] NULL but count[%d]=%d nonzero",\r
+                 node, i, i, node->counts[i]);\r
+       }\r
+\r
+    /* Count the non-NULL elements. */\r
+    for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++);\r
+    /* Ensure no elements beyond the first NULL are non-NULL. */\r
+    for (i = nelems; i < 3; i++)\r
+       if (node->elems[i]) {\r
+           error("node %p: nelems=%d but elems[%d] non-NULL",\r
+                 node, nelems, i);\r
+       }\r
+\r
+    if (nkids == 0) {\r
+       /*\r
+        * If nkids==0, this is a leaf node; verify that the tree\r
+        * depth is the same everywhere.\r
+        */\r
+       if (ctx->treedepth < 0)\r
+           ctx->treedepth = level;    /* we didn't know the depth yet */\r
+       else if (ctx->treedepth != level)\r
+           error("node %p: leaf at depth %d, previously seen depth %d",\r
+                 node, level, ctx->treedepth);\r
+    } else {\r
+       /*\r
+        * If nkids != 0, then it should be nelems+1, unless nelems\r
+        * is 0 in which case nkids should also be 0 (and so we\r
+        * shouldn't be in this condition at all).\r
+        */\r
+       int shouldkids = (nelems ? nelems + 1 : 0);\r
+       if (nkids != shouldkids) {\r
+           error("node %p: %d elems should mean %d kids but has %d",\r
+                 node, nelems, shouldkids, nkids);\r
+       }\r
+    }\r
+\r
+    /*\r
+     * nelems should be at least 1.\r
+     */\r
+    if (nelems == 0) {\r
+       error("node %p: no elems", node, nkids);\r
+    }\r
+\r
+    /*\r
+     * Add nelems to the running element count of the whole tree.\r
+     */\r
+    ctx->elemcount += nelems;\r
+\r
+    /*\r
+     * Check ordering property: all elements should be strictly >\r
+     * lowbound, strictly < highbound, and strictly < each other in\r
+     * sequence. (lowbound and highbound are NULL at edges of tree\r
+     * - both NULL at root node - and NULL is considered to be <\r
+     * everything and > everything. IYSWIM.)\r
+     */\r
+    if (cmp) {\r
+       for (i = -1; i < nelems; i++) {\r
+           void *lower = (i == -1 ? lowbound : node->elems[i]);\r
+           void *higher =\r
+               (i + 1 == nelems ? highbound : node->elems[i + 1]);\r
+           if (lower && higher && cmp(lower, higher) >= 0) {\r
+               error("node %p: kid comparison [%d=%s,%d=%s] failed",\r
+                     node, i, lower, i + 1, higher);\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Check parent pointers: all non-NULL kids should have a\r
+     * parent pointer coming back to this node.\r
+     */\r
+    for (i = 0; i < nkids; i++)\r
+       if (node->kids[i]->parent != node) {\r
+           error("node %p kid %d: parent ptr is %p not %p",\r
+                 node, i, node->kids[i]->parent, node);\r
+       }\r
+\r
+\r
+    /*\r
+     * Now (finally!) recurse into subtrees.\r
+     */\r
+    count = nelems;\r
+\r
+    for (i = 0; i < nkids; i++) {\r
+       void *lower = (i == 0 ? lowbound : node->elems[i - 1]);\r
+       void *higher = (i >= nelems ? highbound : node->elems[i]);\r
+       int subcount =\r
+           chknode(ctx, level + 1, node->kids[i], lower, higher);\r
+       if (node->counts[i] != subcount) {\r
+           error("node %p kid %d: count says %d, subtree really has %d",\r
+                 node, i, node->counts[i], subcount);\r
+       }\r
+       count += subcount;\r
+    }\r
+\r
+    return count;\r
+}\r
+\r
+void verify(void)\r
+{\r
+    chkctx ctx;\r
+    int i;\r
+    void *p;\r
+\r
+    ctx.treedepth = -1;                       /* depth unknown yet */\r
+    ctx.elemcount = 0;                /* no elements seen yet */\r
+    /*\r
+     * Verify validity of tree properties.\r
+     */\r
+    if (tree->root) {\r
+       if (tree->root->parent != NULL)\r
+           error("root->parent is %p should be null", tree->root->parent);\r
+       chknode(&ctx, 0, tree->root, NULL, NULL);\r
+    }\r
+    printf("tree depth: %d\n", ctx.treedepth);\r
+    /*\r
+     * Enumerate the tree and ensure it matches up to the array.\r
+     */\r
+    for (i = 0; NULL != (p = index234(tree, i)); i++) {\r
+       if (i >= arraylen)\r
+           error("tree contains more than %d elements", arraylen);\r
+       if (array[i] != p)\r
+           error("enum at position %d: array says %s, tree says %s",\r
+                 i, array[i], p);\r
+    }\r
+    if (ctx.elemcount != i) {\r
+       error("tree really contains %d elements, enum gave %d",\r
+             ctx.elemcount, i);\r
+    }\r
+    if (i < arraylen) {\r
+       error("enum gave only %d elements, array has %d", i, arraylen);\r
+    }\r
+    i = count234(tree);\r
+    if (ctx.elemcount != i) {\r
+       error("tree really contains %d elements, count234 gave %d",\r
+             ctx.elemcount, i);\r
+    }\r
+}\r
+\r
+void internal_addtest(void *elem, int index, void *realret)\r
+{\r
+    int i, j;\r
+    void *retval;\r
+\r
+    if (arraysize < arraylen + 1) {\r
+       arraysize = arraylen + 1 + 256;\r
+       array = sresize(array, arraysize, void *);\r
+    }\r
+\r
+    i = index;\r
+    /* now i points to the first element >= elem */\r
+    retval = elem;                    /* expect elem returned (success) */\r
+    for (j = arraylen; j > i; j--)\r
+       array[j] = array[j - 1];\r
+    array[i] = elem;                  /* add elem to array */\r
+    arraylen++;\r
+\r
+    if (realret != retval) {\r
+       error("add: retval was %p expected %p", realret, retval);\r
+    }\r
+\r
+    verify();\r
+}\r
+\r
+void addtest(void *elem)\r
+{\r
+    int i;\r
+    void *realret;\r
+\r
+    realret = add234(tree, elem);\r
+\r
+    i = 0;\r
+    while (i < arraylen && cmp(elem, array[i]) > 0)\r
+       i++;\r
+    if (i < arraylen && !cmp(elem, array[i])) {\r
+       void *retval = array[i];       /* expect that returned not elem */\r
+       if (realret != retval) {\r
+           error("add: retval was %p expected %p", realret, retval);\r
+       }\r
+    } else\r
+       internal_addtest(elem, i, realret);\r
+}\r
+\r
+void addpostest(void *elem, int i)\r
+{\r
+    void *realret;\r
+\r
+    realret = addpos234(tree, elem, i);\r
+\r
+    internal_addtest(elem, i, realret);\r
+}\r
+\r
+void delpostest(int i)\r
+{\r
+    int index = i;\r
+    void *elem = array[i], *ret;\r
+\r
+    /* i points to the right element */\r
+    while (i < arraylen - 1) {\r
+       array[i] = array[i + 1];\r
+       i++;\r
+    }\r
+    arraylen--;                               /* delete elem from array */\r
+\r
+    if (tree->cmp)\r
+       ret = del234(tree, elem);\r
+    else\r
+       ret = delpos234(tree, index);\r
+\r
+    if (ret != elem) {\r
+       error("del returned %p, expected %p", ret, elem);\r
+    }\r
+\r
+    verify();\r
+}\r
+\r
+void deltest(void *elem)\r
+{\r
+    int i;\r
+\r
+    i = 0;\r
+    while (i < arraylen && cmp(elem, array[i]) > 0)\r
+       i++;\r
+    if (i >= arraylen || cmp(elem, array[i]) != 0)\r
+       return;                        /* don't do it! */\r
+    delpostest(i);\r
+}\r
+\r
+/* A sample data set and test utility. Designed for pseudo-randomness,\r
+ * and yet repeatability. */\r
+\r
+/*\r
+ * This random number generator uses the `portable implementation'\r
+ * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits;\r
+ * change it if not.\r
+ */\r
+int randomnumber(unsigned *seed)\r
+{\r
+    *seed *= 1103515245;\r
+    *seed += 12345;\r
+    return ((*seed) / 65536) % 32768;\r
+}\r
+\r
+int mycmp(void *av, void *bv)\r
+{\r
+    char const *a = (char const *) av;\r
+    char const *b = (char const *) bv;\r
+    return strcmp(a, b);\r
+}\r
+\r
+#define lenof(x) ( sizeof((x)) / sizeof(*(x)) )\r
+\r
+char *strings[] = {\r
+    "a", "ab", "absque", "coram", "de",\r
+    "palam", "clam", "cum", "ex", "e",\r
+    "sine", "tenus", "pro", "prae",\r
+    "banana", "carrot", "cabbage", "broccoli", "onion", "zebra",\r
+    "penguin", "blancmange", "pangolin", "whale", "hedgehog",\r
+    "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux",\r
+    "murfl", "spoo", "breen", "flarn", "octothorpe",\r
+    "snail", "tiger", "elephant", "octopus", "warthog", "armadillo",\r
+    "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin",\r
+    "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper",\r
+    "wand", "ring", "amulet"\r
+};\r
+\r
+#define NSTR lenof(strings)\r
+\r
+int findtest(void)\r
+{\r
+    const static int rels[] = {\r
+       REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT\r
+    };\r
+    const static char *const relnames[] = {\r
+       "EQ", "GE", "LE", "LT", "GT"\r
+    };\r
+    int i, j, rel, index;\r
+    char *p, *ret, *realret, *realret2;\r
+    int lo, hi, mid, c;\r
+\r
+    for (i = 0; i < NSTR; i++) {\r
+       p = strings[i];\r
+       for (j = 0; j < sizeof(rels) / sizeof(*rels); j++) {\r
+           rel = rels[j];\r
+\r
+           lo = 0;\r
+           hi = arraylen - 1;\r
+           while (lo <= hi) {\r
+               mid = (lo + hi) / 2;\r
+               c = strcmp(p, array[mid]);\r
+               if (c < 0)\r
+                   hi = mid - 1;\r
+               else if (c > 0)\r
+                   lo = mid + 1;\r
+               else\r
+                   break;\r
+           }\r
+\r
+           if (c == 0) {\r
+               if (rel == REL234_LT)\r
+                   ret = (mid > 0 ? array[--mid] : NULL);\r
+               else if (rel == REL234_GT)\r
+                   ret = (mid < arraylen - 1 ? array[++mid] : NULL);\r
+               else\r
+                   ret = array[mid];\r
+           } else {\r
+               assert(lo == hi + 1);\r
+               if (rel == REL234_LT || rel == REL234_LE) {\r
+                   mid = hi;\r
+                   ret = (hi >= 0 ? array[hi] : NULL);\r
+               } else if (rel == REL234_GT || rel == REL234_GE) {\r
+                   mid = lo;\r
+                   ret = (lo < arraylen ? array[lo] : NULL);\r
+               } else\r
+                   ret = NULL;\r
+           }\r
+\r
+           realret = findrelpos234(tree, p, NULL, rel, &index);\r
+           if (realret != ret) {\r
+               error("find(\"%s\",%s) gave %s should be %s",\r
+                     p, relnames[j], realret, ret);\r
+           }\r
+           if (realret && index != mid) {\r
+               error("find(\"%s\",%s) gave %d should be %d",\r
+                     p, relnames[j], index, mid);\r
+           }\r
+           if (realret && rel == REL234_EQ) {\r
+               realret2 = index234(tree, index);\r
+               if (realret2 != realret) {\r
+                   error("find(\"%s\",%s) gave %s(%d) but %d -> %s",\r
+                         p, relnames[j], realret, index, index, realret2);\r
+               }\r
+           }\r
+#if 0\r
+           printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j],\r
+                  realret, index);\r
+#endif\r
+       }\r
+    }\r
+\r
+    realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index);\r
+    if (arraylen && (realret != array[0] || index != 0)) {\r
+       error("find(NULL,GT) gave %s(%d) should be %s(0)",\r
+             realret, index, array[0]);\r
+    } else if (!arraylen && (realret != NULL)) {\r
+       error("find(NULL,GT) gave %s(%d) should be NULL", realret, index);\r
+    }\r
+\r
+    realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index);\r
+    if (arraylen\r
+       && (realret != array[arraylen - 1] || index != arraylen - 1)) {\r
+       error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index,\r
+             array[arraylen - 1]);\r
+    } else if (!arraylen && (realret != NULL)) {\r
+       error("find(NULL,LT) gave %s(%d) should be NULL", realret, index);\r
+    }\r
+}\r
+\r
+int main(void)\r
+{\r
+    int in[NSTR];\r
+    int i, j, k;\r
+    unsigned seed = 0;\r
+\r
+    for (i = 0; i < NSTR; i++)\r
+       in[i] = 0;\r
+    array = NULL;\r
+    arraylen = arraysize = 0;\r
+    tree = newtree234(mycmp);\r
+    cmp = mycmp;\r
+\r
+    verify();\r
+    for (i = 0; i < 10000; i++) {\r
+       j = randomnumber(&seed);\r
+       j %= NSTR;\r
+       printf("trial: %d\n", i);\r
+       if (in[j]) {\r
+           printf("deleting %s (%d)\n", strings[j], j);\r
+           deltest(strings[j]);\r
+           in[j] = 0;\r
+       } else {\r
+           printf("adding %s (%d)\n", strings[j], j);\r
+           addtest(strings[j]);\r
+           in[j] = 1;\r
+       }\r
+       findtest();\r
+    }\r
+\r
+    while (arraylen > 0) {\r
+       j = randomnumber(&seed);\r
+       j %= arraylen;\r
+       deltest(array[j]);\r
+    }\r
+\r
+    freetree234(tree);\r
+\r
+    /*\r
+     * Now try an unsorted tree. We don't really need to test\r
+     * delpos234 because we know del234 is based on it, so it's\r
+     * already been tested in the above sorted-tree code; but for\r
+     * completeness we'll use it to tear down our unsorted tree\r
+     * once we've built it.\r
+     */\r
+    tree = newtree234(NULL);\r
+    cmp = NULL;\r
+    verify();\r
+    for (i = 0; i < 1000; i++) {\r
+       printf("trial: %d\n", i);\r
+       j = randomnumber(&seed);\r
+       j %= NSTR;\r
+       k = randomnumber(&seed);\r
+       k %= count234(tree) + 1;\r
+       printf("adding string %s at index %d\n", strings[j], k);\r
+       addpostest(strings[j], k);\r
+    }\r
+    while (count234(tree) > 0) {\r
+       printf("cleanup: tree size %d\n", count234(tree));\r
+       j = randomnumber(&seed);\r
+       j %= count234(tree);\r
+       printf("deleting string %s from index %d\n", array[j], j);\r
+       delpostest(j);\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/TREE234.H b/putty/TREE234.H
new file mode 100644 (file)
index 0000000..a043f1f
--- /dev/null
@@ -0,0 +1,160 @@
+/*\r
+ * tree234.h: header defining functions in tree234.c.\r
+ * \r
+ * This file is copyright 1999-2001 Simon Tatham.\r
+ * \r
+ * Permission is hereby granted, free of charge, to any person\r
+ * obtaining a copy of this software and associated documentation\r
+ * files (the "Software"), to deal in the Software without\r
+ * restriction, including without limitation the rights to use,\r
+ * copy, modify, merge, publish, distribute, sublicense, and/or\r
+ * sell copies of the Software, and to permit persons to whom the\r
+ * Software is furnished to do so, subject to the following\r
+ * conditions:\r
+ * \r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ * \r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+ * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+\r
+#ifndef TREE234_H\r
+#define TREE234_H\r
+\r
+/*\r
+ * This typedef is opaque outside tree234.c itself.\r
+ */\r
+typedef struct tree234_Tag tree234;\r
+\r
+typedef int (*cmpfn234) (void *, void *);\r
+\r
+/*\r
+ * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and\r
+ * lookups by key will fail: you can only look things up by numeric\r
+ * index, and you have to use addpos234() and delpos234().\r
+ */\r
+tree234 *newtree234(cmpfn234 cmp);\r
+\r
+/*\r
+ * Free a 2-3-4 tree (not including freeing the elements).\r
+ */\r
+void freetree234(tree234 * t);\r
+\r
+/*\r
+ * Add an element e to a sorted 2-3-4 tree t. Returns e on success,\r
+ * or if an existing element compares equal, returns that.\r
+ */\r
+void *add234(tree234 * t, void *e);\r
+\r
+/*\r
+ * Add an element e to an unsorted 2-3-4 tree t. Returns e on\r
+ * success, NULL on failure. (Failure should only occur if the\r
+ * index is out of range or the tree is sorted.)\r
+ * \r
+ * Index range can be from 0 to the tree's current element count,\r
+ * inclusive.\r
+ */\r
+void *addpos234(tree234 * t, void *e, int index);\r
+\r
+/*\r
+ * Look up the element at a given numeric index in a 2-3-4 tree.\r
+ * Returns NULL if the index is out of range.\r
+ * \r
+ * One obvious use for this function is in iterating over the whole\r
+ * of a tree (sorted or unsorted):\r
+ * \r
+ *   for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p);\r
+ * \r
+ * or\r
+ * \r
+ *   int maxcount = count234(tree);\r
+ *   for (i = 0; i < maxcount; i++) {\r
+ *       p = index234(tree, i);\r
+ *       assert(p != NULL);\r
+ *       consume(p);\r
+ *   }\r
+ */\r
+void *index234(tree234 * t, int index);\r
+\r
+/*\r
+ * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not\r
+ * found. e is always passed as the first argument to cmp, so cmp\r
+ * can be an asymmetric function if desired. cmp can also be passed\r
+ * as NULL, in which case the compare function from the tree proper\r
+ * will be used.\r
+ * \r
+ * Three of these functions are special cases of findrelpos234. The\r
+ * non-`pos' variants lack the `index' parameter: if the parameter\r
+ * is present and non-NULL, it must point to an integer variable\r
+ * which will be filled with the numeric index of the returned\r
+ * element.\r
+ * \r
+ * The non-`rel' variants lack the `relation' parameter. This\r
+ * parameter allows you to specify what relation the element you\r
+ * provide has to the element you're looking for. This parameter\r
+ * can be:\r
+ * \r
+ *   REL234_EQ     - find only an element that compares equal to e\r
+ *   REL234_LT     - find the greatest element that compares < e\r
+ *   REL234_LE     - find the greatest element that compares <= e\r
+ *   REL234_GT     - find the smallest element that compares > e\r
+ *   REL234_GE     - find the smallest element that compares >= e\r
+ * \r
+ * Non-`rel' variants assume REL234_EQ.\r
+ * \r
+ * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be\r
+ * NULL. In this case, REL234_GT will return the smallest element\r
+ * in the tree, and REL234_LT will return the greatest. This gives\r
+ * an alternative means of iterating over a sorted tree, instead of\r
+ * using index234:\r
+ * \r
+ *   // to loop forwards\r
+ *   for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;)\r
+ *       consume(p);\r
+ * \r
+ *   // to loop backwards\r
+ *   for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;)\r
+ *       consume(p);\r
+ */\r
+enum {\r
+    REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE\r
+};\r
+void *find234(tree234 * t, void *e, cmpfn234 cmp);\r
+void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation);\r
+void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index);\r
+void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp, int relation,\r
+                   int *index);\r
+\r
+/*\r
+ * Delete an element e in a 2-3-4 tree. Does not free the element,\r
+ * merely removes all links to it from the tree nodes.\r
+ * \r
+ * delpos234 deletes the element at a particular tree index: it\r
+ * works on both sorted and unsorted trees.\r
+ * \r
+ * del234 deletes the element passed to it, so it only works on\r
+ * sorted trees. (It's equivalent to using findpos234 to determine\r
+ * the index of an element, and then passing that index to\r
+ * delpos234.)\r
+ * \r
+ * Both functions return a pointer to the element they delete, for\r
+ * the user to free or pass on elsewhere or whatever. If the index\r
+ * is out of range (delpos234) or the element is already not in the\r
+ * tree (del234) then they return NULL.\r
+ */\r
+void *del234(tree234 * t, void *e);\r
+void *delpos234(tree234 * t, int index);\r
+\r
+/*\r
+ * Return the total element count of a tree234.\r
+ */\r
+int count234(tree234 * t);\r
+\r
+#endif                         /* TREE234_H */\r
diff --git a/putty/UNIX/CONFIGUR.AC b/putty/UNIX/CONFIGUR.AC
new file mode 100644 (file)
index 0000000..382bf2a
--- /dev/null
@@ -0,0 +1,107 @@
+# To compile this into a configure script, you need:\r
+# * Autoconf 2.50 or newer\r
+# * Gtk (for $prefix/share/aclocal/gtk.m4)\r
+# * Automake (for aclocal)\r
+# If you've got them, running "autoreconf" should work.\r
+\r
+AC_INIT\r
+AC_CONFIG_FILES([Makefile])\r
+AC_CONFIG_HEADERS([uxconfig.h:uxconfig.in])\r
+\r
+AC_PROG_INSTALL\r
+AC_PROG_CC\r
+if test "X$GCC" = Xyes; then\r
+    PUTTYCFLAGS="-Wall -Werror"\r
+else\r
+    PUTTYCFLAGS=""\r
+fi\r
+AC_SUBST(PUTTYCFLAGS)\r
+\r
+AC_ARG_WITH([gssapi],\r
+  [AS_HELP_STRING([--without-gssapi],\r
+                  [disable GSSAPI support])],\r
+  [],\r
+  [with_gssapi=yes])\r
+\r
+WITH_GSSAPI=\r
+AS_IF([test "x$with_gssapi" != xno],\r
+  [AC_DEFINE([WITH_GSSAPI], [1], [Define if building with GSSAPI support.])])\r
+\r
+AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[\r
+#include <sys/types.h>\r
+#include <utmp.h>])\r
+\r
+# Look for both GTK 1 and GTK 2.\r
+AM_PATH_GTK([1.2.0], [gtk=1], [gtk=none])\r
+AM_PATH_GTK_2_0([2.0.0], [gtk=2], [])\r
+if test "$gtk" = "none"; then\r
+  all_targets="all-cli"\r
+else\r
+  all_targets="all-cli all-gtk"\r
+fi\r
+if test "$gtk" = "2"; then\r
+  ac_save_CFLAGS="$CFLAGS"\r
+  ac_save_LIBS="$LIBS"\r
+  CFLAGS="$CFLAGS $GTK_CFLAGS"\r
+  LIBS="$GTK_LIBS $LIBS"\r
+  AC_CHECK_FUNCS([pango_font_family_is_monospace pango_font_map_list_families])\r
+  CFLAGS="$ac_save_CFLAGS"\r
+  LIBS="$ac_save_LIBS"\r
+fi\r
+AC_SUBST([all_targets])\r
+\r
+AC_SEARCH_LIBS([socket], [xnet])\r
+\r
+AS_IF([test "x$with_gssapi" != xno],\r
+  [AC_SEARCH_LIBS(\r
+    [dlopen],[dl],\r
+    [],\r
+    [AC_DEFINE([NO_LIBDL], [1], [Define if we could not find libdl.])\r
+     AC_CHECK_HEADERS([gssapi/gssapi.h])\r
+     AC_SEARCH_LIBS(\r
+       [gss_init_sec_context],[gssapi gssapi_krb5 gss],\r
+       [],\r
+       [AC_DEFINE([NO_GSSAPI_LIB], [1], [Define if we could not find a gssapi library])])])])\r
+\r
+AC_CHECK_LIB(X11, XOpenDisplay)\r
+\r
+AC_CHECK_FUNCS([getaddrinfo ptsname setresuid strsignal updwtmpx])\r
+\r
+AC_OUTPUT\r
+\r
+AH_BOTTOM([\r
+/* Convert autoconf definitions to ones that PuTTY wants. */\r
+\r
+#ifndef HAVE_GETADDRINFO\r
+# define NO_IPV6\r
+#endif\r
+#ifndef HAVE_SETRESUID\r
+# define HAVE_NO_SETRESUID\r
+#endif\r
+#ifndef HAVE_STRSIGNAL\r
+# define HAVE_NO_STRSIGNAL\r
+#endif\r
+#if !defined(HAVE_UTMPX_H) || !defined(HAVE_UPDWTMPX)\r
+# define OMIT_UTMP\r
+#endif\r
+#ifndef HAVE_PTSNAME\r
+# define BSD_PTYS\r
+#endif\r
+#ifndef HAVE_SYS_SELECT_H\r
+# define HAVE_NO_SYS_SELECT_H\r
+#endif\r
+#ifndef HAVE_PANGO_FONT_FAMILY_IS_MONOSPACE\r
+# define PANGO_PRE_1POINT4\r
+#endif\r
+#ifndef HAVE_PANGO_FONT_MAP_LIST_FAMILIES\r
+# define PANGO_PRE_1POINT6\r
+#endif\r
+#if !defined(WITH_GSSAPI)\r
+# define NO_GSSAPI\r
+#endif\r
+#if !defined(NO_GSSAPI) && defined(NO_LIBDL)\r
+# if !defined(HAVE_GSSAPI_GSSAPI_H) || defined(NO_GSSAPI_LIB)\r
+#  define NO_GSSAPI\r
+# endif\r
+#endif\r
+])\r
diff --git a/putty/UNIX/GTKCFG.C b/putty/UNIX/GTKCFG.C
new file mode 100644 (file)
index 0000000..2108935
--- /dev/null
@@ -0,0 +1,144 @@
+/*\r
+ * gtkcfg.c - the GTK-specific parts of the PuTTY configuration\r
+ * box.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "storage.h"\r
+\r
+static void about_handler(union control *ctrl, void *dlg,\r
+                         void *data, int event)\r
+{\r
+    if (event == EVENT_ACTION) {\r
+       about_box(ctrl->generic.context.p);\r
+    }\r
+}\r
+\r
+void gtk_setup_config_box(struct controlbox *b, int midsession, void *win)\r
+{\r
+    struct controlset *s, *s2;\r
+    union control *c;\r
+    int i;\r
+\r
+    if (!midsession) {\r
+       /*\r
+        * Add the About button to the standard panel.\r
+        */\r
+       s = ctrl_getset(b, "", "", "");\r
+       c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help),\r
+                           about_handler, P(win));\r
+       c->generic.column = 0;\r
+    }\r
+\r
+    /*\r
+     * GTK makes it rather easier to put the scrollbar on the left\r
+     * than Windows does!\r
+     */\r
+    s = ctrl_getset(b, "Window", "scrollback",\r
+                   "Control the scrollback in the window");\r
+    ctrl_checkbox(s, "Scrollbar on left", 'l',\r
+                 HELPCTX(no_help),\r
+                 dlg_stdcheckbox_handler,\r
+                  I(offsetof(Config,scrollbar_on_left)));\r
+    /*\r
+     * Really this wants to go just after `Display scrollbar'. See\r
+     * if we can find that control, and do some shuffling.\r
+     */\r
+    for (i = 0; i < s->ncontrols; i++) {\r
+        c = s->ctrls[i];\r
+        if (c->generic.type == CTRL_CHECKBOX &&\r
+            c->generic.context.i == offsetof(Config,scrollbar)) {\r
+            /*\r
+             * Control i is the scrollbar checkbox.\r
+             * Control s->ncontrols-1 is the scrollbar-on-left one.\r
+             */\r
+            if (i < s->ncontrols-2) {\r
+                c = s->ctrls[s->ncontrols-1];\r
+                memmove(s->ctrls+i+2, s->ctrls+i+1,\r
+                        (s->ncontrols-i-2)*sizeof(union control *));\r
+                s->ctrls[i+1] = c;\r
+            }\r
+            break;\r
+        }\r
+    }\r
+\r
+    /*\r
+     * X requires three more fonts: bold, wide, and wide-bold; also\r
+     * we need the fiddly shadow-bold-offset control. This would\r
+     * make the Window/Appearance panel rather unwieldy and large,\r
+     * so I think the sensible thing here is to _move_ this\r
+     * controlset into a separate Window/Fonts panel!\r
+     */\r
+    s2 = ctrl_getset(b, "Window/Appearance", "font",\r
+                     "Font settings");\r
+    /* Remove this controlset from b. */\r
+    for (i = 0; i < b->nctrlsets; i++) {\r
+        if (b->ctrlsets[i] == s2) {\r
+            memmove(b->ctrlsets+i, b->ctrlsets+i+1,\r
+                    (b->nctrlsets-i-1) * sizeof(*b->ctrlsets));\r
+            b->nctrlsets--;\r
+            break;\r
+        }\r
+    }\r
+    ctrl_settitle(b, "Window/Fonts", "Options controlling font usage");\r
+    s = ctrl_getset(b, "Window/Fonts", "font",\r
+                    "Fonts for displaying non-bold text");\r
+    ctrl_fontsel(s, "Font used for ordinary text", 'f',\r
+                HELPCTX(no_help),\r
+                dlg_stdfontsel_handler, I(offsetof(Config,font)));\r
+    ctrl_fontsel(s, "Font used for wide (CJK) text", 'w',\r
+                HELPCTX(no_help),\r
+                dlg_stdfontsel_handler, I(offsetof(Config,widefont)));\r
+    s = ctrl_getset(b, "Window/Fonts", "fontbold",\r
+                    "Fonts for displaying bolded text");\r
+    ctrl_fontsel(s, "Font used for bolded text", 'b',\r
+                HELPCTX(no_help),\r
+                dlg_stdfontsel_handler, I(offsetof(Config,boldfont)));\r
+    ctrl_fontsel(s, "Font used for bold wide text", 'i',\r
+                HELPCTX(no_help),\r
+                dlg_stdfontsel_handler, I(offsetof(Config,wideboldfont)));\r
+    ctrl_checkbox(s, "Use shadow bold instead of bold fonts", 'u',\r
+                 HELPCTX(no_help),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,shadowbold)));\r
+    ctrl_text(s, "(Note that bold fonts or shadow bolding are only"\r
+             " used if you have not requested bolding to be done by"\r
+             " changing the text colour.)",\r
+              HELPCTX(no_help));\r
+    ctrl_editbox(s, "Horizontal offset for shadow bold:", 'z', 20,\r
+                HELPCTX(no_help), dlg_stdeditbox_handler,\r
+                 I(offsetof(Config,shadowboldoffset)), I(-1));\r
+\r
+    /*\r
+     * Markus Kuhn feels, not totally unreasonably, that it's good\r
+     * for all applications to shift into UTF-8 mode if they notice\r
+     * that they've been started with a LANG setting dictating it,\r
+     * so that people don't have to keep remembering a separate\r
+     * UTF-8 option for every application they use. Therefore,\r
+     * here's an override option in the Translation panel.\r
+     */\r
+    s = ctrl_getset(b, "Window/Translation", "trans",\r
+                   "Character set translation on received data");\r
+    ctrl_checkbox(s, "Override with UTF-8 if locale says so", 'l',\r
+                 HELPCTX(translation_utf8_override),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,utf8_override)));\r
+\r
+    if (!midsession) {\r
+        /*\r
+         * Allow the user to specify the window class as part of the saved\r
+         * configuration, so that they can have their window manager treat\r
+         * different kinds of PuTTY and pterm differently if they want to.\r
+         */\r
+        s = ctrl_getset(b, "Window/Behaviour", "x11",\r
+                        "X Window System settings");\r
+        ctrl_editbox(s, "Window class name:", 'z', 50,\r
+                     HELPCTX(no_help), dlg_stdeditbox_handler,\r
+                     I(offsetof(Config,winclass)),\r
+                     I(sizeof(((Config *)0)->winclass)));\r
+    }\r
+}\r
diff --git a/putty/UNIX/GTKCOLS.C b/putty/UNIX/GTKCOLS.C
new file mode 100644 (file)
index 0000000..cb9fd5b
--- /dev/null
@@ -0,0 +1,752 @@
+/*\r
+ * gtkcols.c - implementation of the `Columns' GTK layout container.\r
+ */\r
+\r
+#include "gtkcols.h"\r
+#include <gtk/gtk.h>\r
+\r
+static void columns_init(Columns *cols);\r
+static void columns_class_init(ColumnsClass *klass);\r
+static void columns_map(GtkWidget *widget);\r
+static void columns_unmap(GtkWidget *widget);\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+static void columns_draw(GtkWidget *widget, GdkRectangle *area);\r
+static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);\r
+#endif\r
+static void columns_base_add(GtkContainer *container, GtkWidget *widget);\r
+static void columns_remove(GtkContainer *container, GtkWidget *widget);\r
+static void columns_forall(GtkContainer *container, gboolean include_internals,\r
+                           GtkCallback callback, gpointer callback_data);\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+static gint columns_focus(GtkContainer *container, GtkDirectionType dir);\r
+#endif\r
+static GtkType columns_child_type(GtkContainer *container);\r
+static void columns_size_request(GtkWidget *widget, GtkRequisition *req);\r
+static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);\r
+\r
+static GtkContainerClass *parent_class = NULL;\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+GtkType columns_get_type(void)\r
+{\r
+    static GtkType columns_type = 0;\r
+\r
+    if (!columns_type) {\r
+        static const GtkTypeInfo columns_info = {\r
+            "Columns",\r
+            sizeof(Columns),\r
+            sizeof(ColumnsClass),\r
+            (GtkClassInitFunc) columns_class_init,\r
+            (GtkObjectInitFunc) columns_init,\r
+            /* reserved_1 */ NULL,\r
+            /* reserved_2 */ NULL,\r
+            (GtkClassInitFunc) NULL,\r
+        };\r
+\r
+        columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info);\r
+    }\r
+\r
+    return columns_type;\r
+}\r
+#else\r
+GType columns_get_type(void)\r
+{\r
+    static GType columns_type = 0;\r
+\r
+    if (!columns_type) {\r
+        static const GTypeInfo columns_info = {\r
+            sizeof(ColumnsClass),\r
+           NULL,\r
+           NULL,\r
+            (GClassInitFunc) columns_class_init,\r
+           NULL,\r
+           NULL,\r
+            sizeof(Columns),\r
+           0,\r
+            (GInstanceInitFunc)columns_init,\r
+        };\r
+\r
+        columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",\r
+                                             &columns_info, 0);\r
+    }\r
+\r
+    return columns_type;\r
+}\r
+#endif\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+static gint (*columns_inherited_focus)(GtkContainer *container,\r
+                                      GtkDirectionType direction);\r
+#endif\r
+\r
+static void columns_class_init(ColumnsClass *klass)\r
+{\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */\r
+    GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;\r
+    GtkContainerClass *container_class = (GtkContainerClass *)klass;\r
+#else\r
+    /* GObjectClass *object_class = G_OBJECT_CLASS(klass); */\r
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);\r
+    GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);\r
+#endif\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    parent_class = gtk_type_class(GTK_TYPE_CONTAINER);\r
+#else\r
+    parent_class = g_type_class_peek_parent(klass);\r
+#endif\r
+\r
+    widget_class->map = columns_map;\r
+    widget_class->unmap = columns_unmap;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    widget_class->draw = columns_draw;\r
+    widget_class->expose_event = columns_expose;\r
+#endif\r
+    widget_class->size_request = columns_size_request;\r
+    widget_class->size_allocate = columns_size_allocate;\r
+\r
+    container_class->add = columns_base_add;\r
+    container_class->remove = columns_remove;\r
+    container_class->forall = columns_forall;\r
+    container_class->child_type = columns_child_type;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    /* Save the previous value of this method. */\r
+    if (!columns_inherited_focus)\r
+       columns_inherited_focus = container_class->focus;\r
+    container_class->focus = columns_focus;\r
+#endif\r
+}\r
+\r
+static void columns_init(Columns *cols)\r
+{\r
+    GTK_WIDGET_SET_FLAGS(cols, GTK_NO_WINDOW);\r
+\r
+    cols->children = NULL;\r
+    cols->spacing = 0;\r
+}\r
+\r
+/*\r
+ * These appear to be thoroughly tedious functions; the only reason\r
+ * we have to reimplement them at all is because we defined our own\r
+ * format for our GList of children...\r
+ */\r
+static void columns_map(GtkWidget *widget)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GList *children;\r
+\r
+    g_return_if_fail(widget != NULL);\r
+    g_return_if_fail(IS_COLUMNS(widget));\r
+\r
+    cols = COLUMNS(widget);\r
+    GTK_WIDGET_SET_FLAGS(cols, GTK_MAPPED);\r
+\r
+    for (children = cols->children;\r
+         children && (child = children->data);\r
+         children = children->next) {\r
+        if (child->widget &&\r
+           GTK_WIDGET_VISIBLE(child->widget) &&\r
+            !GTK_WIDGET_MAPPED(child->widget))\r
+            gtk_widget_map(child->widget);\r
+    }\r
+}\r
+static void columns_unmap(GtkWidget *widget)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GList *children;\r
+\r
+    g_return_if_fail(widget != NULL);\r
+    g_return_if_fail(IS_COLUMNS(widget));\r
+\r
+    cols = COLUMNS(widget);\r
+    GTK_WIDGET_UNSET_FLAGS(cols, GTK_MAPPED);\r
+\r
+    for (children = cols->children;\r
+         children && (child = children->data);\r
+         children = children->next) {\r
+        if (child->widget &&\r
+           GTK_WIDGET_VISIBLE(child->widget) &&\r
+            GTK_WIDGET_MAPPED(child->widget))\r
+            gtk_widget_unmap(child->widget);\r
+    }\r
+}\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+static void columns_draw(GtkWidget *widget, GdkRectangle *area)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GList *children;\r
+    GdkRectangle child_area;\r
+\r
+    g_return_if_fail(widget != NULL);\r
+    g_return_if_fail(IS_COLUMNS(widget));\r
+\r
+    if (GTK_WIDGET_DRAWABLE(widget)) {\r
+        cols = COLUMNS(widget);\r
+\r
+        for (children = cols->children;\r
+             children && (child = children->data);\r
+             children = children->next) {\r
+            if (child->widget &&\r
+               GTK_WIDGET_DRAWABLE(child->widget) &&\r
+                gtk_widget_intersect(child->widget, area, &child_area))\r
+                gtk_widget_draw(child->widget, &child_area);\r
+        }\r
+    }\r
+}\r
+static gint columns_expose(GtkWidget *widget, GdkEventExpose *event)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GList *children;\r
+    GdkEventExpose child_event;\r
+\r
+    g_return_val_if_fail(widget != NULL, FALSE);\r
+    g_return_val_if_fail(IS_COLUMNS(widget), FALSE);\r
+    g_return_val_if_fail(event != NULL, FALSE);\r
+\r
+    if (GTK_WIDGET_DRAWABLE(widget)) {\r
+        cols = COLUMNS(widget);\r
+        child_event = *event;\r
+\r
+        for (children = cols->children;\r
+             children && (child = children->data);\r
+             children = children->next) {\r
+            if (child->widget &&\r
+               GTK_WIDGET_DRAWABLE(child->widget) &&\r
+                GTK_WIDGET_NO_WINDOW(child->widget) &&\r
+                gtk_widget_intersect(child->widget, &event->area,\r
+                                     &child_event.area))\r
+                gtk_widget_event(child->widget, (GdkEvent *)&child_event);\r
+        }\r
+    }\r
+    return FALSE;\r
+}\r
+#endif\r
+\r
+static void columns_base_add(GtkContainer *container, GtkWidget *widget)\r
+{\r
+    Columns *cols;\r
+\r
+    g_return_if_fail(container != NULL);\r
+    g_return_if_fail(IS_COLUMNS(container));\r
+    g_return_if_fail(widget != NULL);\r
+\r
+    cols = COLUMNS(container);\r
+\r
+    /*\r
+     * Default is to add a new widget spanning all columns.\r
+     */\r
+    columns_add(cols, widget, 0, 0);   /* 0 means ncols */\r
+}\r
+\r
+static void columns_remove(GtkContainer *container, GtkWidget *widget)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GtkWidget *childw;\r
+    GList *children;\r
+    gboolean was_visible;\r
+\r
+    g_return_if_fail(container != NULL);\r
+    g_return_if_fail(IS_COLUMNS(container));\r
+    g_return_if_fail(widget != NULL);\r
+\r
+    cols = COLUMNS(container);\r
+\r
+    for (children = cols->children;\r
+         children && (child = children->data);\r
+         children = children->next) {\r
+        if (child->widget != widget)\r
+            continue;\r
+\r
+        was_visible = GTK_WIDGET_VISIBLE(widget);\r
+        gtk_widget_unparent(widget);\r
+        cols->children = g_list_remove_link(cols->children, children);\r
+        g_list_free(children);\r
+        g_free(child);\r
+        if (was_visible)\r
+            gtk_widget_queue_resize(GTK_WIDGET(container));\r
+        break;\r
+    }\r
+\r
+    for (children = cols->taborder;\r
+         children && (childw = children->data);\r
+         children = children->next) {\r
+        if (childw != widget)\r
+            continue;\r
+\r
+        cols->taborder = g_list_remove_link(cols->taborder, children);\r
+        g_list_free(children);\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+       gtk_container_set_focus_chain(container, cols->taborder);\r
+#endif\r
+        break;\r
+    }\r
+}\r
+\r
+static void columns_forall(GtkContainer *container, gboolean include_internals,\r
+                           GtkCallback callback, gpointer callback_data)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GList *children, *next;\r
+\r
+    g_return_if_fail(container != NULL);\r
+    g_return_if_fail(IS_COLUMNS(container));\r
+    g_return_if_fail(callback != NULL);\r
+\r
+    cols = COLUMNS(container);\r
+\r
+    for (children = cols->children;\r
+         children && (child = children->data);\r
+         children = next) {\r
+        /*\r
+         * We can't wait until after the callback to assign\r
+         * `children = children->next', because the callback might\r
+         * be gtk_widget_destroy, which would remove the link\r
+         * `children' from the list! So instead we must get our\r
+         * hands on the value of the `next' pointer _before_ the\r
+         * callback.\r
+         */\r
+        next = children->next;\r
+       if (child->widget)\r
+           callback(child->widget, callback_data);\r
+    }\r
+}\r
+\r
+static GtkType columns_child_type(GtkContainer *container)\r
+{\r
+    return GTK_TYPE_WIDGET;\r
+}\r
+\r
+GtkWidget *columns_new(gint spacing)\r
+{\r
+    Columns *cols;\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    cols = gtk_type_new(columns_get_type());\r
+#else\r
+    cols = g_object_new(TYPE_COLUMNS, NULL);\r
+#endif\r
+\r
+    cols->spacing = spacing;\r
+\r
+    return GTK_WIDGET(cols);\r
+}\r
+\r
+void columns_set_cols(Columns *cols, gint ncols, const gint *percentages)\r
+{\r
+    ColumnsChild *childdata;\r
+    gint i;\r
+\r
+    g_return_if_fail(cols != NULL);\r
+    g_return_if_fail(IS_COLUMNS(cols));\r
+    g_return_if_fail(ncols > 0);\r
+    g_return_if_fail(percentages != NULL);\r
+\r
+    childdata = g_new(ColumnsChild, 1);\r
+    childdata->widget = NULL;\r
+    childdata->ncols = ncols;\r
+    childdata->percentages = g_new(gint, ncols);\r
+    childdata->force_left = FALSE;\r
+    for (i = 0; i < ncols; i++)\r
+        childdata->percentages[i] = percentages[i];\r
+\r
+    cols->children = g_list_append(cols->children, childdata);\r
+}\r
+\r
+void columns_add(Columns *cols, GtkWidget *child,\r
+                 gint colstart, gint colspan)\r
+{\r
+    ColumnsChild *childdata;\r
+\r
+    g_return_if_fail(cols != NULL);\r
+    g_return_if_fail(IS_COLUMNS(cols));\r
+    g_return_if_fail(child != NULL);\r
+    g_return_if_fail(child->parent == NULL);\r
+\r
+    childdata = g_new(ColumnsChild, 1);\r
+    childdata->widget = child;\r
+    childdata->colstart = colstart;\r
+    childdata->colspan = colspan;\r
+    childdata->force_left = FALSE;\r
+\r
+    cols->children = g_list_append(cols->children, childdata);\r
+    cols->taborder = g_list_append(cols->taborder, child);\r
+\r
+    gtk_widget_set_parent(child, GTK_WIDGET(cols));\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);\r
+#endif\r
+\r
+    if (GTK_WIDGET_REALIZED(cols))\r
+        gtk_widget_realize(child);\r
+\r
+    if (GTK_WIDGET_VISIBLE(cols) && GTK_WIDGET_VISIBLE(child)) {\r
+        if (GTK_WIDGET_MAPPED(cols))\r
+            gtk_widget_map(child);\r
+        gtk_widget_queue_resize(child);\r
+    }\r
+}\r
+\r
+void columns_force_left_align(Columns *cols, GtkWidget *widget)\r
+{\r
+    ColumnsChild *child;\r
+    GList *children;\r
+\r
+    g_return_if_fail(cols != NULL);\r
+    g_return_if_fail(IS_COLUMNS(cols));\r
+    g_return_if_fail(widget != NULL);\r
+\r
+    for (children = cols->children;\r
+         children && (child = children->data);\r
+         children = children->next) {\r
+        if (child->widget != widget)\r
+            continue;\r
+\r
+       child->force_left = TRUE;\r
+        if (GTK_WIDGET_VISIBLE(widget))\r
+            gtk_widget_queue_resize(GTK_WIDGET(cols));\r
+        break;\r
+    }\r
+}\r
+\r
+void columns_taborder_last(Columns *cols, GtkWidget *widget)\r
+{\r
+    GtkWidget *childw;\r
+    GList *children;\r
+\r
+    g_return_if_fail(cols != NULL);\r
+    g_return_if_fail(IS_COLUMNS(cols));\r
+    g_return_if_fail(widget != NULL);\r
+\r
+    for (children = cols->taborder;\r
+         children && (childw = children->data);\r
+         children = children->next) {\r
+        if (childw != widget)\r
+            continue;\r
+\r
+        cols->taborder = g_list_remove_link(cols->taborder, children);\r
+        g_list_free(children);\r
+       cols->taborder = g_list_append(cols->taborder, widget);\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+       gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);\r
+#endif\r
+        break;\r
+    }\r
+}\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+/*\r
+ * Override GtkContainer's focus movement so the user can\r
+ * explicitly specify the tab order.\r
+ */\r
+static gint columns_focus(GtkContainer *container, GtkDirectionType dir)\r
+{\r
+    Columns *cols;\r
+    GList *pos;\r
+    GtkWidget *focuschild;\r
+\r
+    g_return_val_if_fail(container != NULL, FALSE);\r
+    g_return_val_if_fail(IS_COLUMNS(container), FALSE);\r
+\r
+    cols = COLUMNS(container);\r
+\r
+    if (!GTK_WIDGET_DRAWABLE(cols) ||\r
+       !GTK_WIDGET_IS_SENSITIVE(cols))\r
+       return FALSE;\r
+\r
+    if (!GTK_WIDGET_CAN_FOCUS(container) &&\r
+       (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {\r
+\r
+       focuschild = container->focus_child;\r
+       gtk_container_set_focus_child(container, NULL);\r
+\r
+       if (dir == GTK_DIR_TAB_FORWARD)\r
+           pos = cols->taborder;\r
+       else\r
+           pos = g_list_last(cols->taborder);\r
+\r
+       while (pos) {\r
+           GtkWidget *child = pos->data;\r
+\r
+           if (focuschild) {\r
+               if (focuschild == child) {\r
+                   focuschild = NULL; /* now we can start looking in here */\r
+                   if (GTK_WIDGET_DRAWABLE(child) &&\r
+                       GTK_IS_CONTAINER(child) &&\r
+                       !GTK_WIDGET_HAS_FOCUS(child)) {\r
+                       if (gtk_container_focus(GTK_CONTAINER(child), dir))\r
+                           return TRUE;\r
+                   }\r
+               }\r
+           } else if (GTK_WIDGET_DRAWABLE(child)) {\r
+               if (GTK_IS_CONTAINER(child)) {\r
+                   if (gtk_container_focus(GTK_CONTAINER(child), dir))\r
+                       return TRUE;\r
+               } else if (GTK_WIDGET_CAN_FOCUS(child)) {\r
+                   gtk_widget_grab_focus(child);\r
+                   return TRUE;\r
+               }\r
+           }\r
+\r
+           if (dir == GTK_DIR_TAB_FORWARD)\r
+               pos = pos->next;\r
+           else\r
+               pos = pos->prev;\r
+       }\r
+\r
+       return FALSE;\r
+    } else\r
+       return columns_inherited_focus(container, dir);\r
+}\r
+#endif\r
+\r
+/*\r
+ * Now here comes the interesting bit. The actual layout part is\r
+ * done in the following two functions:\r
+ * \r
+ * columns_size_request() examines the list of widgets held in the\r
+ * Columns, and returns a requisition stating the absolute minimum\r
+ * size it can bear to be.\r
+ * \r
+ * columns_size_allocate() is given an allocation telling it what\r
+ * size the whole container is going to be, and it calls\r
+ * gtk_widget_size_allocate() on all of its (visible) children to\r
+ * set their size and position relative to the top left of the\r
+ * container.\r
+ */\r
+\r
+static void columns_size_request(GtkWidget *widget, GtkRequisition *req)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GList *children;\r
+    gint i, ncols, colspan, *colypos;\r
+    const gint *percentages;\r
+    static const gint onecol[] = { 100 };\r
+\r
+    g_return_if_fail(widget != NULL);\r
+    g_return_if_fail(IS_COLUMNS(widget));\r
+    g_return_if_fail(req != NULL);\r
+\r
+    cols = COLUMNS(widget);\r
+\r
+    req->width = 0;\r
+    req->height = cols->spacing;\r
+\r
+    ncols = 1;\r
+    colypos = g_new(gint, 1);\r
+    colypos[0] = 0;\r
+    percentages = onecol;\r
+\r
+    for (children = cols->children;\r
+         children && (child = children->data);\r
+         children = children->next) {\r
+        GtkRequisition creq;\r
+\r
+       if (!child->widget) {\r
+           /* Column reconfiguration. */\r
+           for (i = 1; i < ncols; i++) {\r
+               if (colypos[0] < colypos[i])\r
+                   colypos[0] = colypos[i];\r
+           }\r
+           ncols = child->ncols;\r
+           percentages = child->percentages;\r
+           colypos = g_renew(gint, colypos, ncols);\r
+           for (i = 1; i < ncols; i++)\r
+               colypos[i] = colypos[0];\r
+           continue;\r
+       }\r
+\r
+        /* Only take visible widgets into account. */\r
+        if (!GTK_WIDGET_VISIBLE(child->widget))\r
+            continue;\r
+\r
+        gtk_widget_size_request(child->widget, &creq);\r
+       colspan = child->colspan ? child->colspan : ncols-child->colstart;\r
+\r
+        /*\r
+         * To compute width: we know that creq.width plus\r
+         * cols->spacing needs to equal a certain percentage of the\r
+         * full width of the container. So we work this value out,\r
+         * figure out how wide the container will need to be to\r
+         * make that percentage of it equal to that width, and\r
+         * ensure our returned width is at least that much. Very\r
+         * simple really.\r
+         */\r
+        {\r
+            int percent, thiswid, fullwid;\r
+\r
+            percent = 0;\r
+            for (i = 0; i < colspan; i++)\r
+                percent += percentages[child->colstart+i];\r
+\r
+            thiswid = creq.width + cols->spacing;\r
+            /*\r
+             * Since creq is the _minimum_ size the child needs, we\r
+             * must ensure that it gets _at least_ that size.\r
+             * Hence, when scaling thiswid up to fullwid, we must\r
+             * round up, which means adding percent-1 before\r
+             * dividing by percent.\r
+             */\r
+            fullwid = (thiswid * 100 + percent - 1) / percent;\r
+\r
+            /*\r
+             * The above calculation assumes every widget gets\r
+             * cols->spacing on the right. So we subtract\r
+             * cols->spacing here to account for the extra load of\r
+             * spacing on the right.\r
+             */\r
+            if (req->width < fullwid - cols->spacing)\r
+                req->width = fullwid - cols->spacing;\r
+        }\r
+\r
+        /*\r
+         * To compute height: the widget's top will be positioned\r
+         * at the largest y value so far reached in any of the\r
+         * columns it crosses. Then it will go down by creq.height\r
+         * plus padding; and the point it reaches at the bottom is\r
+         * the new y value in all those columns, and minus the\r
+         * padding it is also a lower bound on our own size\r
+         * request.\r
+         */\r
+        {\r
+            int topy, boty;\r
+\r
+            topy = 0;\r
+            for (i = 0; i < colspan; i++) {\r
+                if (topy < colypos[child->colstart+i])\r
+                    topy = colypos[child->colstart+i];\r
+            }\r
+            boty = topy + creq.height + cols->spacing;\r
+            for (i = 0; i < colspan; i++) {\r
+                colypos[child->colstart+i] = boty;\r
+            }\r
+\r
+            if (req->height < boty - cols->spacing)\r
+                req->height = boty - cols->spacing;\r
+        }\r
+    }\r
+\r
+    req->width += 2*GTK_CONTAINER(cols)->border_width;\r
+    req->height += 2*GTK_CONTAINER(cols)->border_width;\r
+\r
+    g_free(colypos);\r
+}\r
+\r
+static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)\r
+{\r
+    Columns *cols;\r
+    ColumnsChild *child;\r
+    GList *children;\r
+    gint i, ncols, colspan, border, *colxpos, *colypos;\r
+    const gint *percentages;\r
+    static const gint onecol[] = { 100 };\r
+\r
+    g_return_if_fail(widget != NULL);\r
+    g_return_if_fail(IS_COLUMNS(widget));\r
+    g_return_if_fail(alloc != NULL);\r
+\r
+    cols = COLUMNS(widget);\r
+    widget->allocation = *alloc;\r
+    border = GTK_CONTAINER(cols)->border_width;\r
+\r
+    ncols = 1;\r
+    percentages = onecol;\r
+    /* colxpos gives the starting x position of each column.\r
+     * We supply n+1 of them, so that we can find the RH edge easily.\r
+     * All ending x positions are expected to be adjusted afterwards by\r
+     * subtracting the spacing. */\r
+    colxpos = g_new(gint, 2);\r
+    colxpos[0] = 0;\r
+    colxpos[1] = alloc->width - 2*border + cols->spacing;\r
+    /* As in size_request, colypos is the lowest y reached in each column. */\r
+    colypos = g_new(gint, 1);\r
+    colypos[0] = 0;\r
+\r
+    for (children = cols->children;\r
+         children && (child = children->data);\r
+         children = children->next) {\r
+        GtkRequisition creq;\r
+        GtkAllocation call;\r
+\r
+       if (!child->widget) {\r
+           gint percent;\r
+\r
+           /* Column reconfiguration. */\r
+           for (i = 1; i < ncols; i++) {\r
+               if (colypos[0] < colypos[i])\r
+                   colypos[0] = colypos[i];\r
+           }\r
+           ncols = child->ncols;\r
+           percentages = child->percentages;\r
+           colypos = g_renew(gint, colypos, ncols);\r
+           for (i = 1; i < ncols; i++)\r
+               colypos[i] = colypos[0];\r
+           colxpos = g_renew(gint, colxpos, ncols + 1);\r
+           colxpos[0] = 0;\r
+           percent = 0;\r
+           for (i = 0; i < ncols; i++) {\r
+               percent += percentages[i];\r
+               colxpos[i+1] = (((alloc->width - 2*border) + cols->spacing)\r
+                               * percent / 100);\r
+           }\r
+           continue;\r
+       }\r
+\r
+        /* Only take visible widgets into account. */\r
+        if (!GTK_WIDGET_VISIBLE(child->widget))\r
+            continue;\r
+\r
+        gtk_widget_get_child_requisition(child->widget, &creq);\r
+       colspan = child->colspan ? child->colspan : ncols-child->colstart;\r
+\r
+        /*\r
+         * Starting x position is cols[colstart].\r
+         * Ending x position is cols[colstart+colspan] - spacing.\r
+        * \r
+        * Unless we're forcing left, in which case the width is\r
+        * exactly the requisition width.\r
+         */\r
+        call.x = alloc->x + border + colxpos[child->colstart];\r
+       if (child->force_left)\r
+           call.width = creq.width;\r
+       else\r
+           call.width = (colxpos[child->colstart+colspan] -\r
+                         colxpos[child->colstart] - cols->spacing);\r
+\r
+        /*\r
+         * To compute height: the widget's top will be positioned\r
+         * at the largest y value so far reached in any of the\r
+         * columns it crosses. Then it will go down by creq.height\r
+         * plus padding; and the point it reaches at the bottom is\r
+         * the new y value in all those columns.\r
+         */\r
+        {\r
+            int topy, boty;\r
+\r
+            topy = 0;\r
+            for (i = 0; i < colspan; i++) {\r
+                if (topy < colypos[child->colstart+i])\r
+                    topy = colypos[child->colstart+i];\r
+            }\r
+            call.y = alloc->y + border + topy;\r
+            call.height = creq.height;\r
+            boty = topy + creq.height + cols->spacing;\r
+            for (i = 0; i < colspan; i++) {\r
+                colypos[child->colstart+i] = boty;\r
+            }\r
+        }\r
+\r
+        gtk_widget_size_allocate(child->widget, &call);\r
+    }\r
+\r
+    g_free(colxpos);\r
+    g_free(colypos);    \r
+}\r
diff --git a/putty/UNIX/GTKCOLS.H b/putty/UNIX/GTKCOLS.H
new file mode 100644 (file)
index 0000000..295e650
--- /dev/null
@@ -0,0 +1,62 @@
+/*\r
+ * gtkcols.h - header file for a columns-based widget container\r
+ * capable of supporting the PuTTY portable dialog box layout\r
+ * mechanism.\r
+ */\r
+\r
+#ifndef COLUMNS_H\r
+#define COLUMNS_H\r
+\r
+#include <gdk/gdk.h>\r
+#include <gtk/gtkcontainer.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif /* __cplusplus */\r
+\r
+#define TYPE_COLUMNS (columns_get_type())\r
+#define COLUMNS(obj) (GTK_CHECK_CAST((obj), TYPE_COLUMNS, Columns))\r
+#define COLUMNS_CLASS(klass) \\r
+                (GTK_CHECK_CLASS_CAST((klass), TYPE_COLUMNS, ColumnsClass))\r
+#define IS_COLUMNS(obj) (GTK_CHECK_TYPE((obj), TYPE_COLUMNS))\r
+#define IS_COLUMNS_CLASS(klass) (GTK_CHECK_CLASS_TYPE((klass), TYPE_COLUMNS))\r
+\r
+typedef struct Columns_tag Columns;\r
+typedef struct ColumnsClass_tag ColumnsClass;\r
+typedef struct ColumnsChild_tag ColumnsChild;\r
+\r
+struct Columns_tag {\r
+    GtkContainer container;\r
+    /* private after here */\r
+    GList *children;                  /* this holds ColumnsChild structures */\r
+    GList *taborder;                  /* this just holds GtkWidgets */\r
+    gint spacing;\r
+};\r
+\r
+struct ColumnsClass_tag {\r
+    GtkContainerClass parent_class;\r
+};\r
+\r
+struct ColumnsChild_tag {\r
+    /* If `widget' is non-NULL, this entry represents an actual widget. */\r
+    GtkWidget *widget;\r
+    gint colstart, colspan;\r
+    gboolean force_left;              /* for recalcitrant GtkLabels */\r
+    /* Otherwise, this entry represents a change in the column setup. */\r
+    gint ncols;\r
+    gint *percentages;\r
+};\r
+\r
+GtkType columns_get_type(void);\r
+GtkWidget *columns_new(gint spacing);\r
+void columns_set_cols(Columns *cols, gint ncols, const gint *percentages);\r
+void columns_add(Columns *cols, GtkWidget *child,\r
+                 gint colstart, gint colspan);\r
+void columns_taborder_last(Columns *cols, GtkWidget *child);\r
+void columns_force_left_align(Columns *cols, GtkWidget *child);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif /* __cplusplus */\r
+\r
+#endif /* COLUMNS_H */\r
diff --git a/putty/UNIX/GTKDLG.C b/putty/UNIX/GTKDLG.C
new file mode 100644 (file)
index 0000000..fc25e78
--- /dev/null
@@ -0,0 +1,3782 @@
+/*\r
+ * gtkdlg.c - GTK implementation of the PuTTY configuration box.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdarg.h>\r
+#include <ctype.h>\r
+#include <time.h>\r
+#include <gtk/gtk.h>\r
+#include <gdk/gdkkeysyms.h>\r
+#include <gdk/gdkx.h>\r
+#include <X11/Xlib.h>\r
+#include <X11/Xutil.h>\r
+\r
+#include "gtkcols.h"\r
+#include "gtkfont.h"\r
+\r
+#ifdef TESTMODE\r
+#define PUTTY_DO_GLOBALS              /* actually _define_ globals */\r
+#endif\r
+\r
+#include "putty.h"\r
+#include "storage.h"\r
+#include "dialog.h"\r
+#include "tree234.h"\r
+\r
+struct Shortcut {\r
+    GtkWidget *widget;\r
+    struct uctrl *uc;\r
+    int action;\r
+};\r
+\r
+struct Shortcuts {\r
+    struct Shortcut sc[128];\r
+};\r
+\r
+struct uctrl {\r
+    union control *ctrl;\r
+    GtkWidget *toplevel;\r
+    void *privdata;\r
+    int privdata_needs_free;\r
+    GtkWidget **buttons; int nbuttons; /* for radio buttons */\r
+    GtkWidget *entry;         /* for editbox, filesel, fontsel */\r
+    GtkWidget *button;        /* for filesel, fontsel */\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    GtkWidget *list;         /* for listbox (in GTK1), combobox (<=GTK2.3) */\r
+    GtkWidget *menu;         /* for optionmenu (==droplist) */\r
+    GtkWidget *optmenu;              /* also for optionmenu */\r
+#else\r
+    GtkWidget *combo;         /* for combo box (either editable or not) */\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    GtkWidget *treeview;      /* for listbox (GTK2), droplist+combo (>=2.4) */\r
+    GtkListStore *listmodel;  /* for all types of list box */\r
+#endif\r
+    GtkWidget *text;         /* for text */\r
+    GtkWidget *label;         /* for dlg_label_change */\r
+    GtkAdjustment *adj;       /* for the scrollbar in a list box */\r
+    guint entrysig;\r
+    guint textsig;\r
+    int nclicks;\r
+};\r
+\r
+struct dlgparam {\r
+    tree234 *byctrl, *bywidget;\r
+    void *data;\r
+    struct { unsigned char r, g, b, ok; } coloursel_result;   /* 0-255 */\r
+    /* `flags' are set to indicate when a GTK signal handler is being called\r
+     * due to automatic processing and should not flag a user event. */\r
+    int flags;\r
+    struct Shortcuts *shortcuts;\r
+    GtkWidget *window, *cancelbutton;\r
+    union control *currfocus, *lastfocus;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    GtkWidget *currtreeitem, **treeitems;\r
+    int ntreeitems;\r
+#endif\r
+    int retval;\r
+};\r
+#define FLAG_UPDATING_COMBO_LIST 1\r
+#define FLAG_UPDATING_LISTBOX    2\r
+\r
+enum {                                /* values for Shortcut.action */\r
+    SHORTCUT_EMPTY,                   /* no shortcut on this key */\r
+    SHORTCUT_TREE,                    /* focus a tree item */\r
+    SHORTCUT_FOCUS,                   /* focus the supplied widget */\r
+    SHORTCUT_UCTRL,                   /* do something sane with uctrl */\r
+    SHORTCUT_UCTRL_UP,                /* uctrl is a draglist, move Up */\r
+    SHORTCUT_UCTRL_DOWN,              /* uctrl is a draglist, move Down */\r
+};\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+enum {\r
+    TREESTORE_PATH,\r
+    TREESTORE_PARAMS,\r
+    TREESTORE_NUM\r
+};\r
+#endif\r
+\r
+/*\r
+ * Forward references.\r
+ */\r
+static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,\r
+                             gpointer data);\r
+static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,\r
+                        int chr, int action, void *ptr);\r
+static void shortcut_highlight(GtkWidget *label, int chr);\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+static gboolean listitem_single_key(GtkWidget *item, GdkEventKey *event,\r
+                                   gpointer data);\r
+static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event,\r
+                                  gpointer data);\r
+static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event,\r
+                                     gpointer data);\r
+static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event,\r
+                                       gpointer data);\r
+#endif\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+static void menuitem_activate(GtkMenuItem *item, gpointer data);\r
+#endif\r
+static void coloursel_ok(GtkButton *button, gpointer data);\r
+static void coloursel_cancel(GtkButton *button, gpointer data);\r
+static void window_destroy(GtkWidget *widget, gpointer data);\r
+int get_listitemheight(GtkWidget *widget);\r
+\r
+static int uctrl_cmp_byctrl(void *av, void *bv)\r
+{\r
+    struct uctrl *a = (struct uctrl *)av;\r
+    struct uctrl *b = (struct uctrl *)bv;\r
+    if (a->ctrl < b->ctrl)\r
+       return -1;\r
+    else if (a->ctrl > b->ctrl)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int uctrl_cmp_byctrl_find(void *av, void *bv)\r
+{\r
+    union control *a = (union control *)av;\r
+    struct uctrl *b = (struct uctrl *)bv;\r
+    if (a < b->ctrl)\r
+       return -1;\r
+    else if (a > b->ctrl)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int uctrl_cmp_bywidget(void *av, void *bv)\r
+{\r
+    struct uctrl *a = (struct uctrl *)av;\r
+    struct uctrl *b = (struct uctrl *)bv;\r
+    if (a->toplevel < b->toplevel)\r
+       return -1;\r
+    else if (a->toplevel > b->toplevel)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int uctrl_cmp_bywidget_find(void *av, void *bv)\r
+{\r
+    GtkWidget *a = (GtkWidget *)av;\r
+    struct uctrl *b = (struct uctrl *)bv;\r
+    if (a < b->toplevel)\r
+       return -1;\r
+    else if (a > b->toplevel)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static void dlg_init(struct dlgparam *dp)\r
+{\r
+    dp->byctrl = newtree234(uctrl_cmp_byctrl);\r
+    dp->bywidget = newtree234(uctrl_cmp_bywidget);\r
+    dp->coloursel_result.ok = FALSE;\r
+    dp->window = dp->cancelbutton = NULL;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    dp->treeitems = NULL;\r
+    dp->currtreeitem = NULL;\r
+#endif\r
+    dp->flags = 0;\r
+    dp->currfocus = NULL;\r
+}\r
+\r
+static void dlg_cleanup(struct dlgparam *dp)\r
+{\r
+    struct uctrl *uc;\r
+\r
+    freetree234(dp->byctrl);          /* doesn't free the uctrls inside */\r
+    dp->byctrl = NULL;\r
+    while ( (uc = index234(dp->bywidget, 0)) != NULL) {\r
+       del234(dp->bywidget, uc);\r
+       if (uc->privdata_needs_free)\r
+           sfree(uc->privdata);\r
+       sfree(uc->buttons);\r
+       sfree(uc);\r
+    }\r
+    freetree234(dp->bywidget);\r
+    dp->bywidget = NULL;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    sfree(dp->treeitems);\r
+#endif\r
+}\r
+\r
+static void dlg_add_uctrl(struct dlgparam *dp, struct uctrl *uc)\r
+{\r
+    add234(dp->byctrl, uc);\r
+    add234(dp->bywidget, uc);\r
+}\r
+\r
+static struct uctrl *dlg_find_byctrl(struct dlgparam *dp, union control *ctrl)\r
+{\r
+    if (!dp->byctrl)\r
+       return NULL;\r
+    return find234(dp->byctrl, ctrl, uctrl_cmp_byctrl_find);\r
+}\r
+\r
+static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w)\r
+{\r
+    struct uctrl *ret = NULL;\r
+    if (!dp->bywidget)\r
+       return NULL;\r
+    do {\r
+       ret = find234(dp->bywidget, w, uctrl_cmp_bywidget_find);\r
+       if (ret)\r
+           return ret;\r
+       w = w->parent;\r
+    } while (w);\r
+    return ret;\r
+}\r
+\r
+void *dlg_get_privdata(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    return uc->privdata;\r
+}\r
+\r
+void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    uc->privdata = ptr;\r
+    uc->privdata_needs_free = FALSE;\r
+}\r
+\r
+void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    /*\r
+     * This is an internal allocation routine, so it's allowed to\r
+     * use smalloc directly.\r
+     */\r
+    uc->privdata = smalloc(size);\r
+    uc->privdata_needs_free = FALSE;\r
+    return uc->privdata;\r
+}\r
+\r
+union control *dlg_last_focused(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    if (dp->currfocus != ctrl)\r
+        return dp->currfocus;\r
+    else\r
+        return dp->lastfocus;\r
+}\r
+\r
+void dlg_radiobutton_set(union control *ctrl, void *dlg, int which)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_RADIO);\r
+    assert(uc->buttons != NULL);\r
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->buttons[which]), TRUE);\r
+}\r
+\r
+int dlg_radiobutton_get(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    int i;\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_RADIO);\r
+    assert(uc->buttons != NULL);\r
+    for (i = 0; i < uc->nbuttons; i++)\r
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->buttons[i])))\r
+           return i;\r
+    return 0;                         /* got to return something */\r
+}\r
+\r
+void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_CHECKBOX);\r
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->toplevel), checked);\r
+}\r
+\r
+int dlg_checkbox_get(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_CHECKBOX);\r
+    return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->toplevel));\r
+}\r
+\r
+void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    GtkWidget *entry;\r
+    char *tmpstring;\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX);\r
+\r
+#if GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->combo)\r
+       entry = gtk_bin_get_child(GTK_BIN(uc->combo));\r
+    else\r
+#endif\r
+    entry = uc->entry;\r
+\r
+    assert(entry != NULL);\r
+\r
+    /*\r
+     * GTK 2 implements gtk_entry_set_text by means of two separate\r
+     * operations: first delete the previous text leaving the empty\r
+     * string, then insert the new text. This causes two calls to\r
+     * the "changed" signal.\r
+     *\r
+     * The first call to "changed", if allowed to proceed normally,\r
+     * will cause an EVENT_VALCHANGE event on the edit box, causing\r
+     * a call to dlg_editbox_get() which will read the empty string\r
+     * out of the GtkEntry - and promptly write it straight into\r
+     * the Config structure, which is precisely where our `text'\r
+     * pointer is probably pointing, so the second editing\r
+     * operation will insert that instead of the string we\r
+     * originally asked for.\r
+     *\r
+     * Hence, we must take our own copy of the text before we do\r
+     * this.\r
+     */\r
+    tmpstring = dupstr(text);\r
+    gtk_entry_set_text(GTK_ENTRY(entry), tmpstring);\r
+    sfree(tmpstring);\r
+}\r
+\r
+void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX);\r
+\r
+#if GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->combo) {\r
+#if GTK_CHECK_VERSION(2,6,0)\r
+       strncpy(buffer,\r
+               gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo)),\r
+               length);\r
+#else\r
+       strncpy(buffer,\r
+               gtk_entry_get_text\r
+               (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo)))),\r
+               length);\r
+#endif\r
+       buffer[length-1] = '\0';\r
+       return;\r
+    }\r
+#endif\r
+\r
+    if (uc->entry) {\r
+       strncpy(buffer, gtk_entry_get_text(GTK_ENTRY(uc->entry)),\r
+               length);\r
+       buffer[length-1] = '\0';\r
+       return;\r
+    }\r
+\r
+    assert(!"We shouldn't get here");\r
+}\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+static void container_remove_and_destroy(GtkWidget *w, gpointer data)\r
+{\r
+    GtkContainer *cont = GTK_CONTAINER(data);\r
+    /* gtk_container_remove will unref the widget for us; we need not. */\r
+    gtk_container_remove(cont, w);\r
+}\r
+#endif\r
+\r
+/* The `listbox' functions can also apply to combo boxes. */\r
+void dlg_listbox_clear(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+          uc->ctrl->generic.type == CTRL_LISTBOX);\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->menu) {\r
+       gtk_container_foreach(GTK_CONTAINER(uc->menu),\r
+                             container_remove_and_destroy,\r
+                             GTK_CONTAINER(uc->menu));\r
+       return;\r
+    }\r
+    if (uc->list) {\r
+       gtk_list_clear_items(GTK_LIST(uc->list), 0, -1);\r
+       return;\r
+    }\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    if (uc->listmodel) {\r
+       gtk_list_store_clear(uc->listmodel);\r
+       return;\r
+    }\r
+#endif\r
+    assert(!"We shouldn't get here");\r
+}\r
+\r
+void dlg_listbox_del(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+          uc->ctrl->generic.type == CTRL_LISTBOX);\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->menu) {\r
+       gtk_container_remove\r
+           (GTK_CONTAINER(uc->menu),\r
+            g_list_nth_data(GTK_MENU_SHELL(uc->menu)->children, index));\r
+       return;\r
+    }\r
+    if (uc->list) {\r
+       gtk_list_clear_items(GTK_LIST(uc->list), index, index+1);\r
+       return;\r
+    }\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    if (uc->listmodel) {\r
+       GtkTreePath *path;\r
+       GtkTreeIter iter;\r
+       assert(uc->listmodel != NULL);\r
+       path = gtk_tree_path_new_from_indices(index, -1);\r
+       gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path);\r
+       gtk_list_store_remove(uc->listmodel, &iter);\r
+       gtk_tree_path_free(path);\r
+       return;\r
+    }\r
+#endif\r
+    assert(!"We shouldn't get here");\r
+}\r
+\r
+void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    dlg_listbox_addwithid(ctrl, dlg, text, 0);\r
+}\r
+\r
+/*\r
+ * Each listbox entry may have a numeric id associated with it.\r
+ * Note that some front ends only permit a string to be stored at\r
+ * each position, which means that _if_ you put two identical\r
+ * strings in any listbox then you MUST not assign them different\r
+ * IDs and expect to get meaningful results back.\r
+ */\r
+void dlg_listbox_addwithid(union control *ctrl, void *dlg,\r
+                          char const *text, int id)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+          uc->ctrl->generic.type == CTRL_LISTBOX);\r
+\r
+    /*\r
+     * This routine is long and complicated in both GTK 1 and 2,\r
+     * and completely different. Sigh.\r
+     */\r
+    dp->flags |= FLAG_UPDATING_COMBO_LIST;\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->menu) {\r
+       /*\r
+        * List item in a drop-down (but non-combo) list. Tabs are\r
+        * ignored; we just provide a standard menu item with the\r
+        * text.\r
+        */\r
+       GtkWidget *menuitem = gtk_menu_item_new_with_label(text);\r
+\r
+       gtk_container_add(GTK_CONTAINER(uc->menu), menuitem);\r
+       gtk_widget_show(menuitem);\r
+\r
+       gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",\r
+                           GINT_TO_POINTER(id));\r
+       gtk_signal_connect(GTK_OBJECT(menuitem), "activate",\r
+                          GTK_SIGNAL_FUNC(menuitem_activate), dp);\r
+       goto done;\r
+    }\r
+    if (uc->list && uc->entry) {\r
+       /*\r
+        * List item in a combo-box list, which means the sensible\r
+        * thing to do is make it a perfectly normal label. Hence\r
+        * tabs are disregarded.\r
+        */\r
+       GtkWidget *listitem = gtk_list_item_new_with_label(text);\r
+\r
+       gtk_container_add(GTK_CONTAINER(uc->list), listitem);\r
+       gtk_widget_show(listitem);\r
+\r
+       gtk_object_set_data(GTK_OBJECT(listitem), "user-data",\r
+                           GINT_TO_POINTER(id));\r
+       goto done;\r
+    }\r
+#endif\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    if (uc->list) {\r
+       /*\r
+        * List item in a non-combo-box list box. We make all of\r
+        * these Columns containing GtkLabels. This allows us to do\r
+        * the nasty force_left hack irrespective of whether there\r
+        * are tabs in the thing.\r
+        */\r
+       GtkWidget *listitem = gtk_list_item_new();\r
+       GtkWidget *cols = columns_new(10);\r
+       gint *percents;\r
+       int i, ncols;\r
+\r
+       /* Count the tabs in the text, and hence determine # of columns. */\r
+       ncols = 1;\r
+       for (i = 0; text[i]; i++)\r
+           if (text[i] == '\t')\r
+               ncols++;\r
+\r
+       assert(ncols <=\r
+              (uc->ctrl->listbox.ncols ? uc->ctrl->listbox.ncols : 1));\r
+       percents = snewn(ncols, gint);\r
+       percents[ncols-1] = 100;\r
+       for (i = 0; i < ncols-1; i++) {\r
+           percents[i] = uc->ctrl->listbox.percentages[i];\r
+           percents[ncols-1] -= percents[i];\r
+       }\r
+       columns_set_cols(COLUMNS(cols), ncols, percents);\r
+       sfree(percents);\r
+\r
+       for (i = 0; i < ncols; i++) {\r
+           int len = strcspn(text, "\t");\r
+           char *dup = dupprintf("%.*s", len, text);\r
+           GtkWidget *label;\r
+\r
+           text += len;\r
+           if (*text) text++;\r
+           label = gtk_label_new(dup);\r
+           sfree(dup);\r
+\r
+           columns_add(COLUMNS(cols), label, i, 1);\r
+           columns_force_left_align(COLUMNS(cols), label);\r
+           gtk_widget_show(label);\r
+       }\r
+       gtk_container_add(GTK_CONTAINER(listitem), cols);\r
+       gtk_widget_show(cols);\r
+       gtk_container_add(GTK_CONTAINER(uc->list), listitem);\r
+       gtk_widget_show(listitem);\r
+\r
+        if (ctrl->listbox.multisel) {\r
+            gtk_signal_connect(GTK_OBJECT(listitem), "key_press_event",\r
+                               GTK_SIGNAL_FUNC(listitem_multi_key), uc->adj);\r
+        } else {\r
+            gtk_signal_connect(GTK_OBJECT(listitem), "key_press_event",\r
+                               GTK_SIGNAL_FUNC(listitem_single_key), uc->adj);\r
+        }\r
+        gtk_signal_connect(GTK_OBJECT(listitem), "focus_in_event",\r
+                           GTK_SIGNAL_FUNC(widget_focus), dp);\r
+       gtk_signal_connect(GTK_OBJECT(listitem), "button_press_event",\r
+                          GTK_SIGNAL_FUNC(listitem_button_press), dp);\r
+       gtk_signal_connect(GTK_OBJECT(listitem), "button_release_event",\r
+                          GTK_SIGNAL_FUNC(listitem_button_release), dp);\r
+       gtk_object_set_data(GTK_OBJECT(listitem), "user-data",\r
+                           GINT_TO_POINTER(id));\r
+       goto done;\r
+    }\r
+#else\r
+    if (uc->listmodel) {\r
+       GtkTreeIter iter;\r
+       int i, cols;\r
+\r
+       dp->flags |= FLAG_UPDATING_LISTBOX;/* inhibit drag-list update */\r
+       gtk_list_store_append(uc->listmodel, &iter);\r
+       dp->flags &= ~FLAG_UPDATING_LISTBOX;\r
+       gtk_list_store_set(uc->listmodel, &iter, 0, id, -1);\r
+\r
+       /*\r
+        * Now go through text and divide it into columns at the tabs,\r
+        * as necessary.\r
+        */\r
+       cols = (uc->ctrl->generic.type == CTRL_LISTBOX ? ctrl->listbox.ncols : 1);\r
+       cols = cols ? cols : 1;\r
+       for (i = 0; i < cols; i++) {\r
+           int collen = strcspn(text, "\t");\r
+           char *tmpstr = snewn(collen+1, char);\r
+           memcpy(tmpstr, text, collen);\r
+           tmpstr[collen] = '\0';\r
+           gtk_list_store_set(uc->listmodel, &iter, i+1, tmpstr, -1);\r
+           sfree(tmpstr);\r
+           text += collen;\r
+           if (*text) text++;\r
+       }\r
+       goto done;\r
+    }\r
+#endif\r
+    assert(!"We shouldn't get here");\r
+    done:\r
+    dp->flags &= ~FLAG_UPDATING_COMBO_LIST;\r
+}\r
+\r
+int dlg_listbox_getid(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+          uc->ctrl->generic.type == CTRL_LISTBOX);\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->menu || uc->list) {\r
+       GList *children;\r
+       GtkObject *item;\r
+\r
+       children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :\r
+                                                       uc->list));\r
+       item = GTK_OBJECT(g_list_nth_data(children, index));\r
+       g_list_free(children);\r
+\r
+       return GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),\r
+                                                  "user-data"));\r
+    }\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    if (uc->listmodel) {\r
+       GtkTreePath *path;\r
+       GtkTreeIter iter;\r
+       int ret;\r
+\r
+       path = gtk_tree_path_new_from_indices(index, -1);\r
+       gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path);\r
+       gtk_tree_model_get(GTK_TREE_MODEL(uc->listmodel), &iter, 0, &ret, -1);\r
+       gtk_tree_path_free(path);\r
+\r
+       return ret;\r
+    }\r
+#endif\r
+    assert(!"We shouldn't get here");\r
+    return -1;                        /* placate dataflow analysis */\r
+}\r
+\r
+/* dlg_listbox_index returns <0 if no single element is selected. */\r
+int dlg_listbox_index(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+          uc->ctrl->generic.type == CTRL_LISTBOX);\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->menu || uc->list) {\r
+       GList *children;\r
+       GtkWidget *item, *activeitem;\r
+       int i;\r
+       int selected = -1;\r
+\r
+       if (uc->menu)\r
+           activeitem = gtk_menu_get_active(GTK_MENU(uc->menu));\r
+       else\r
+           activeitem = NULL;         /* unnecessarily placate gcc */\r
+\r
+       children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :\r
+                                                       uc->list));\r
+       for (i = 0; children!=NULL && (item = GTK_WIDGET(children->data))!=NULL;\r
+            i++, children = children->next) {\r
+           if (uc->menu ? activeitem == item :\r
+               GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED) {\r
+               if (selected == -1)\r
+                   selected = i;\r
+               else\r
+                   selected = -2;\r
+           }\r
+       }\r
+       g_list_free(children);\r
+       return selected < 0 ? -1 : selected;\r
+    }\r
+#else\r
+    if (uc->combo) {\r
+       /*\r
+        * This API function already does the right thing in the\r
+        * case of no current selection.\r
+        */\r
+       return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo));\r
+    }\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    if (uc->treeview) {\r
+       GtkTreeSelection *treesel;\r
+       GtkTreePath *path;\r
+       GtkTreeModel *model;\r
+       GList *sellist;\r
+       gint *indices;\r
+       int ret;\r
+\r
+       assert(uc->treeview != NULL);\r
+       treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview));\r
+\r
+       if (gtk_tree_selection_count_selected_rows(treesel) != 1)\r
+           return -1;\r
+\r
+       sellist = gtk_tree_selection_get_selected_rows(treesel, &model);\r
+\r
+       assert(sellist && sellist->data);\r
+       path = sellist->data;\r
+\r
+       if (gtk_tree_path_get_depth(path) != 1) {\r
+           ret = -1;\r
+       } else {\r
+           indices = gtk_tree_path_get_indices(path);\r
+           if (!indices) {\r
+               ret = -1;\r
+           } else {\r
+               ret = indices[0];\r
+           }\r
+       }\r
+\r
+       g_list_foreach(sellist, (GFunc)gtk_tree_path_free, NULL);\r
+       g_list_free(sellist);\r
+\r
+       return ret;\r
+    }\r
+#endif\r
+    assert(!"We shouldn't get here");\r
+    return -1;                        /* placate dataflow analysis */\r
+}\r
+\r
+int dlg_listbox_issel(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+          uc->ctrl->generic.type == CTRL_LISTBOX);\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->menu || uc->list) {\r
+       GList *children;\r
+       GtkWidget *item, *activeitem;\r
+\r
+       assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+              uc->ctrl->generic.type == CTRL_LISTBOX);\r
+       assert(uc->menu != NULL || uc->list != NULL);\r
+\r
+       children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu :\r
+                                                       uc->list));\r
+       item = GTK_WIDGET(g_list_nth_data(children, index));\r
+       g_list_free(children);\r
+\r
+       if (uc->menu) {\r
+           activeitem = gtk_menu_get_active(GTK_MENU(uc->menu));\r
+           return item == activeitem;\r
+       } else {\r
+           return GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED;\r
+       }\r
+    }\r
+#else\r
+    if (uc->combo) {\r
+       /*\r
+        * This API function already does the right thing in the\r
+        * case of no current selection.\r
+        */\r
+       return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo)) == index;\r
+    }\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    if (uc->treeview) {\r
+       GtkTreeSelection *treesel;\r
+       GtkTreePath *path;\r
+       int ret;\r
+\r
+       assert(uc->treeview != NULL);\r
+       treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview));\r
+\r
+       path = gtk_tree_path_new_from_indices(index, -1);\r
+       ret = gtk_tree_selection_path_is_selected(treesel, path);\r
+       gtk_tree_path_free(path);\r
+\r
+       return ret;\r
+    }\r
+#endif\r
+    assert(!"We shouldn't get here");\r
+    return -1;                        /* placate dataflow analysis */\r
+}\r
+\r
+void dlg_listbox_select(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_EDITBOX ||\r
+          uc->ctrl->generic.type == CTRL_LISTBOX);\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+    if (uc->optmenu) {\r
+       gtk_option_menu_set_history(GTK_OPTION_MENU(uc->optmenu), index);\r
+       return;\r
+    } \r
+    if (uc->list) {\r
+        int nitems;\r
+        GList *items;\r
+        gdouble newtop, newbot;\r
+\r
+       gtk_list_select_item(GTK_LIST(uc->list), index);\r
+\r
+        /*\r
+         * Scroll the list box if necessary to ensure the newly\r
+         * selected item is visible.\r
+         */\r
+        items = gtk_container_children(GTK_CONTAINER(uc->list));\r
+        nitems = g_list_length(items);\r
+        if (nitems > 0) {\r
+            int modified = FALSE;\r
+            g_list_free(items);\r
+            newtop = uc->adj->lower +\r
+                (uc->adj->upper - uc->adj->lower) * index / nitems;\r
+            newbot = uc->adj->lower +\r
+                (uc->adj->upper - uc->adj->lower) * (index+1) / nitems;\r
+            if (uc->adj->value > newtop) {\r
+                modified = TRUE;\r
+                uc->adj->value = newtop;\r
+            } else if (uc->adj->value < newbot - uc->adj->page_size) {\r
+                modified = TRUE;\r
+                uc->adj->value = newbot - uc->adj->page_size;\r
+            }\r
+            if (modified)\r
+                gtk_adjustment_value_changed(uc->adj);\r
+        }\r
+       return;\r
+    }\r
+#else\r
+    if (uc->combo) {\r
+       gtk_combo_box_set_active(GTK_COMBO_BOX(uc->combo), index);\r
+       return;\r
+    }\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    if (uc->treeview) {\r
+       GtkTreeSelection *treesel;\r
+       GtkTreePath *path;\r
+\r
+       treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview));\r
+\r
+       path = gtk_tree_path_new_from_indices(index, -1);\r
+       gtk_tree_selection_select_path(treesel, path);\r
+       gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(uc->treeview),\r
+                                    path, NULL, FALSE, 0.0, 0.0);\r
+       gtk_tree_path_free(path);\r
+       return;\r
+    }\r
+#endif\r
+    assert(!"We shouldn't get here");\r
+}\r
+\r
+void dlg_text_set(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    assert(uc->ctrl->generic.type == CTRL_TEXT);\r
+    assert(uc->text != NULL);\r
+\r
+    gtk_label_set_text(GTK_LABEL(uc->text), text);\r
+}\r
+\r
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    switch (uc->ctrl->generic.type) {\r
+      case CTRL_BUTTON:\r
+       gtk_label_set_text(GTK_LABEL(uc->toplevel), text);\r
+       shortcut_highlight(uc->toplevel, ctrl->button.shortcut);\r
+       break;\r
+      case CTRL_CHECKBOX:\r
+       gtk_label_set_text(GTK_LABEL(uc->toplevel), text);\r
+       shortcut_highlight(uc->toplevel, ctrl->checkbox.shortcut);\r
+       break;\r
+      case CTRL_RADIO:\r
+       gtk_label_set_text(GTK_LABEL(uc->label), text);\r
+       shortcut_highlight(uc->label, ctrl->radio.shortcut);\r
+       break;\r
+      case CTRL_EDITBOX:\r
+       gtk_label_set_text(GTK_LABEL(uc->label), text);\r
+       shortcut_highlight(uc->label, ctrl->editbox.shortcut);\r
+       break;\r
+      case CTRL_FILESELECT:\r
+       gtk_label_set_text(GTK_LABEL(uc->label), text);\r
+       shortcut_highlight(uc->label, ctrl->fileselect.shortcut);\r
+       break;\r
+      case CTRL_FONTSELECT:\r
+       gtk_label_set_text(GTK_LABEL(uc->label), text);\r
+       shortcut_highlight(uc->label, ctrl->fontselect.shortcut);\r
+       break;\r
+      case CTRL_LISTBOX:\r
+       gtk_label_set_text(GTK_LABEL(uc->label), text);\r
+       shortcut_highlight(uc->label, ctrl->listbox.shortcut);\r
+       break;\r
+      default:\r
+       assert(!"This shouldn't happen");\r
+       break;\r
+    }\r
+}\r
+\r
+void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_FILESELECT);\r
+    assert(uc->entry != NULL);\r
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), fn.path);\r
+}\r
+\r
+void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_FILESELECT);\r
+    assert(uc->entry != NULL);\r
+    strncpy(fn->path, gtk_entry_get_text(GTK_ENTRY(uc->entry)),\r
+           lenof(fn->path));\r
+    fn->path[lenof(fn->path)-1] = '\0';\r
+}\r
+\r
+void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_FONTSELECT);\r
+    assert(uc->entry != NULL);\r
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), fs.name);\r
+}\r
+\r
+void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    assert(uc->ctrl->generic.type == CTRL_FONTSELECT);\r
+    assert(uc->entry != NULL);\r
+    strncpy(fs->name, gtk_entry_get_text(GTK_ENTRY(uc->entry)),\r
+           lenof(fs->name));\r
+    fs->name[lenof(fs->name)-1] = '\0';\r
+}\r
+\r
+/*\r
+ * Bracketing a large set of updates in these two functions will\r
+ * cause the front end (if possible) to delay updating the screen\r
+ * until it's all complete, thus avoiding flicker.\r
+ */\r
+void dlg_update_start(union control *ctrl, void *dlg)\r
+{\r
+    /*\r
+     * Apparently we can't do this at all in GTK. GtkCList supports\r
+     * freeze and thaw, but not GtkList. Bah.\r
+     */\r
+}\r
+\r
+void dlg_update_done(union control *ctrl, void *dlg)\r
+{\r
+    /*\r
+     * Apparently we can't do this at all in GTK. GtkCList supports\r
+     * freeze and thaw, but not GtkList. Bah.\r
+     */\r
+}\r
+\r
+void dlg_set_focus(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+\r
+    switch (ctrl->generic.type) {\r
+      case CTRL_CHECKBOX:\r
+      case CTRL_BUTTON:\r
+        /* Check boxes and buttons get the focus _and_ get toggled. */\r
+        gtk_widget_grab_focus(uc->toplevel);\r
+        break;\r
+      case CTRL_FILESELECT:\r
+      case CTRL_FONTSELECT:\r
+      case CTRL_EDITBOX:\r
+       if (uc->entry) {\r
+           /* Anything containing an edit box gets that focused. */\r
+           gtk_widget_grab_focus(uc->entry);\r
+       }\r
+#if GTK_CHECK_VERSION(2,4,0)\r
+       else if (uc->combo) {\r
+           /* Failing that, there'll be a combo box. */\r
+           gtk_widget_grab_focus(uc->combo);\r
+       }\r
+#endif\r
+        break;\r
+      case CTRL_RADIO:\r
+        /*\r
+         * Radio buttons: we find the currently selected button and\r
+         * focus it.\r
+         */\r
+        {\r
+            int i;\r
+            for (i = 0; i < ctrl->radio.nbuttons; i++)\r
+                if (gtk_toggle_button_get_active\r
+                    (GTK_TOGGLE_BUTTON(uc->buttons[i]))) {\r
+                    gtk_widget_grab_focus(uc->buttons[i]);\r
+                }\r
+        }\r
+        break;\r
+      case CTRL_LISTBOX:\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+        if (uc->optmenu) {\r
+            gtk_widget_grab_focus(uc->optmenu);\r
+           break;\r
+        }\r
+#else\r
+       if (uc->combo) {\r
+           gtk_widget_grab_focus(uc->combo);\r
+           break;\r
+       }\r
+#endif\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+       if (uc->list) {\r
+           /*\r
+            * For GTK-1 style list boxes, we tell it to focus one\r
+            * of its children, which appears to do the Right\r
+            * Thing.\r
+            */\r
+            gtk_container_focus(GTK_CONTAINER(uc->list), GTK_DIR_TAB_FORWARD);\r
+           break;\r
+       }\r
+#else\r
+       if (uc->treeview) {\r
+           gtk_widget_grab_focus(uc->treeview);\r
+           break;\r
+       }\r
+#endif\r
+       assert(!"We shouldn't get here");\r
+        break;\r
+    }\r
+}\r
+\r
+/*\r
+ * During event processing, you might well want to give an error\r
+ * indication to the user. dlg_beep() is a quick and easy generic\r
+ * error; dlg_error() puts up a message-box or equivalent.\r
+ */\r
+void dlg_beep(void *dlg)\r
+{\r
+    gdk_beep();\r
+}\r
+\r
+static void errmsg_button_clicked(GtkButton *button, gpointer data)\r
+{\r
+    gtk_widget_destroy(GTK_WIDGET(data));\r
+}\r
+\r
+static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child)\r
+{\r
+    gint x, y, w, h, dx, dy;\r
+    GtkRequisition req;\r
+    gtk_window_set_position(GTK_WINDOW(child), GTK_WIN_POS_NONE);\r
+    gtk_widget_size_request(GTK_WIDGET(child), &req);\r
+\r
+    gdk_window_get_origin(GTK_WIDGET(parent)->window, &x, &y);\r
+    gdk_window_get_size(GTK_WIDGET(parent)->window, &w, &h);\r
+\r
+    /*\r
+     * One corner of the transient will be offset inwards, by 1/4\r
+     * of the parent window's size, from the corresponding corner\r
+     * of the parent window. The corner will be chosen so as to\r
+     * place the transient closer to the centre of the screen; this\r
+     * should avoid transients going off the edge of the screen on\r
+     * a regular basis.\r
+     */\r
+    if (x + w/2 < gdk_screen_width() / 2)\r
+        dx = x + w/4;                  /* work from left edges */\r
+    else\r
+        dx = x + 3*w/4 - req.width;    /* work from right edges */\r
+    if (y + h/2 < gdk_screen_height() / 2)\r
+        dy = y + h/4;                  /* work from top edges */\r
+    else\r
+        dy = y + 3*h/4 - req.height;   /* work from bottom edges */\r
+    gtk_widget_set_uposition(GTK_WIDGET(child), dx, dy);\r
+}\r
+\r
+void dlg_error_msg(void *dlg, char *msg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    GtkWidget *window, *hbox, *text, *ok;\r
+\r
+    window = gtk_dialog_new();\r
+    text = gtk_label_new(msg);\r
+    gtk_misc_set_alignment(GTK_MISC(text), 0.0, 0.0);\r
+    hbox = gtk_hbox_new(FALSE, 0);\r
+    gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 20);\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),\r
+                       hbox, FALSE, FALSE, 20);\r
+    gtk_widget_show(text);\r
+    gtk_widget_show(hbox);\r
+    gtk_window_set_title(GTK_WINDOW(window), "Error");\r
+    gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);\r
+    ok = gtk_button_new_with_label("OK");\r
+    gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window)->action_area),\r
+                     ok, FALSE, FALSE, 0);\r
+    gtk_widget_show(ok);\r
+    GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);\r
+    gtk_window_set_default(GTK_WINDOW(window), ok);\r
+    gtk_signal_connect(GTK_OBJECT(ok), "clicked",\r
+                       GTK_SIGNAL_FUNC(errmsg_button_clicked), window);\r
+    gtk_signal_connect(GTK_OBJECT(window), "destroy",\r
+                       GTK_SIGNAL_FUNC(window_destroy), NULL);\r
+    gtk_window_set_modal(GTK_WINDOW(window), TRUE);\r
+    gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dp->window));\r
+    set_transient_window_pos(dp->window, window);\r
+    gtk_widget_show(window);\r
+    gtk_main();\r
+}\r
+\r
+/*\r
+ * This function signals to the front end that the dialog's\r
+ * processing is completed, and passes an integer value (typically\r
+ * a success status).\r
+ */\r
+void dlg_end(void *dlg, int value)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    dp->retval = value;\r
+    gtk_widget_destroy(dp->window);\r
+}\r
+\r
+void dlg_refresh(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc;\r
+\r
+    if (ctrl) {\r
+       if (ctrl->generic.handler != NULL)\r
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);\r
+    } else {\r
+       int i;\r
+\r
+       for (i = 0; (uc = index234(dp->byctrl, i)) != NULL; i++) {\r
+           assert(uc->ctrl != NULL);\r
+           if (uc->ctrl->generic.handler != NULL)\r
+               uc->ctrl->generic.handler(uc->ctrl, dp,\r
+                                         dp->data, EVENT_REFRESH);\r
+       }\r
+    }\r
+}\r
+\r
+void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct uctrl *uc = dlg_find_byctrl(dp, ctrl);\r
+    gdouble cvals[4];\r
+\r
+    GtkWidget *coloursel =\r
+       gtk_color_selection_dialog_new("Select a colour");\r
+    GtkColorSelectionDialog *ccs = GTK_COLOR_SELECTION_DIALOG(coloursel);\r
+\r
+    dp->coloursel_result.ok = FALSE;\r
+\r
+    gtk_window_set_modal(GTK_WINDOW(coloursel), TRUE);\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(ccs->colorsel), FALSE);\r
+#else\r
+    gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(ccs->colorsel), FALSE);\r
+#endif\r
+    cvals[0] = r / 255.0;\r
+    cvals[1] = g / 255.0;\r
+    cvals[2] = b / 255.0;\r
+    cvals[3] = 1.0;                   /* fully opaque! */\r
+    gtk_color_selection_set_color(GTK_COLOR_SELECTION(ccs->colorsel), cvals);\r
+\r
+    gtk_object_set_data(GTK_OBJECT(ccs->ok_button), "user-data",\r
+                       (gpointer)coloursel);\r
+    gtk_object_set_data(GTK_OBJECT(ccs->cancel_button), "user-data",\r
+                       (gpointer)coloursel);\r
+    gtk_object_set_data(GTK_OBJECT(coloursel), "user-data", (gpointer)uc);\r
+    gtk_signal_connect(GTK_OBJECT(ccs->ok_button), "clicked",\r
+                      GTK_SIGNAL_FUNC(coloursel_ok), (gpointer)dp);\r
+    gtk_signal_connect(GTK_OBJECT(ccs->cancel_button), "clicked",\r
+                      GTK_SIGNAL_FUNC(coloursel_cancel), (gpointer)dp);\r
+    gtk_signal_connect_object(GTK_OBJECT(ccs->ok_button), "clicked",\r
+                             GTK_SIGNAL_FUNC(gtk_widget_destroy),\r
+                             (gpointer)coloursel);\r
+    gtk_signal_connect_object(GTK_OBJECT(ccs->cancel_button), "clicked",\r
+                             GTK_SIGNAL_FUNC(gtk_widget_destroy),\r
+                             (gpointer)coloursel);\r
+    gtk_widget_show(coloursel);\r
+}\r
+\r
+int dlg_coloursel_results(union control *ctrl, void *dlg,\r
+                         int *r, int *g, int *b)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    if (dp->coloursel_result.ok) {\r
+       *r = dp->coloursel_result.r;\r
+       *g = dp->coloursel_result.g;\r
+       *b = dp->coloursel_result.b;\r
+       return 1;\r
+    } else\r
+       return 0;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Signal handlers while the dialog box is active.\r
+ */\r
+\r
+static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,\r
+                             gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, widget);\r
+    union control *focus;\r
+\r
+    if (uc && uc->ctrl)\r
+        focus = uc->ctrl;\r
+    else\r
+        focus = NULL;\r
+\r
+    if (focus != dp->currfocus) {\r
+        dp->lastfocus = dp->currfocus;\r
+        dp->currfocus = focus;\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+\r
+static void button_clicked(GtkButton *button, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);\r
+}\r
+\r
+static void button_toggled(GtkToggleButton *tb, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tb));\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+}\r
+\r
+static gboolean editbox_key(GtkWidget *widget, GdkEventKey *event,\r
+                           gpointer data)\r
+{\r
+    /*\r
+     * GtkEntry has a nasty habit of eating the Return key, which\r
+     * is unhelpful since it doesn't actually _do_ anything with it\r
+     * (it calls gtk_widget_activate, but our edit boxes never need\r
+     * activating). So I catch Return before GtkEntry sees it, and\r
+     * pass it straight on to the parent widget. Effect: hitting\r
+     * Return in an edit box will now activate the default button\r
+     * in the dialog just like it will everywhere else.\r
+     */\r
+    if (event->keyval == GDK_Return && widget->parent != NULL) {\r
+       gboolean return_val;\r
+       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");\r
+       gtk_signal_emit_by_name(GTK_OBJECT(widget->parent), "key_press_event",\r
+                               event, &return_val);\r
+       return return_val;\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+static void editbox_changed(GtkEditable *ed, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    if (!(dp->flags & FLAG_UPDATING_COMBO_LIST)) {\r
+       struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));\r
+       uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+    }\r
+}\r
+\r
+static gboolean editbox_lostfocus(GtkWidget *ed, GdkEventFocus *event,\r
+                                 gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_REFRESH);\r
+    return FALSE;\r
+}\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+\r
+/*\r
+ * GTK 1 list box event handlers.\r
+ */\r
+\r
+static gboolean listitem_key(GtkWidget *item, GdkEventKey *event,\r
+                            gpointer data, int multiple)\r
+{\r
+    GtkAdjustment *adj = GTK_ADJUSTMENT(data);\r
+\r
+    if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||\r
+        event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||\r
+        event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||\r
+        event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down) {\r
+        /*\r
+         * Up, Down, PgUp or PgDn have been pressed on a ListItem\r
+         * in a list box. So, if the list box is single-selection:\r
+         * \r
+         *  - if the list item in question isn't already selected,\r
+         *    we simply select it.\r
+         *  - otherwise, we find the next one (or next\r
+         *    however-far-away) in whichever direction we're going,\r
+         *    and select that.\r
+         *     + in this case, we must also fiddle with the\r
+         *       scrollbar to ensure the newly selected item is\r
+         *       actually visible.\r
+         * \r
+         * If it's multiple-selection, we do all of the above\r
+         * except actually selecting anything, so we move the focus\r
+         * and fiddle the scrollbar to follow it.\r
+         */\r
+        GtkWidget *list = item->parent;\r
+\r
+        gtk_signal_emit_stop_by_name(GTK_OBJECT(item), "key_press_event");\r
+\r
+        if (!multiple &&\r
+            GTK_WIDGET_STATE(item) != GTK_STATE_SELECTED) {\r
+                gtk_list_select_child(GTK_LIST(list), item);\r
+        } else {\r
+            int direction =\r
+                (event->keyval==GDK_Up || event->keyval==GDK_KP_Up ||\r
+                 event->keyval==GDK_Page_Up || event->keyval==GDK_KP_Page_Up)\r
+                ? -1 : +1;\r
+            int step =\r
+                (event->keyval==GDK_Page_Down || \r
+                 event->keyval==GDK_KP_Page_Down ||\r
+                 event->keyval==GDK_Page_Up || event->keyval==GDK_KP_Page_Up)\r
+                ? 2 : 1;\r
+            int i, n;\r
+            GList *children, *chead;\r
+\r
+            chead = children = gtk_container_children(GTK_CONTAINER(list));\r
+\r
+            n = g_list_length(children);\r
+\r
+            if (step == 2) {\r
+                /*\r
+                 * Figure out how many list items to a screenful,\r
+                 * and adjust the step appropriately.\r
+                 */\r
+                step = 0.5 + adj->page_size * n / (adj->upper - adj->lower);\r
+                step--;                /* go by one less than that */\r
+            }\r
+\r
+            i = 0;\r
+            while (children != NULL) {\r
+                if (item == children->data)\r
+                    break;\r
+                children = children->next;\r
+                i++;\r
+            }\r
+\r
+            while (step > 0) {\r
+                if (direction < 0 && i > 0)\r
+                    children = children->prev, i--;\r
+                else if (direction > 0 && i < n-1)\r
+                    children = children->next, i++;\r
+                step--;\r
+            }\r
+\r
+            if (children && children->data) {\r
+                if (!multiple)\r
+                    gtk_list_select_child(GTK_LIST(list),\r
+                                          GTK_WIDGET(children->data));\r
+                gtk_widget_grab_focus(GTK_WIDGET(children->data));\r
+                gtk_adjustment_clamp_page\r
+                    (adj,\r
+                     adj->lower + (adj->upper-adj->lower) * i / n,\r
+                     adj->lower + (adj->upper-adj->lower) * (i+1) / n);\r
+            }\r
+\r
+            g_list_free(chead);\r
+        }\r
+        return TRUE;\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+\r
+static gboolean listitem_single_key(GtkWidget *item, GdkEventKey *event,\r
+                                   gpointer data)\r
+{\r
+    return listitem_key(item, event, data, FALSE);\r
+}\r
+\r
+static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event,\r
+                                  gpointer data)\r
+{\r
+    return listitem_key(item, event, data, TRUE);\r
+}\r
+\r
+static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event,\r
+                                     gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));\r
+    switch (event->type) {\r
+    default:\r
+    case GDK_BUTTON_PRESS: uc->nclicks = 1; break;\r
+    case GDK_2BUTTON_PRESS: uc->nclicks = 2; break;\r
+    case GDK_3BUTTON_PRESS: uc->nclicks = 3; break;\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event,\r
+                                       gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));\r
+    if (uc->nclicks>1) {\r
+       uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);\r
+        return TRUE;\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+static void list_selchange(GtkList *list, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(list));\r
+    if (!uc) return;\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);\r
+}\r
+\r
+static void draglist_move(struct dlgparam *dp, struct uctrl *uc, int direction)\r
+{\r
+    int index = dlg_listbox_index(uc->ctrl, dp);\r
+    GList *children = gtk_container_children(GTK_CONTAINER(uc->list));\r
+    GtkWidget *child;\r
+\r
+    if ((index < 0) ||\r
+       (index == 0 && direction < 0) ||\r
+       (index == g_list_length(children)-1 && direction > 0)) {\r
+       gdk_beep();\r
+       return;\r
+    }\r
+\r
+    child = g_list_nth_data(children, index);\r
+    gtk_widget_ref(child);\r
+    gtk_list_clear_items(GTK_LIST(uc->list), index, index+1);\r
+    g_list_free(children);\r
+\r
+    children = NULL;\r
+    children = g_list_append(children, child);\r
+    gtk_list_insert_items(GTK_LIST(uc->list), children, index + direction);\r
+    gtk_list_select_item(GTK_LIST(uc->list), index + direction);\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+}\r
+\r
+static void draglist_up(GtkButton *button, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));\r
+    draglist_move(dp, uc, -1);\r
+}\r
+\r
+static void draglist_down(GtkButton *button, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));\r
+    draglist_move(dp, uc, +1);\r
+}\r
+\r
+#else /* !GTK_CHECK_VERSION(2,0,0) */\r
+\r
+/*\r
+ * GTK 2 list box event handlers.\r
+ */\r
+\r
+static void listbox_doubleclick(GtkTreeView *treeview, GtkTreePath *path,\r
+                               GtkTreeViewColumn *column, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(treeview));\r
+    if (uc)\r
+       uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);\r
+}\r
+\r
+static void listbox_selchange(GtkTreeSelection *treeselection,\r
+                             gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    GtkTreeView *tree = gtk_tree_selection_get_tree_view(treeselection);\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tree));\r
+    if (uc)\r
+       uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);\r
+}\r
+\r
+struct draglist_valchange_ctx {\r
+    struct uctrl *uc;\r
+    struct dlgparam *dp;\r
+};\r
+\r
+static gboolean draglist_valchange(gpointer data)\r
+{\r
+    struct draglist_valchange_ctx *ctx =\r
+       (struct draglist_valchange_ctx *)data;\r
+\r
+    ctx->uc->ctrl->generic.handler(ctx->uc->ctrl, ctx->dp,\r
+                                  ctx->dp->data, EVENT_VALCHANGE);\r
+\r
+    sfree(ctx);\r
+\r
+    return FALSE;\r
+}\r
+\r
+static void listbox_reorder(GtkTreeModel *treemodel, GtkTreePath *path,\r
+                           GtkTreeIter *iter, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    gpointer tree;\r
+    struct uctrl *uc;\r
+\r
+    if (dp->flags & FLAG_UPDATING_LISTBOX)\r
+       return;                        /* not a user drag operation */\r
+\r
+    tree = g_object_get_data(G_OBJECT(treemodel), "user-data");\r
+    uc = dlg_find_bywidget(dp, GTK_WIDGET(tree));\r
+    if (uc) {\r
+       /*\r
+        * We should cause EVENT_VALCHANGE on the list box, now\r
+        * that its rows have been reordered. However, the GTK 2\r
+        * docs say that at the point this signal is received the\r
+        * new row might not have actually been filled in yet.\r
+        *\r
+        * (So what smegging use is it then, eh? Don't suppose it\r
+        * occurred to you at any point that letting the\r
+        * application know _after_ the reordering was compelete\r
+        * might be helpful to someone?)\r
+        *\r
+        * To get round this, I schedule an idle function, which I\r
+        * hope won't be called until the main event loop is\r
+        * re-entered after the drag-and-drop handler has finished\r
+        * furtling with the list store.\r
+        */\r
+       struct draglist_valchange_ctx *ctx =\r
+           snew(struct draglist_valchange_ctx);\r
+       ctx->uc = uc;\r
+       ctx->dp = dp;\r
+       g_idle_add(draglist_valchange, ctx);\r
+    }\r
+}\r
+\r
+#endif /* !GTK_CHECK_VERSION(2,0,0) */\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+\r
+static void menuitem_activate(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    GtkWidget *menushell = GTK_WIDGET(item)->parent;\r
+    gpointer optmenu = gtk_object_get_data(GTK_OBJECT(menushell), "user-data");\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(optmenu));\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);\r
+}\r
+\r
+#else\r
+\r
+static void droplist_selchange(GtkComboBox *combo, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(combo));\r
+    if (uc)\r
+       uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);\r
+}\r
+\r
+#endif /* !GTK_CHECK_VERSION(2,4,0) */\r
+\r
+static void filesel_ok(GtkButton *button, gpointer data)\r
+{\r
+    /* struct dlgparam *dp = (struct dlgparam *)data; */\r
+    gpointer filesel = gtk_object_get_data(GTK_OBJECT(button), "user-data");\r
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(filesel), "user-data");\r
+    const char *name = gtk_file_selection_get_filename\r
+       (GTK_FILE_SELECTION(filesel));\r
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), name);\r
+}\r
+\r
+static void fontsel_ok(GtkButton *button, gpointer data)\r
+{\r
+    /* struct dlgparam *dp = (struct dlgparam *)data; */\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+\r
+    gpointer fontsel = gtk_object_get_data(GTK_OBJECT(button), "user-data");\r
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(fontsel), "user-data");\r
+    const char *name = gtk_font_selection_dialog_get_font_name\r
+       (GTK_FONT_SELECTION_DIALOG(fontsel));\r
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), name);\r
+\r
+#else\r
+\r
+    unifontsel *fontsel = (unifontsel *)gtk_object_get_data\r
+       (GTK_OBJECT(button), "user-data");\r
+    struct uctrl *uc = (struct uctrl *)fontsel->user_data;\r
+    char *name = unifontsel_get_name(fontsel);\r
+    assert(name);              /* should always be ok after OK pressed */\r
+    gtk_entry_set_text(GTK_ENTRY(uc->entry), name);\r
+    sfree(name);\r
+\r
+#endif\r
+}\r
+\r
+static void coloursel_ok(GtkButton *button, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    gpointer coloursel = gtk_object_get_data(GTK_OBJECT(button), "user-data");\r
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(coloursel), "user-data");\r
+    gdouble cvals[4];\r
+    gtk_color_selection_get_color\r
+       (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(coloursel)->colorsel),\r
+        cvals);\r
+    dp->coloursel_result.r = (int) (255 * cvals[0]);\r
+    dp->coloursel_result.g = (int) (255 * cvals[1]);\r
+    dp->coloursel_result.b = (int) (255 * cvals[2]);\r
+    dp->coloursel_result.ok = TRUE;\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);\r
+}\r
+\r
+static void coloursel_cancel(GtkButton *button, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    gpointer coloursel = gtk_object_get_data(GTK_OBJECT(button), "user-data");\r
+    struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(coloursel), "user-data");\r
+    dp->coloursel_result.ok = FALSE;\r
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);\r
+}\r
+\r
+static void filefont_clicked(GtkButton *button, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));\r
+\r
+    if (uc->ctrl->generic.type == CTRL_FILESELECT) {\r
+       GtkWidget *filesel =\r
+           gtk_file_selection_new(uc->ctrl->fileselect.title);\r
+       gtk_window_set_modal(GTK_WINDOW(filesel), TRUE);\r
+       gtk_object_set_data\r
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",\r
+            (gpointer)filesel);\r
+       gtk_object_set_data(GTK_OBJECT(filesel), "user-data", (gpointer)uc);\r
+       gtk_signal_connect\r
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",\r
+            GTK_SIGNAL_FUNC(filesel_ok), (gpointer)dp);\r
+       gtk_signal_connect_object\r
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",\r
+            GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);\r
+       gtk_signal_connect_object\r
+           (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",\r
+            GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);\r
+       gtk_widget_show(filesel);\r
+    }\r
+\r
+    if (uc->ctrl->generic.type == CTRL_FONTSELECT) {\r
+        const gchar *fontname = gtk_entry_get_text(GTK_ENTRY(uc->entry));\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+\r
+       /*\r
+        * Use the GTK 1 standard font selector.\r
+        */\r
+\r
+       gchar *spacings[] = { "c", "m", NULL };\r
+       GtkWidget *fontsel =\r
+           gtk_font_selection_dialog_new("Select a font");\r
+       gtk_window_set_modal(GTK_WINDOW(fontsel), TRUE);\r
+       gtk_font_selection_dialog_set_filter\r
+           (GTK_FONT_SELECTION_DIALOG(fontsel),\r
+            GTK_FONT_FILTER_BASE, GTK_FONT_ALL,\r
+            NULL, NULL, NULL, NULL, spacings, NULL);\r
+       if (!gtk_font_selection_dialog_set_font_name\r
+           (GTK_FONT_SELECTION_DIALOG(fontsel), fontname)) {\r
+            /*\r
+             * If the font name wasn't found as it was, try opening\r
+             * it and extracting its FONT property. This should\r
+             * have the effect of mapping short aliases into true\r
+             * XLFDs.\r
+             */\r
+            GdkFont *font = gdk_font_load(fontname);\r
+            if (font) {\r
+                XFontStruct *xfs = GDK_FONT_XFONT(font);\r
+                Display *disp = GDK_FONT_XDISPLAY(font);\r
+                Atom fontprop = XInternAtom(disp, "FONT", False);\r
+                unsigned long ret;\r
+               gdk_font_ref(font);\r
+                if (XGetFontProperty(xfs, fontprop, &ret)) {\r
+                    char *name = XGetAtomName(disp, (Atom)ret);\r
+                    if (name)\r
+                        gtk_font_selection_dialog_set_font_name\r
+                        (GTK_FONT_SELECTION_DIALOG(fontsel), name);\r
+                }\r
+                gdk_font_unref(font);\r
+            }\r
+        }\r
+       gtk_object_set_data\r
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),\r
+            "user-data", (gpointer)fontsel);\r
+       gtk_object_set_data(GTK_OBJECT(fontsel), "user-data", (gpointer)uc);\r
+       gtk_signal_connect\r
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),\r
+            "clicked", GTK_SIGNAL_FUNC(fontsel_ok), (gpointer)dp);\r
+       gtk_signal_connect_object\r
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button),\r
+            "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),\r
+            (gpointer)fontsel);\r
+       gtk_signal_connect_object\r
+           (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->cancel_button),\r
+            "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),\r
+            (gpointer)fontsel);\r
+       gtk_widget_show(fontsel);\r
+\r
+#else /* !GTK_CHECK_VERSION(2,0,0) */\r
+\r
+       /*\r
+        * Use the unifontsel code provided in gtkfont.c.\r
+        */\r
+\r
+       unifontsel *fontsel = unifontsel_new("Select a font");\r
+\r
+       gtk_window_set_modal(fontsel->window, TRUE);\r
+       unifontsel_set_name(fontsel, fontname);\r
+       \r
+       gtk_object_set_data(GTK_OBJECT(fontsel->ok_button),\r
+                           "user-data", (gpointer)fontsel);\r
+       fontsel->user_data = uc;\r
+       gtk_signal_connect(GTK_OBJECT(fontsel->ok_button), "clicked",\r
+                          GTK_SIGNAL_FUNC(fontsel_ok), (gpointer)dp);\r
+       gtk_signal_connect_object(GTK_OBJECT(fontsel->ok_button), "clicked",\r
+                                 GTK_SIGNAL_FUNC(unifontsel_destroy),\r
+                                 (gpointer)fontsel);\r
+       gtk_signal_connect_object(GTK_OBJECT(fontsel->cancel_button),"clicked",\r
+                                 GTK_SIGNAL_FUNC(unifontsel_destroy),\r
+                                 (gpointer)fontsel);\r
+\r
+       gtk_widget_show(GTK_WIDGET(fontsel->window));\r
+\r
+#endif /* !GTK_CHECK_VERSION(2,0,0) */\r
+\r
+    }\r
+}\r
+\r
+static void label_sizealloc(GtkWidget *widget, GtkAllocation *alloc,\r
+                           gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+    struct uctrl *uc = dlg_find_bywidget(dp, widget);\r
+\r
+    gtk_widget_set_usize(uc->text, alloc->width, -1);\r
+    gtk_label_set_text(GTK_LABEL(uc->text), uc->ctrl->generic.label);\r
+    gtk_signal_disconnect(GTK_OBJECT(uc->text), uc->textsig);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * This function does the main layout work: it reads a controlset,\r
+ * it creates the relevant GTK controls, and returns a GtkWidget\r
+ * containing the result. (This widget might be a title of some\r
+ * sort, it might be a Columns containing many controls, or it\r
+ * might be a GtkFrame containing a Columns; whatever it is, it's\r
+ * definitely a GtkWidget and should probably be added to a\r
+ * GtkVbox.)\r
+ * \r
+ * `win' is required for setting the default button. If it is\r
+ * non-NULL, all buttons created will be default-capable (so they\r
+ * have extra space round them for the default highlight).\r
+ */\r
+GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,\r
+                       struct controlset *s, GtkWindow *win)\r
+{\r
+    Columns *cols;\r
+    GtkWidget *ret;\r
+    int i;\r
+\r
+    if (!s->boxname && s->boxtitle) {\r
+        /* This controlset is a panel title. */\r
+        return gtk_label_new(s->boxtitle);\r
+    }\r
+\r
+    /*\r
+     * Otherwise, we expect to be laying out actual controls, so\r
+     * we'll start by creating a Columns for the purpose.\r
+     */\r
+    cols = COLUMNS(columns_new(4));\r
+    ret = GTK_WIDGET(cols);\r
+    gtk_widget_show(ret);\r
+\r
+    /*\r
+     * Create a containing frame if we have a box name.\r
+     */\r
+    if (*s->boxname) {\r
+        ret = gtk_frame_new(s->boxtitle);   /* NULL is valid here */\r
+        gtk_container_set_border_width(GTK_CONTAINER(cols), 4);\r
+        gtk_container_add(GTK_CONTAINER(ret), GTK_WIDGET(cols));\r
+        gtk_widget_show(ret);\r
+    }\r
+\r
+    /*\r
+     * Now iterate through the controls themselves, create them,\r
+     * and add them to the Columns.\r
+     */\r
+    for (i = 0; i < s->ncontrols; i++) {\r
+       union control *ctrl = s->ctrls[i];\r
+       struct uctrl *uc;\r
+       int left = FALSE;\r
+        GtkWidget *w = NULL;\r
+\r
+        switch (ctrl->generic.type) {\r
+          case CTRL_COLUMNS:\r
+            {\r
+                static const int simplecols[1] = { 100 };\r
+                columns_set_cols(cols, ctrl->columns.ncols,\r
+                                 (ctrl->columns.percentages ?\r
+                                  ctrl->columns.percentages : simplecols));\r
+            }\r
+            continue;                  /* no actual control created */\r
+          case CTRL_TABDELAY:\r
+           {\r
+               struct uctrl *uc = dlg_find_byctrl(dp, ctrl->tabdelay.ctrl);\r
+               if (uc)\r
+                   columns_taborder_last(cols, uc->toplevel);\r
+           }\r
+            continue;                  /* no actual control created */\r
+       }\r
+\r
+       uc = snew(struct uctrl);\r
+       uc->ctrl = ctrl;\r
+       uc->privdata = NULL;\r
+       uc->privdata_needs_free = FALSE;\r
+       uc->buttons = NULL;\r
+       uc->entry = NULL;\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+       uc->list = uc->menu = uc->optmenu = NULL;\r
+#else\r
+       uc->combo = NULL;\r
+#endif\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+       uc->treeview = NULL;\r
+       uc->listmodel = NULL;\r
+#endif\r
+       uc->button = uc->text = NULL;\r
+       uc->label = NULL;\r
+        uc->nclicks = 0;\r
+\r
+        switch (ctrl->generic.type) {\r
+          case CTRL_BUTTON:\r
+            w = gtk_button_new_with_label(ctrl->generic.label);\r
+           if (win) {\r
+               GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);\r
+               if (ctrl->button.isdefault)\r
+                   gtk_window_set_default(win, w);\r
+               if (ctrl->button.iscancel)\r
+                   dp->cancelbutton = w;\r
+           }\r
+           gtk_signal_connect(GTK_OBJECT(w), "clicked",\r
+                              GTK_SIGNAL_FUNC(button_clicked), dp);\r
+            gtk_signal_connect(GTK_OBJECT(w), "focus_in_event",\r
+                               GTK_SIGNAL_FUNC(widget_focus), dp);\r
+           shortcut_add(scs, GTK_BIN(w)->child, ctrl->button.shortcut,\r
+                        SHORTCUT_UCTRL, uc);\r
+            break;\r
+          case CTRL_CHECKBOX:\r
+            w = gtk_check_button_new_with_label(ctrl->generic.label);\r
+           gtk_signal_connect(GTK_OBJECT(w), "toggled",\r
+                              GTK_SIGNAL_FUNC(button_toggled), dp);\r
+            gtk_signal_connect(GTK_OBJECT(w), "focus_in_event",\r
+                               GTK_SIGNAL_FUNC(widget_focus), dp);\r
+           shortcut_add(scs, GTK_BIN(w)->child, ctrl->checkbox.shortcut,\r
+                        SHORTCUT_UCTRL, uc);\r
+           left = TRUE;\r
+            break;\r
+          case CTRL_RADIO:\r
+            /*\r
+             * Radio buttons get to go inside their own Columns, no\r
+             * matter what.\r
+             */\r
+            {\r
+                gint i, *percentages;\r
+                GSList *group;\r
+\r
+                w = columns_new(0);\r
+                if (ctrl->generic.label) {\r
+                    GtkWidget *label = gtk_label_new(ctrl->generic.label);\r
+                    columns_add(COLUMNS(w), label, 0, 1);\r
+                   columns_force_left_align(COLUMNS(w), label);\r
+                    gtk_widget_show(label);\r
+                   shortcut_add(scs, label, ctrl->radio.shortcut,\r
+                                SHORTCUT_UCTRL, uc);\r
+                   uc->label = label;\r
+                }\r
+                percentages = g_new(gint, ctrl->radio.ncolumns);\r
+                for (i = 0; i < ctrl->radio.ncolumns; i++) {\r
+                    percentages[i] =\r
+                        ((100 * (i+1) / ctrl->radio.ncolumns) -\r
+                         100 * i / ctrl->radio.ncolumns);\r
+                }\r
+                columns_set_cols(COLUMNS(w), ctrl->radio.ncolumns,\r
+                                 percentages);\r
+                g_free(percentages);\r
+                group = NULL;\r
+\r
+               uc->nbuttons = ctrl->radio.nbuttons;\r
+               uc->buttons = snewn(uc->nbuttons, GtkWidget *);\r
+\r
+                for (i = 0; i < ctrl->radio.nbuttons; i++) {\r
+                    GtkWidget *b;\r
+                    gint colstart;\r
+\r
+                    b = (gtk_radio_button_new_with_label\r
+                         (group, ctrl->radio.buttons[i]));\r
+                   uc->buttons[i] = b;\r
+                    group = gtk_radio_button_group(GTK_RADIO_BUTTON(b));\r
+                    colstart = i % ctrl->radio.ncolumns;\r
+                    columns_add(COLUMNS(w), b, colstart,\r
+                                (i == ctrl->radio.nbuttons-1 ?\r
+                                 ctrl->radio.ncolumns - colstart : 1));\r
+                   columns_force_left_align(COLUMNS(w), b);\r
+                    gtk_widget_show(b);\r
+                   gtk_signal_connect(GTK_OBJECT(b), "toggled",\r
+                                      GTK_SIGNAL_FUNC(button_toggled), dp);\r
+                    gtk_signal_connect(GTK_OBJECT(b), "focus_in_event",\r
+                                       GTK_SIGNAL_FUNC(widget_focus), dp);\r
+                   if (ctrl->radio.shortcuts) {\r
+                       shortcut_add(scs, GTK_BIN(b)->child,\r
+                                    ctrl->radio.shortcuts[i],\r
+                                    SHORTCUT_UCTRL, uc);\r
+                   }\r
+                }\r
+            }\r
+            break;\r
+          case CTRL_EDITBOX:\r
+           {\r
+                GtkRequisition req;\r
+               GtkWidget *signalobject;\r
+\r
+               if (ctrl->editbox.has_list) {\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+                   /*\r
+                    * GTK 1 combo box.\r
+                    */\r
+                   w = gtk_combo_new();\r
+                   gtk_combo_set_value_in_list(GTK_COMBO(w), FALSE, TRUE);\r
+                   uc->entry = GTK_COMBO(w)->entry;\r
+                   uc->list = GTK_COMBO(w)->list;\r
+                   signalobject = uc->entry;\r
+#else\r
+                   /*\r
+                    * GTK 2 combo box.\r
+                    */\r
+                   uc->listmodel = gtk_list_store_new(2, G_TYPE_INT,\r
+                                                      G_TYPE_STRING);\r
+                   w = gtk_combo_box_entry_new_with_model\r
+                       (GTK_TREE_MODEL(uc->listmodel), 1);\r
+                   /* We cannot support password combo boxes. */\r
+                   assert(!ctrl->editbox.password);\r
+                   uc->combo = w;\r
+                   signalobject = uc->combo;\r
+#endif\r
+               } else {\r
+                   w = gtk_entry_new();\r
+                   if (ctrl->editbox.password)\r
+                       gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);\r
+                   uc->entry = w;\r
+                   signalobject = w;\r
+               }\r
+               uc->entrysig =\r
+                   gtk_signal_connect(GTK_OBJECT(signalobject), "changed",\r
+                                      GTK_SIGNAL_FUNC(editbox_changed), dp);\r
+               gtk_signal_connect(GTK_OBJECT(signalobject), "key_press_event",\r
+                                  GTK_SIGNAL_FUNC(editbox_key), dp);\r
+               gtk_signal_connect(GTK_OBJECT(signalobject), "focus_in_event",\r
+                                  GTK_SIGNAL_FUNC(widget_focus), dp);\r
+               gtk_signal_connect(GTK_OBJECT(signalobject), "focus_out_event",\r
+                                  GTK_SIGNAL_FUNC(editbox_lostfocus), dp);\r
+               gtk_signal_connect(GTK_OBJECT(signalobject), "focus_out_event",\r
+                                  GTK_SIGNAL_FUNC(editbox_lostfocus), dp);\r
+               /*\r
+                * Edit boxes, for some strange reason, have a minimum\r
+                * width of 150 in GTK 1.2. We don't want this - we'd\r
+                * rather the edit boxes acquired their natural width\r
+                * from the column layout of the rest of the box.\r
+                *\r
+                * Also, while we're here, we'll squirrel away the\r
+                * edit box height so we can use that to centre its\r
+                * label vertically beside it.\r
+                */\r
+                gtk_widget_size_request(w, &req);\r
+                gtk_widget_set_usize(w, 10, req.height);\r
+\r
+               if (ctrl->generic.label) {\r
+                   GtkWidget *label, *container;\r
+\r
+                   label = gtk_label_new(ctrl->generic.label);\r
+\r
+                   shortcut_add(scs, label, ctrl->editbox.shortcut,\r
+                                SHORTCUT_FOCUS, uc->entry);\r
+\r
+                   container = columns_new(4);\r
+                   if (ctrl->editbox.percentwidth == 100) {\r
+                       columns_add(COLUMNS(container), label, 0, 1);\r
+                       columns_force_left_align(COLUMNS(container), label);\r
+                       columns_add(COLUMNS(container), w, 0, 1);\r
+                   } else {\r
+                       gint percentages[2];\r
+                       percentages[1] = ctrl->editbox.percentwidth;\r
+                       percentages[0] = 100 - ctrl->editbox.percentwidth;\r
+                       columns_set_cols(COLUMNS(container), 2, percentages);\r
+                       columns_add(COLUMNS(container), label, 0, 1);\r
+                       columns_force_left_align(COLUMNS(container), label);\r
+                       columns_add(COLUMNS(container), w, 1, 1);\r
+                       /* Centre the label vertically. */\r
+                       gtk_widget_set_usize(label, -1, req.height);\r
+                       gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);\r
+                   }\r
+                   gtk_widget_show(label);\r
+                   gtk_widget_show(w);\r
+\r
+                   w = container;\r
+                   uc->label = label;\r
+               }\r
+           }\r
+            break;\r
+          case CTRL_FILESELECT:\r
+          case CTRL_FONTSELECT:\r
+            {\r
+                GtkWidget *ww;\r
+                GtkRequisition req;\r
+                char *browsebtn =\r
+                    (ctrl->generic.type == CTRL_FILESELECT ?\r
+                     "Browse..." : "Change...");\r
+\r
+                gint percentages[] = { 75, 25 };\r
+                w = columns_new(4);\r
+                columns_set_cols(COLUMNS(w), 2, percentages);\r
+\r
+                if (ctrl->generic.label) {\r
+                    ww = gtk_label_new(ctrl->generic.label);\r
+                    columns_add(COLUMNS(w), ww, 0, 2);\r
+                   columns_force_left_align(COLUMNS(w), ww);\r
+                    gtk_widget_show(ww);\r
+                   shortcut_add(scs, ww,\r
+                                (ctrl->generic.type == CTRL_FILESELECT ?\r
+                                 ctrl->fileselect.shortcut :\r
+                                 ctrl->fontselect.shortcut),\r
+                                SHORTCUT_UCTRL, uc);\r
+                   uc->label = ww;\r
+                }\r
+\r
+                uc->entry = ww = gtk_entry_new();\r
+                gtk_widget_size_request(ww, &req);\r
+                gtk_widget_set_usize(ww, 10, req.height);\r
+                columns_add(COLUMNS(w), ww, 0, 1);\r
+                gtk_widget_show(ww);\r
+\r
+                uc->button = ww = gtk_button_new_with_label(browsebtn);\r
+                columns_add(COLUMNS(w), ww, 1, 1);\r
+                gtk_widget_show(ww);\r
+\r
+               gtk_signal_connect(GTK_OBJECT(uc->entry), "key_press_event",\r
+                                  GTK_SIGNAL_FUNC(editbox_key), dp);\r
+               uc->entrysig =\r
+                   gtk_signal_connect(GTK_OBJECT(uc->entry), "changed",\r
+                                      GTK_SIGNAL_FUNC(editbox_changed), dp);\r
+                gtk_signal_connect(GTK_OBJECT(uc->entry), "focus_in_event",\r
+                                   GTK_SIGNAL_FUNC(widget_focus), dp);\r
+                gtk_signal_connect(GTK_OBJECT(uc->button), "focus_in_event",\r
+                                   GTK_SIGNAL_FUNC(widget_focus), dp);\r
+               gtk_signal_connect(GTK_OBJECT(ww), "clicked",\r
+                                  GTK_SIGNAL_FUNC(filefont_clicked), dp);\r
+            }\r
+            break;\r
+          case CTRL_LISTBOX:\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+           /*\r
+            * First construct the list data store, with the right\r
+            * number of columns.\r
+            */\r
+#  if !GTK_CHECK_VERSION(2,4,0)\r
+           /* (For GTK 2.0 to 2.3, we do this for full listboxes only,\r
+            * because combo boxes are still done the old GTK1 way.) */\r
+           if (ctrl->listbox.height > 0)\r
+#  endif\r
+           {\r
+               GType *types;\r
+               int i;\r
+               int cols;\r
+\r
+               cols = ctrl->listbox.ncols;\r
+               cols = cols ? cols : 1;\r
+               types = snewn(1 + cols, GType);\r
+\r
+               types[0] = G_TYPE_INT;\r
+               for (i = 0; i < cols; i++)\r
+                   types[i+1] = G_TYPE_STRING;\r
+\r
+               uc->listmodel = gtk_list_store_newv(1 + cols, types);\r
+\r
+               sfree(types);\r
+           }\r
+#endif\r
+\r
+           /*\r
+            * See if it's a drop-down list (non-editable combo\r
+            * box).\r
+            */\r
+           if (ctrl->listbox.height == 0) {\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+               /*\r
+                * GTK1 and early-GTK2 option-menu style of\r
+                * drop-down list.\r
+                */\r
+                uc->optmenu = w = gtk_option_menu_new();\r
+               uc->menu = gtk_menu_new();\r
+               gtk_option_menu_set_menu(GTK_OPTION_MENU(w), uc->menu);\r
+               gtk_object_set_data(GTK_OBJECT(uc->menu), "user-data",\r
+                                   (gpointer)uc->optmenu);\r
+                gtk_signal_connect(GTK_OBJECT(uc->optmenu), "focus_in_event",\r
+                                   GTK_SIGNAL_FUNC(widget_focus), dp);\r
+#else\r
+               /*\r
+                * Late-GTK2 style using a GtkComboBox.\r
+                */\r
+               GtkCellRenderer *cr;\r
+\r
+               /*\r
+                * Create a non-editable GtkComboBox (that is, not\r
+                * its subclass GtkComboBoxEntry).\r
+                */\r
+               w = gtk_combo_box_new_with_model\r
+                   (GTK_TREE_MODEL(uc->listmodel));\r
+               uc->combo = w;\r
+\r
+               /*\r
+                * Tell it how to render a list item (i.e. which\r
+                * column to look at in the list model).\r
+                */\r
+               cr = gtk_cell_renderer_text_new();\r
+               gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), cr, TRUE);\r
+               gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), cr,\r
+                                              "text", 1, NULL);\r
+\r
+               /*\r
+                * And tell it to notify us when the selection\r
+                * changes.\r
+                */\r
+               g_signal_connect(G_OBJECT(w), "changed",\r
+                                G_CALLBACK(droplist_selchange), dp);\r
+#endif\r
+            } else {\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+               /*\r
+                * GTK1-style full list box.\r
+                */\r
+                uc->list = gtk_list_new();\r
+                if (ctrl->listbox.multisel == 2) {\r
+                    gtk_list_set_selection_mode(GTK_LIST(uc->list),\r
+                                                GTK_SELECTION_EXTENDED);\r
+                } else if (ctrl->listbox.multisel == 1) {\r
+                    gtk_list_set_selection_mode(GTK_LIST(uc->list),\r
+                                                GTK_SELECTION_MULTIPLE);\r
+                } else {\r
+                    gtk_list_set_selection_mode(GTK_LIST(uc->list),\r
+                                                GTK_SELECTION_SINGLE);\r
+                }\r
+                w = gtk_scrolled_window_new(NULL, NULL);\r
+                gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w),\r
+                                                      uc->list);\r
+                gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w),\r
+                                               GTK_POLICY_NEVER,\r
+                                               GTK_POLICY_AUTOMATIC);\r
+                uc->adj = gtk_scrolled_window_get_vadjustment\r
+                    (GTK_SCROLLED_WINDOW(w));\r
+\r
+                gtk_widget_show(uc->list);\r
+               gtk_signal_connect(GTK_OBJECT(uc->list), "selection-changed",\r
+                                  GTK_SIGNAL_FUNC(list_selchange), dp);\r
+                gtk_signal_connect(GTK_OBJECT(uc->list), "focus_in_event",\r
+                                   GTK_SIGNAL_FUNC(widget_focus), dp);\r
+\r
+                /*\r
+                 * Adjust the height of the scrolled window to the\r
+                 * minimum given by the height parameter.\r
+                 * \r
+                 * This piece of guesswork is a horrid hack based\r
+                 * on looking inside the GTK 1.2 sources\r
+                 * (specifically gtkviewport.c, which appears to be\r
+                 * the widget which provides the border around the\r
+                 * scrolling area). Anyone lets me know how I can\r
+                 * do this in a way which isn't at risk from GTK\r
+                 * upgrades, I'd be grateful.\r
+                 */\r
+               {\r
+                   int edge;\r
+                   edge = GTK_WIDGET(uc->list)->style->klass->ythickness;\r
+                    gtk_widget_set_usize(w, 10,\r
+                                         2*edge + (ctrl->listbox.height *\r
+                                                  get_listitemheight(w)));\r
+               }\r
+\r
+                if (ctrl->listbox.draglist) {\r
+                    /*\r
+                     * GTK doesn't appear to make it easy to\r
+                     * implement a proper draggable list; so\r
+                     * instead I'm just going to have to put an Up\r
+                     * and a Down button to the right of the actual\r
+                     * list box. Ah well.\r
+                     */\r
+                    GtkWidget *cols, *button;\r
+                    static const gint percentages[2] = { 80, 20 };\r
+\r
+                    cols = columns_new(4);\r
+                    columns_set_cols(COLUMNS(cols), 2, percentages);\r
+                    columns_add(COLUMNS(cols), w, 0, 1);\r
+                    gtk_widget_show(w);\r
+                    button = gtk_button_new_with_label("Up");\r
+                    columns_add(COLUMNS(cols), button, 1, 1);\r
+                    gtk_widget_show(button);\r
+                   gtk_signal_connect(GTK_OBJECT(button), "clicked",\r
+                                      GTK_SIGNAL_FUNC(draglist_up), dp);\r
+                    gtk_signal_connect(GTK_OBJECT(button), "focus_in_event",\r
+                                       GTK_SIGNAL_FUNC(widget_focus), dp);\r
+                    button = gtk_button_new_with_label("Down");\r
+                    columns_add(COLUMNS(cols), button, 1, 1);\r
+                    gtk_widget_show(button);\r
+                   gtk_signal_connect(GTK_OBJECT(button), "clicked",\r
+                                      GTK_SIGNAL_FUNC(draglist_down), dp);\r
+                    gtk_signal_connect(GTK_OBJECT(button), "focus_in_event",\r
+                                       GTK_SIGNAL_FUNC(widget_focus), dp);\r
+\r
+                    w = cols;\r
+                }\r
+#else\r
+               /*\r
+                * GTK2 treeview-based full list box.\r
+                */\r
+               GtkTreeSelection *sel;\r
+\r
+               /*\r
+                * Create the list box itself, its columns, and\r
+                * its containing scrolled window.\r
+                */\r
+               w = gtk_tree_view_new_with_model\r
+                   (GTK_TREE_MODEL(uc->listmodel));\r
+               g_object_set_data(G_OBJECT(uc->listmodel), "user-data",\r
+                                 (gpointer)w);\r
+               gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);\r
+               sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));\r
+               gtk_tree_selection_set_mode\r
+                   (sel, ctrl->listbox.multisel ? GTK_SELECTION_MULTIPLE :\r
+                    GTK_SELECTION_SINGLE);\r
+               uc->treeview = w;\r
+               gtk_signal_connect(GTK_OBJECT(w), "row-activated",\r
+                                  GTK_SIGNAL_FUNC(listbox_doubleclick), dp);\r
+               g_signal_connect(G_OBJECT(sel), "changed",\r
+                                G_CALLBACK(listbox_selchange), dp);\r
+\r
+               if (ctrl->listbox.draglist) {\r
+                   gtk_tree_view_set_reorderable(GTK_TREE_VIEW(w), TRUE);\r
+                   g_signal_connect(G_OBJECT(uc->listmodel), "row-inserted",\r
+                                    G_CALLBACK(listbox_reorder), dp);\r
+               }\r
+\r
+               {\r
+                   int i;\r
+                   int cols;\r
+\r
+                   cols = ctrl->listbox.ncols;\r
+                   cols = cols ? cols : 1;\r
+                   for (i = 0; i < cols; i++) {\r
+                       GtkTreeViewColumn *column;\r
+                       /*\r
+                        * It appears that GTK 2 doesn't leave us any\r
+                        * particularly sensible way to honour the\r
+                        * "percentages" specification in the ctrl\r
+                        * structure.\r
+                        */\r
+                       column = gtk_tree_view_column_new_with_attributes\r
+                           ("heading", gtk_cell_renderer_text_new(),\r
+                            "text", i+1, (char *)NULL);\r
+                       gtk_tree_view_column_set_sizing\r
+                           (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);\r
+                       gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);\r
+                   }\r
+               }\r
+\r
+               {\r
+                   GtkWidget *scroll;\r
+\r
+                   scroll = gtk_scrolled_window_new(NULL, NULL);\r
+                   gtk_scrolled_window_set_shadow_type\r
+                       (GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);\r
+                   gtk_widget_show(w);\r
+                   gtk_container_add(GTK_CONTAINER(scroll), w);\r
+                   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),\r
+                                                  GTK_POLICY_AUTOMATIC,\r
+                                                  GTK_POLICY_ALWAYS);\r
+                   gtk_widget_set_size_request\r
+                       (scroll, -1,\r
+                        ctrl->listbox.height * get_listitemheight(w));\r
+\r
+                   w = scroll;\r
+               }\r
+#endif\r
+            }\r
+\r
+           if (ctrl->generic.label) {\r
+               GtkWidget *label, *container;\r
+                GtkRequisition req;\r
+\r
+               label = gtk_label_new(ctrl->generic.label);\r
+\r
+               shortcut_add(scs, label, ctrl->listbox.shortcut,\r
+                            SHORTCUT_FOCUS, w);\r
+\r
+               container = columns_new(4);\r
+               if (ctrl->listbox.percentwidth == 100) {\r
+                   columns_add(COLUMNS(container), label, 0, 1);\r
+                   columns_force_left_align(COLUMNS(container), label);\r
+                   columns_add(COLUMNS(container), w, 0, 1);\r
+               } else {\r
+                   gint percentages[2];\r
+                   percentages[1] = ctrl->listbox.percentwidth;\r
+                   percentages[0] = 100 - ctrl->listbox.percentwidth;\r
+                   columns_set_cols(COLUMNS(container), 2, percentages);\r
+                   columns_add(COLUMNS(container), label, 0, 1);\r
+                   columns_force_left_align(COLUMNS(container), label);\r
+                   columns_add(COLUMNS(container), w, 1, 1);\r
+                   /* Centre the label vertically. */\r
+                   gtk_widget_size_request(w, &req);\r
+                   gtk_widget_set_usize(label, -1, req.height);\r
+                   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);\r
+               }\r
+               gtk_widget_show(label);\r
+               gtk_widget_show(w);\r
+\r
+               w = container;\r
+               uc->label = label;\r
+           }\r
+\r
+           break;\r
+          case CTRL_TEXT:\r
+           /*\r
+            * Wrapping text widgets don't sit well with the GTK\r
+            * layout model, in which widgets state a minimum size\r
+            * and the whole window then adjusts to the smallest\r
+            * size it can sensibly take given its contents. A\r
+            * wrapping text widget _has_ no clear minimum size;\r
+            * instead it has a range of possibilities. It can be\r
+            * one line deep but 2000 wide, or two lines deep and\r
+            * 1000 pixels, or three by 867, or four by 500 and so\r
+            * on. It can be as short as you like provided you\r
+            * don't mind it being wide, or as narrow as you like\r
+            * provided you don't mind it being tall.\r
+            * \r
+            * Therefore, it fits very badly into the layout model.\r
+            * Hence the only thing to do is pick a width and let\r
+            * it choose its own number of lines. To do this I'm\r
+            * going to cheat a little. All new wrapping text\r
+            * widgets will be created with a minimal text content\r
+            * "X"; then, after the rest of the dialog box is set\r
+            * up and its size calculated, the text widgets will be\r
+            * told their width and given their real text, which\r
+            * will cause the size to be recomputed in the y\r
+            * direction (because many of them will expand to more\r
+            * than one line).\r
+            */\r
+            uc->text = w = gtk_label_new("X");\r
+            gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);\r
+            gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);\r
+           uc->textsig =\r
+               gtk_signal_connect(GTK_OBJECT(w), "size-allocate",\r
+                                  GTK_SIGNAL_FUNC(label_sizealloc), dp);\r
+            break;\r
+        }\r
+\r
+       assert(w != NULL);\r
+\r
+       columns_add(cols, w,\r
+                   COLUMN_START(ctrl->generic.column),\r
+                   COLUMN_SPAN(ctrl->generic.column));\r
+       if (left)\r
+           columns_force_left_align(cols, w);\r
+       gtk_widget_show(w);\r
+\r
+       uc->toplevel = w;\r
+       dlg_add_uctrl(dp, uc);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+struct selparam {\r
+    struct dlgparam *dp;\r
+    GtkNotebook *panels;\r
+    GtkWidget *panel;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    GtkWidget *treeitem;\r
+#else\r
+    int depth;\r
+    GtkTreePath *treepath;\r
+#endif\r
+    struct Shortcuts shortcuts;\r
+};\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+static void treeselection_changed(GtkTreeSelection *treeselection,\r
+                                 gpointer data)\r
+{\r
+    struct selparam *sps = (struct selparam *)data, *sp;\r
+    GtkTreeModel *treemodel;\r
+    GtkTreeIter treeiter;\r
+    gint spindex;\r
+    gint page_num;\r
+\r
+    if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))\r
+       return;\r
+\r
+    gtk_tree_model_get(treemodel, &treeiter, TREESTORE_PARAMS, &spindex, -1);\r
+    sp = &sps[spindex];\r
+\r
+    page_num = gtk_notebook_page_num(sp->panels, sp->panel);\r
+    gtk_notebook_set_page(sp->panels, page_num);\r
+\r
+    dlg_refresh(NULL, sp->dp);\r
+\r
+    sp->dp->shortcuts = &sp->shortcuts;\r
+}\r
+#else\r
+static void treeitem_sel(GtkItem *item, gpointer data)\r
+{\r
+    struct selparam *sp = (struct selparam *)data;\r
+    gint page_num;\r
+\r
+    page_num = gtk_notebook_page_num(sp->panels, sp->panel);\r
+    gtk_notebook_set_page(sp->panels, page_num);\r
+\r
+    dlg_refresh(NULL, sp->dp);\r
+\r
+    sp->dp->shortcuts = &sp->shortcuts;\r
+    sp->dp->currtreeitem = sp->treeitem;\r
+}\r
+#endif\r
+\r
+static void window_destroy(GtkWidget *widget, gpointer data)\r
+{\r
+    gtk_main_quit();\r
+}\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+static int tree_grab_focus(struct dlgparam *dp)\r
+{\r
+    int i, f;\r
+\r
+    /*\r
+     * See if any of the treeitems has the focus.\r
+     */\r
+    f = -1;\r
+    for (i = 0; i < dp->ntreeitems; i++)\r
+        if (GTK_WIDGET_HAS_FOCUS(dp->treeitems[i])) {\r
+            f = i;\r
+            break;\r
+        }\r
+\r
+    if (f >= 0)\r
+        return FALSE;\r
+    else {\r
+        gtk_widget_grab_focus(dp->currtreeitem);\r
+        return TRUE;\r
+    }\r
+}\r
+\r
+gint tree_focus(GtkContainer *container, GtkDirectionType direction,\r
+                gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+\r
+    gtk_signal_emit_stop_by_name(GTK_OBJECT(container), "focus");\r
+    /*\r
+     * If there's a focused treeitem, we return FALSE to cause the\r
+     * focus to move on to some totally other control. If not, we\r
+     * focus the selected one.\r
+     */\r
+    return tree_grab_focus(dp);\r
+}\r
+#endif\r
+\r
+int win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+\r
+    if (event->keyval == GDK_Escape && dp->cancelbutton) {\r
+       gtk_signal_emit_by_name(GTK_OBJECT(dp->cancelbutton), "clicked");\r
+       return TRUE;\r
+    }\r
+\r
+    if ((event->state & GDK_MOD1_MASK) &&\r
+       (unsigned char)event->string[0] > 0 &&\r
+       (unsigned char)event->string[0] <= 127) {\r
+       int schr = (unsigned char)event->string[0];\r
+       struct Shortcut *sc = &dp->shortcuts->sc[schr];\r
+\r
+       switch (sc->action) {\r
+         case SHORTCUT_TREE:\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+           gtk_widget_grab_focus(sc->widget);\r
+#else\r
+           tree_grab_focus(dp);\r
+#endif\r
+           break;\r
+         case SHORTCUT_FOCUS:\r
+           gtk_widget_grab_focus(sc->widget);\r
+           break;\r
+         case SHORTCUT_UCTRL:\r
+           /*\r
+            * We must do something sensible with a uctrl.\r
+            * Precisely what this is depends on the type of\r
+            * control.\r
+            */\r
+           switch (sc->uc->ctrl->generic.type) {\r
+             case CTRL_CHECKBOX:\r
+             case CTRL_BUTTON:\r
+               /* Check boxes and buttons get the focus _and_ get toggled. */\r
+               gtk_widget_grab_focus(sc->uc->toplevel);\r
+               gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->toplevel),\r
+                                       "clicked");\r
+               break;\r
+             case CTRL_FILESELECT:\r
+             case CTRL_FONTSELECT:\r
+               /* File/font selectors have their buttons pressed (ooer),\r
+                * and focus transferred to the edit box. */\r
+               gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->button),\r
+                                       "clicked");\r
+               gtk_widget_grab_focus(sc->uc->entry);\r
+               break;\r
+             case CTRL_RADIO:\r
+               /*\r
+                * Radio buttons are fun, because they have\r
+                * multiple shortcuts. We must find whether the\r
+                * activated shortcut is the shortcut for the whole\r
+                * group, or for a particular button. In the former\r
+                * case, we find the currently selected button and\r
+                * focus it; in the latter, we focus-and-click the\r
+                * button whose shortcut was pressed.\r
+                */\r
+               if (schr == sc->uc->ctrl->radio.shortcut) {\r
+                   int i;\r
+                   for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)\r
+                       if (gtk_toggle_button_get_active\r
+                           (GTK_TOGGLE_BUTTON(sc->uc->buttons[i]))) {\r
+                           gtk_widget_grab_focus(sc->uc->buttons[i]);\r
+                       }\r
+               } else if (sc->uc->ctrl->radio.shortcuts) {\r
+                   int i;\r
+                   for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)\r
+                       if (schr == sc->uc->ctrl->radio.shortcuts[i]) {\r
+                           gtk_widget_grab_focus(sc->uc->buttons[i]);\r
+                           gtk_signal_emit_by_name\r
+                               (GTK_OBJECT(sc->uc->buttons[i]), "clicked");\r
+                       }\r
+               }\r
+               break;\r
+             case CTRL_LISTBOX:\r
+\r
+#if !GTK_CHECK_VERSION(2,4,0)\r
+               if (sc->uc->optmenu) {\r
+                   GdkEventButton bev;\r
+                   gint returnval;\r
+\r
+                   gtk_widget_grab_focus(sc->uc->optmenu);\r
+                   /* Option menus don't work using the "clicked" signal.\r
+                    * We need to manufacture a button press event :-/ */\r
+                   bev.type = GDK_BUTTON_PRESS;\r
+                   bev.button = 1;\r
+                   gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->optmenu),\r
+                                           "button_press_event",\r
+                                           &bev, &returnval);\r
+                   break;\r
+               }\r
+#else\r
+               if (sc->uc->combo) {\r
+                   gtk_widget_grab_focus(sc->uc->combo);\r
+                   gtk_combo_box_popup(GTK_COMBO_BOX(sc->uc->combo));\r
+                   break;\r
+               }\r
+#endif\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+               if (sc->uc->list) {\r
+                   /*\r
+                    * For GTK-1 style list boxes, we tell it to\r
+                    * focus one of its children, which appears to\r
+                    * do the Right Thing.\r
+                    */\r
+                    gtk_container_focus(GTK_CONTAINER(sc->uc->list),\r
+                                        GTK_DIR_TAB_FORWARD);\r
+                   break;\r
+               }\r
+#else\r
+               if (sc->uc->treeview) {\r
+                   gtk_widget_grab_focus(sc->uc->treeview);\r
+                   break;\r
+               }\r
+#endif\r
+               assert(!"We shouldn't get here");\r
+               break;\r
+           }\r
+           break;\r
+       }\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+int tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)data;\r
+\r
+    if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||\r
+        event->keyval == GDK_Down || event->keyval == GDK_KP_Down) {\r
+        int dir, i, j = -1;\r
+        for (i = 0; i < dp->ntreeitems; i++)\r
+            if (widget == dp->treeitems[i])\r
+               break;\r
+       if (i < dp->ntreeitems) {\r
+           if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)\r
+               dir = -1;\r
+           else\r
+               dir = +1;\r
+\r
+           while (1) {\r
+               i += dir;\r
+               if (i < 0 || i >= dp->ntreeitems)\r
+                   break;             /* nothing in that dir to select */\r
+               /*\r
+                * Determine if this tree item is visible.\r
+                */\r
+               {\r
+                   GtkWidget *w = dp->treeitems[i];\r
+                   int vis = TRUE;\r
+                   while (w && (GTK_IS_TREE_ITEM(w) || GTK_IS_TREE(w))) {\r
+                       if (!GTK_WIDGET_VISIBLE(w)) {\r
+                           vis = FALSE;\r
+                           break;\r
+                       }\r
+                       w = w->parent;\r
+                   }\r
+                   if (vis) {\r
+                       j = i;         /* got one */\r
+                       break;\r
+                   }\r
+               }\r
+           }\r
+       }\r
+        gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),\r
+                                     "key_press_event");\r
+        if (j >= 0) {\r
+            gtk_signal_emit_by_name(GTK_OBJECT(dp->treeitems[j]), "toggle");\r
+            gtk_widget_grab_focus(dp->treeitems[j]);\r
+        }\r
+        return TRUE;\r
+    }\r
+\r
+    /*\r
+     * It's nice for Left and Right to expand and collapse tree\r
+     * branches.\r
+     */\r
+    if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left) {\r
+        gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),\r
+                                     "key_press_event");\r
+       gtk_tree_item_collapse(GTK_TREE_ITEM(widget));\r
+       return TRUE;\r
+    }\r
+    if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right) {\r
+        gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),\r
+                                     "key_press_event");\r
+       gtk_tree_item_expand(GTK_TREE_ITEM(widget));\r
+       return TRUE;\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+#endif\r
+\r
+static void shortcut_highlight(GtkWidget *labelw, int chr)\r
+{\r
+    GtkLabel *label = GTK_LABEL(labelw);\r
+    gchar *currstr, *pattern;\r
+    int i;\r
+\r
+    gtk_label_get(label, &currstr);\r
+    for (i = 0; currstr[i]; i++)\r
+       if (tolower((unsigned char)currstr[i]) == chr) {\r
+           GtkRequisition req;\r
+\r
+           pattern = dupprintf("%*s_", i, "");\r
+\r
+           gtk_widget_size_request(GTK_WIDGET(label), &req);\r
+           gtk_label_set_pattern(label, pattern);\r
+           gtk_widget_set_usize(GTK_WIDGET(label), -1, req.height);\r
+\r
+           sfree(pattern);\r
+           break;\r
+       }\r
+}\r
+\r
+void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,\r
+                 int chr, int action, void *ptr)\r
+{\r
+    if (chr == NO_SHORTCUT)\r
+       return;\r
+\r
+    chr = tolower((unsigned char)chr);\r
+\r
+    assert(scs->sc[chr].action == SHORTCUT_EMPTY);\r
+\r
+    scs->sc[chr].action = action;\r
+\r
+    if (action == SHORTCUT_FOCUS) {\r
+       scs->sc[chr].uc = NULL;\r
+       scs->sc[chr].widget = (GtkWidget *)ptr;\r
+    } else {\r
+       scs->sc[chr].widget = NULL;\r
+       scs->sc[chr].uc = (struct uctrl *)ptr;\r
+    }\r
+\r
+    shortcut_highlight(labelw, chr);\r
+}\r
+\r
+int get_listitemheight(GtkWidget *w)\r
+{\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    GtkWidget *listitem = gtk_list_item_new_with_label("foo");\r
+    GtkRequisition req;\r
+    gtk_widget_size_request(listitem, &req);\r
+    gtk_object_sink(GTK_OBJECT(listitem));\r
+    return req.height;\r
+#else\r
+    int height;\r
+    GtkCellRenderer *cr = gtk_cell_renderer_text_new();\r
+    gtk_cell_renderer_get_size(cr, w, NULL, NULL, NULL, NULL, &height);\r
+    g_object_ref(G_OBJECT(cr));\r
+    gtk_object_sink(GTK_OBJECT(cr));\r
+    g_object_unref(G_OBJECT(cr));\r
+    return height;\r
+#endif\r
+}\r
+\r
+void set_dialog_action_area(GtkDialog *dlg, GtkWidget *w)\r
+{\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+\r
+    /*\r
+     * In GTK 1, laying out the buttons at the bottom of the\r
+     * configuration box is nice and easy, because a GtkDialog's\r
+     * action_area is a GtkHBox which stretches to cover the full\r
+     * width of the dialog. So we just put our Columns widget\r
+     * straight into that hbox, and it ends up just where we want\r
+     * it.\r
+     */\r
+    gtk_box_pack_start(GTK_BOX(dlg->action_area), w, TRUE, TRUE, 0);\r
+\r
+#else\r
+    /*\r
+     * In GTK 2, the action area is now a GtkHButtonBox and its\r
+     * layout behaviour seems to be different: it doesn't stretch\r
+     * to cover the full width of the window, but instead finds its\r
+     * own preferred width and right-aligns that within the window.\r
+     * This isn't what we want, because we have both left-aligned\r
+     * and right-aligned buttons coming out of the above call to\r
+     * layout_ctrls(), and right-aligning the whole thing will\r
+     * result in the former being centred and looking weird.\r
+     *\r
+     * So instead we abandon the dialog's action area completely:\r
+     * we gtk_widget_hide() it in the below code, and we also call\r
+     * gtk_dialog_set_has_separator() to remove the separator above\r
+     * it. We then insert our own action area into the end of the\r
+     * dialog's main vbox, and add our own separator above that.\r
+     *\r
+     * (Ideally, if we were a native GTK app, we would use the\r
+     * GtkHButtonBox's _own_ innate ability to support one set of\r
+     * buttons being right-aligned and one left-aligned. But to do\r
+     * that here, we would have to either (a) pick apart our cross-\r
+     * platform layout structures and treat them specially for this\r
+     * particular set of controls, which would be painful, or else\r
+     * (b) develop a special and simpler cross-platform\r
+     * representation for these particular controls, and introduce\r
+     * special-case code into all the _other_ platforms to handle\r
+     * it. Neither appeals. Therefore, I regretfully discard the\r
+     * GTKHButtonBox and go it alone.)\r
+     */\r
+\r
+    GtkWidget *align;\r
+    align = gtk_alignment_new(0, 0, 1, 1);\r
+    gtk_container_add(GTK_CONTAINER(align), w);\r
+    /*\r
+     * The purpose of this GtkAlignment is to provide padding\r
+     * around the buttons. The padding we use is twice the padding\r
+     * used in our GtkColumns, because we nest two GtkColumns most\r
+     * of the time (one separating the tree view from the main\r
+     * controls, and another for the main controls themselves).\r
+     */\r
+#if GTK_CHECK_VERSION(2,4,0)\r
+    gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 8, 8);\r
+#endif\r
+    gtk_widget_show(align);\r
+    gtk_box_pack_end(GTK_BOX(dlg->vbox), align, FALSE, TRUE, 0);\r
+    w = gtk_hseparator_new();\r
+    gtk_box_pack_end(GTK_BOX(dlg->vbox), w, FALSE, TRUE, 0);\r
+    gtk_widget_show(w);\r
+    gtk_widget_hide(dlg->action_area);\r
+    gtk_dialog_set_has_separator(dlg, FALSE);\r
+#endif\r
+}\r
+\r
+int do_config_box(const char *title, Config *cfg, int midsession,\r
+                 int protcfginfo)\r
+{\r
+    GtkWidget *window, *hbox, *vbox, *cols, *label,\r
+       *tree, *treescroll, *panels, *panelvbox;\r
+    int index, level;\r
+    struct controlbox *ctrlbox;\r
+    char *path;\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    GtkTreeStore *treestore;\r
+    GtkCellRenderer *treerenderer;\r
+    GtkTreeViewColumn *treecolumn;\r
+    GtkTreeSelection *treeselection;\r
+    GtkTreeIter treeiterlevels[8];\r
+#else\r
+    GtkTreeItem *treeitemlevels[8];\r
+    GtkTree *treelevels[8];\r
+#endif\r
+    struct dlgparam dp;\r
+    struct Shortcuts scs;\r
+\r
+    struct selparam *selparams = NULL;\r
+    int nselparams = 0, selparamsize = 0;\r
+\r
+    dlg_init(&dp);\r
+\r
+    for (index = 0; index < lenof(scs.sc); index++) {\r
+       scs.sc[index].action = SHORTCUT_EMPTY;\r
+    }\r
+\r
+    window = gtk_dialog_new();\r
+\r
+    ctrlbox = ctrl_new_box();\r
+    setup_config_box(ctrlbox, midsession, cfg->protocol, protcfginfo);\r
+    unix_setup_config_box(ctrlbox, midsession, cfg->protocol);\r
+    gtk_setup_config_box(ctrlbox, midsession, window);\r
+\r
+    gtk_window_set_title(GTK_WINDOW(window), title);\r
+    hbox = gtk_hbox_new(FALSE, 4);\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), hbox, TRUE, TRUE, 0);\r
+    gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);\r
+    gtk_widget_show(hbox);\r
+    vbox = gtk_vbox_new(FALSE, 4);\r
+    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);\r
+    gtk_widget_show(vbox);\r
+    cols = columns_new(4);\r
+    gtk_box_pack_start(GTK_BOX(vbox), cols, FALSE, FALSE, 0);\r
+    gtk_widget_show(cols);\r
+    label = gtk_label_new("Category:");\r
+    columns_add(COLUMNS(cols), label, 0, 1);\r
+    columns_force_left_align(COLUMNS(cols), label);\r
+    gtk_widget_show(label);\r
+    treescroll = gtk_scrolled_window_new(NULL, NULL);\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    treestore = gtk_tree_store_new\r
+       (TREESTORE_NUM, G_TYPE_STRING, G_TYPE_INT);\r
+    tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore));\r
+    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);\r
+    treerenderer = gtk_cell_renderer_text_new();\r
+    treecolumn = gtk_tree_view_column_new_with_attributes\r
+       ("Label", treerenderer, "text", 0, NULL);\r
+    gtk_tree_view_append_column(GTK_TREE_VIEW(tree), treecolumn);\r
+    treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));\r
+    gtk_tree_selection_set_mode(treeselection, GTK_SELECTION_BROWSE);\r
+    gtk_container_add(GTK_CONTAINER(treescroll), tree);\r
+#else\r
+    tree = gtk_tree_new();\r
+    gtk_tree_set_view_mode(GTK_TREE(tree), GTK_TREE_VIEW_ITEM);\r
+    gtk_tree_set_selection_mode(GTK_TREE(tree), GTK_SELECTION_BROWSE);\r
+    gtk_signal_connect(GTK_OBJECT(tree), "focus",\r
+                      GTK_SIGNAL_FUNC(tree_focus), &dp);\r
+#endif\r
+    gtk_signal_connect(GTK_OBJECT(tree), "focus_in_event",\r
+                       GTK_SIGNAL_FUNC(widget_focus), &dp);\r
+    shortcut_add(&scs, label, 'g', SHORTCUT_TREE, tree);\r
+    gtk_widget_show(treescroll);\r
+    gtk_box_pack_start(GTK_BOX(vbox), treescroll, TRUE, TRUE, 0);\r
+    panels = gtk_notebook_new();\r
+    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(panels), FALSE);\r
+    gtk_notebook_set_show_border(GTK_NOTEBOOK(panels), FALSE);\r
+    gtk_box_pack_start(GTK_BOX(hbox), panels, TRUE, TRUE, 0);\r
+    gtk_widget_show(panels);\r
+\r
+    panelvbox = NULL;\r
+    path = NULL;\r
+    level = 0;\r
+    for (index = 0; index < ctrlbox->nctrlsets; index++) {\r
+       struct controlset *s = ctrlbox->ctrlsets[index];\r
+       GtkWidget *w;\r
+\r
+       if (!*s->pathname) {\r
+           w = layout_ctrls(&dp, &scs, s, GTK_WINDOW(window));\r
+\r
+           set_dialog_action_area(GTK_DIALOG(window), w);\r
+       } else {\r
+           int j = path ? ctrl_path_compare(s->pathname, path) : 0;\r
+           if (j != INT_MAX) {        /* add to treeview, start new panel */\r
+               char *c;\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+               GtkTreeIter treeiter;\r
+#else\r
+               GtkWidget *treeitem;\r
+#endif\r
+               int first;\r
+\r
+               /*\r
+                * We expect never to find an implicit path\r
+                * component. For example, we expect never to see\r
+                * A/B/C followed by A/D/E, because that would\r
+                * _implicitly_ create A/D. All our path prefixes\r
+                * are expected to contain actual controls and be\r
+                * selectable in the treeview; so we would expect\r
+                * to see A/D _explicitly_ before encountering\r
+                * A/D/E.\r
+                */\r
+               assert(j == ctrl_path_elements(s->pathname) - 1);\r
+\r
+               c = strrchr(s->pathname, '/');\r
+               if (!c)\r
+                   c = s->pathname;\r
+               else\r
+                   c++;\r
+\r
+               path = s->pathname;\r
+\r
+               first = (panelvbox == NULL);\r
+\r
+               panelvbox = gtk_vbox_new(FALSE, 4);\r
+               gtk_widget_show(panelvbox);\r
+               gtk_notebook_append_page(GTK_NOTEBOOK(panels), panelvbox,\r
+                                        NULL);\r
+               if (first) {\r
+                   gint page_num;\r
+\r
+                   page_num = gtk_notebook_page_num(GTK_NOTEBOOK(panels),\r
+                                                    panelvbox);\r
+                   gtk_notebook_set_page(GTK_NOTEBOOK(panels), page_num);\r
+               }\r
+\r
+               if (nselparams >= selparamsize) {\r
+                   selparamsize += 16;\r
+                   selparams = sresize(selparams, selparamsize,\r
+                                       struct selparam);\r
+               }\r
+               selparams[nselparams].dp = &dp;\r
+               selparams[nselparams].panels = GTK_NOTEBOOK(panels);\r
+               selparams[nselparams].panel = panelvbox;\r
+               selparams[nselparams].shortcuts = scs;   /* structure copy */\r
+\r
+               assert(j-1 < level);\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+               if (j > 0)\r
+                   /* treeiterlevels[j-1] will always be valid because we\r
+                    * don't allow implicit path components; see above.\r
+                    */\r
+                   gtk_tree_store_append(treestore, &treeiter,\r
+                                         &treeiterlevels[j-1]);\r
+               else\r
+                   gtk_tree_store_append(treestore, &treeiter, NULL);\r
+               gtk_tree_store_set(treestore, &treeiter,\r
+                                  TREESTORE_PATH, c,\r
+                                  TREESTORE_PARAMS, nselparams,\r
+                                  -1);\r
+               treeiterlevels[j] = treeiter;\r
+\r
+               selparams[nselparams].depth = j;\r
+               if (j > 0) {\r
+                   selparams[nselparams].treepath =\r
+                       gtk_tree_model_get_path(GTK_TREE_MODEL(treestore),\r
+                                               &treeiterlevels[j-1]);\r
+                   /*\r
+                    * We are going to collapse all tree branches\r
+                    * at depth greater than 2, but not _yet_; see\r
+                    * the comment at the call to\r
+                    * gtk_tree_view_collapse_row below.\r
+                    */\r
+                   gtk_tree_view_expand_row(GTK_TREE_VIEW(tree),\r
+                                            selparams[nselparams].treepath,\r
+                                            FALSE);\r
+               } else {\r
+                   selparams[nselparams].treepath = NULL;\r
+               }\r
+#else\r
+               treeitem = gtk_tree_item_new_with_label(c);\r
+               if (j > 0) {\r
+                   if (!treelevels[j-1]) {\r
+                       treelevels[j-1] = GTK_TREE(gtk_tree_new());\r
+                       gtk_tree_item_set_subtree\r
+                           (treeitemlevels[j-1],\r
+                            GTK_WIDGET(treelevels[j-1]));\r
+                        if (j < 2)\r
+                            gtk_tree_item_expand(treeitemlevels[j-1]);\r
+                        else\r
+                            gtk_tree_item_collapse(treeitemlevels[j-1]);\r
+                   }\r
+                   gtk_tree_append(treelevels[j-1], treeitem);\r
+               } else {\r
+                   gtk_tree_append(GTK_TREE(tree), treeitem);\r
+               }\r
+               treeitemlevels[j] = GTK_TREE_ITEM(treeitem);\r
+               treelevels[j] = NULL;\r
+\r
+                gtk_signal_connect(GTK_OBJECT(treeitem), "key_press_event",\r
+                                   GTK_SIGNAL_FUNC(tree_key_press), &dp);\r
+                gtk_signal_connect(GTK_OBJECT(treeitem), "focus_in_event",\r
+                                   GTK_SIGNAL_FUNC(widget_focus), &dp);\r
+\r
+               gtk_widget_show(treeitem);\r
+\r
+               if (first)\r
+                   gtk_tree_select_child(GTK_TREE(tree), treeitem);\r
+               selparams[nselparams].treeitem = treeitem;\r
+#endif\r
+\r
+               level = j+1;\r
+               nselparams++;\r
+           }\r
+\r
+           w = layout_ctrls(&dp, &selparams[nselparams-1].shortcuts, s, NULL);\r
+           gtk_box_pack_start(GTK_BOX(panelvbox), w, FALSE, FALSE, 0);\r
+            gtk_widget_show(w);\r
+       }\r
+    }\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    {\r
+       GtkRequisition req;\r
+       int i;\r
+\r
+       /*\r
+        * We want our tree view to come up with all branches at\r
+        * depth 2 or more collapsed. However, if we start off\r
+        * with those branches collapsed, then the tree view's\r
+        * size request will be calculated based on the width of\r
+        * the collapsed tree. So instead we start with them all\r
+        * expanded; then we ask for the current size request,\r
+        * collapse the relevant rows, and force the width to the\r
+        * value we just computed. This arranges that the tree\r
+        * view is wide enough to have all branches expanded\r
+        * safely.\r
+        */\r
+\r
+       gtk_widget_size_request(tree, &req);\r
+\r
+       for (i = 0; i < nselparams; i++)\r
+           if (selparams[i].depth >= 2)\r
+               gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree),\r
+                                          selparams[i].treepath);\r
+\r
+       gtk_widget_set_size_request(tree, req.width, -1);\r
+    }\r
+#endif\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    g_signal_connect(G_OBJECT(treeselection), "changed",\r
+                    G_CALLBACK(treeselection_changed), selparams);\r
+#else\r
+    dp.ntreeitems = nselparams;\r
+    dp.treeitems = snewn(dp.ntreeitems, GtkWidget *);\r
+\r
+    for (index = 0; index < nselparams; index++) {\r
+       gtk_signal_connect(GTK_OBJECT(selparams[index].treeitem), "select",\r
+                          GTK_SIGNAL_FUNC(treeitem_sel),\r
+                          &selparams[index]);\r
+        dp.treeitems[index] = selparams[index].treeitem;\r
+    }\r
+#endif\r
+\r
+    dp.data = cfg;\r
+    dlg_refresh(NULL, &dp);\r
+\r
+    dp.shortcuts = &selparams[0].shortcuts;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    dp.currtreeitem = dp.treeitems[0];\r
+#endif\r
+    dp.lastfocus = NULL;\r
+    dp.retval = 0;\r
+    dp.window = window;\r
+\r
+    {\r
+       /* in gtkwin.c */\r
+       extern void set_window_icon(GtkWidget *window,\r
+                                   const char *const *const *icon,\r
+                                   int n_icon);\r
+       extern const char *const *const cfg_icon[];\r
+       extern const int n_cfg_icon;\r
+       set_window_icon(window, cfg_icon, n_cfg_icon);\r
+    }\r
+\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll),\r
+                                         tree);\r
+#endif\r
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll),\r
+                                  GTK_POLICY_NEVER,\r
+                                  GTK_POLICY_AUTOMATIC);\r
+    gtk_widget_show(tree);\r
+\r
+    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);\r
+    gtk_widget_show(window);\r
+\r
+    /*\r
+     * Set focus into the first available control.\r
+     */\r
+    for (index = 0; index < ctrlbox->nctrlsets; index++) {\r
+       struct controlset *s = ctrlbox->ctrlsets[index];\r
+        int done = 0;\r
+        int j;\r
+\r
+       if (*s->pathname) {\r
+            for (j = 0; j < s->ncontrols; j++)\r
+                if (s->ctrls[j]->generic.type != CTRL_TABDELAY &&\r
+                    s->ctrls[j]->generic.type != CTRL_COLUMNS &&\r
+                    s->ctrls[j]->generic.type != CTRL_TEXT) {\r
+                    dlg_set_focus(s->ctrls[j], &dp);\r
+                    dp.lastfocus = s->ctrls[j];\r
+                    done = 1;\r
+                    break;\r
+                }\r
+        }\r
+        if (done)\r
+            break;\r
+    }\r
+\r
+    gtk_signal_connect(GTK_OBJECT(window), "destroy",\r
+                      GTK_SIGNAL_FUNC(window_destroy), NULL);\r
+    gtk_signal_connect(GTK_OBJECT(window), "key_press_event",\r
+                      GTK_SIGNAL_FUNC(win_key_press), &dp);\r
+\r
+    gtk_main();\r
+\r
+    dlg_cleanup(&dp);\r
+    sfree(selparams);\r
+\r
+    return dp.retval;\r
+}\r
+\r
+static void messagebox_handler(union control *ctrl, void *dlg,\r
+                              void *data, int event)\r
+{\r
+    if (event == EVENT_ACTION)\r
+       dlg_end(dlg, ctrl->generic.context.i);\r
+}\r
+int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...)\r
+{\r
+    GtkWidget *window, *w0, *w1;\r
+    struct controlbox *ctrlbox;\r
+    struct controlset *s0, *s1;\r
+    union control *c;\r
+    struct dlgparam dp;\r
+    struct Shortcuts scs;\r
+    int index, ncols;\r
+    va_list ap;\r
+\r
+    dlg_init(&dp);\r
+\r
+    for (index = 0; index < lenof(scs.sc); index++) {\r
+       scs.sc[index].action = SHORTCUT_EMPTY;\r
+    }\r
+\r
+    ctrlbox = ctrl_new_box();\r
+\r
+    ncols = 0;\r
+    va_start(ap, minwid);\r
+    while (va_arg(ap, char *) != NULL) {\r
+       ncols++;\r
+       (void) va_arg(ap, int);        /* shortcut */\r
+       (void) va_arg(ap, int);        /* normal/default/cancel */\r
+       (void) va_arg(ap, int);        /* end value */\r
+    }\r
+    va_end(ap);\r
+\r
+    s0 = ctrl_getset(ctrlbox, "", "", "");\r
+    c = ctrl_columns(s0, 2, 50, 50);\r
+    c->columns.ncols = s0->ncolumns = ncols;\r
+    c->columns.percentages = sresize(c->columns.percentages, ncols, int);\r
+    for (index = 0; index < ncols; index++)\r
+       c->columns.percentages[index] = (index+1)*100/ncols - index*100/ncols;\r
+    va_start(ap, minwid);\r
+    index = 0;\r
+    while (1) {\r
+       char *title = va_arg(ap, char *);\r
+       int shortcut, type, value;\r
+       if (title == NULL)\r
+           break;\r
+       shortcut = va_arg(ap, int);\r
+       type = va_arg(ap, int);\r
+       value = va_arg(ap, int);\r
+       c = ctrl_pushbutton(s0, title, shortcut, HELPCTX(no_help),\r
+                           messagebox_handler, I(value));\r
+       c->generic.column = index++;\r
+       if (type > 0)\r
+           c->button.isdefault = TRUE;\r
+       else if (type < 0)\r
+           c->button.iscancel = TRUE;\r
+    }\r
+    va_end(ap);\r
+\r
+    s1 = ctrl_getset(ctrlbox, "x", "", "");\r
+    ctrl_text(s1, msg, HELPCTX(no_help));\r
+\r
+    window = gtk_dialog_new();\r
+    gtk_window_set_title(GTK_WINDOW(window), title);\r
+    w0 = layout_ctrls(&dp, &scs, s0, GTK_WINDOW(window));\r
+    set_dialog_action_area(GTK_DIALOG(window), w0);\r
+    gtk_widget_show(w0);\r
+    w1 = layout_ctrls(&dp, &scs, s1, GTK_WINDOW(window));\r
+    gtk_container_set_border_width(GTK_CONTAINER(w1), 10);\r
+    gtk_widget_set_usize(w1, minwid+20, -1);\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),\r
+                      w1, TRUE, TRUE, 0);\r
+    gtk_widget_show(w1);\r
+\r
+    dp.shortcuts = &scs;\r
+    dp.lastfocus = NULL;\r
+    dp.retval = 0;\r
+    dp.window = window;\r
+\r
+    gtk_window_set_modal(GTK_WINDOW(window), TRUE);\r
+    if (parentwin) {\r
+        set_transient_window_pos(parentwin, window);\r
+       gtk_window_set_transient_for(GTK_WINDOW(window),\r
+                                    GTK_WINDOW(parentwin));\r
+    } else\r
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);\r
+    gtk_widget_show(window);\r
+\r
+    gtk_signal_connect(GTK_OBJECT(window), "destroy",\r
+                      GTK_SIGNAL_FUNC(window_destroy), NULL);\r
+    gtk_signal_connect(GTK_OBJECT(window), "key_press_event",\r
+                      GTK_SIGNAL_FUNC(win_key_press), &dp);\r
+\r
+    gtk_main();\r
+\r
+    dlg_cleanup(&dp);\r
+    ctrl_free_box(ctrlbox);\r
+\r
+    return dp.retval;\r
+}\r
+\r
+static int string_width(char *text)\r
+{\r
+    GtkWidget *label = gtk_label_new(text);\r
+    GtkRequisition req;\r
+    gtk_widget_size_request(label, &req);\r
+    gtk_object_sink(GTK_OBJECT(label));\r
+    return req.width;\r
+}\r
+\r
+int reallyclose(void *frontend)\r
+{\r
+    char *title = dupcat(appname, " Exit Confirmation", NULL);\r
+    int ret = messagebox(GTK_WIDGET(get_window(frontend)),\r
+                        title, "Are you sure you want to close this session?",\r
+                        string_width("Most of the width of the above text"),\r
+                        "Yes", 'y', +1, 1,\r
+                        "No", 'n', -1, 0,\r
+                        NULL);\r
+    sfree(title);\r
+    return ret;\r
+}\r
+\r
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
+                        char *keystr, char *fingerprint,\r
+                        void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char absenttxt[] =\r
+       "The server's host key is not cached. You have no guarantee "\r
+       "that the server is the computer you think it is.\n"\r
+       "The server's %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you trust this host, press \"Accept\" to add the key to "\r
+       "PuTTY's cache and carry on connecting.\n"\r
+       "If you want to carry on connecting just once, without "\r
+       "adding the key to the cache, press \"Connect Once\".\n"\r
+       "If you do not trust this host, press \"Cancel\" to abandon the "\r
+       "connection.";\r
+    static const char wrongtxt[] =\r
+       "WARNING - POTENTIAL SECURITY BREACH!\n"\r
+       "The server's host key does not match the one PuTTY has "\r
+       "cached. This means that either the server administrator "\r
+       "has changed the host key, or you have actually connected "\r
+       "to another computer pretending to be the server.\n"\r
+       "The new %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you were expecting this change and trust the new key, "\r
+       "press \"Accept\" to update PuTTY's cache and continue connecting.\n"\r
+       "If you want to carry on connecting but without updating "\r
+       "the cache, press \"Connect Once\".\n"\r
+       "If you want to abandon the connection completely, press "\r
+       "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "\r
+       "safe choice.";\r
+    char *text;\r
+    int ret;\r
+\r
+    /*\r
+     * Verify the key.\r
+     */\r
+    ret = verify_host_key(host, port, keytype, keystr);\r
+\r
+    if (ret == 0)                     /* success - key matched OK */\r
+       return 1;\r
+\r
+    text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);\r
+\r
+    ret = messagebox(GTK_WIDGET(get_window(frontend)),\r
+                    "PuTTY Security Alert", text,\r
+                    string_width(fingerprint),\r
+                    "Accept", 'a', 0, 2,\r
+                    "Connect Once", 'o', 0, 1,\r
+                    "Cancel", 'c', -1, 0,\r
+                    NULL);\r
+\r
+    sfree(text);\r
+\r
+    if (ret == 2) {\r
+       store_host_key(host, port, keytype, keystr);\r
+       return 1;                      /* continue with connection */\r
+    } else if (ret == 1)\r
+       return 1;                      /* continue with connection */\r
+    return 0;                         /* do not continue with connection */\r
+}\r
+\r
+/*\r
+ * Ask whether the selected algorithm is acceptable (since it was\r
+ * below the configured 'warn' threshold).\r
+ */\r
+int askalg(void *frontend, const char *algtype, const char *algname,\r
+          void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char msg[] =\r
+       "The first %s supported by the server is "\r
+       "%s, which is below the configured warning threshold.\n"\r
+       "Continue with connection?";\r
+    char *text;\r
+    int ret;\r
+\r
+    text = dupprintf(msg, algtype, algname);\r
+    ret = messagebox(GTK_WIDGET(get_window(frontend)),\r
+                    "PuTTY Security Alert", text,\r
+                    string_width("Continue with connection?"),\r
+                    "Yes", 'y', 0, 1,\r
+                    "No", 'n', 0, 0,\r
+                    NULL);\r
+    sfree(text);\r
+\r
+    if (ret) {\r
+       return 1;\r
+    } else {\r
+       return 0;\r
+    }\r
+}\r
+\r
+void old_keyfile_warning(void)\r
+{\r
+    /*\r
+     * This should never happen on Unix. We hope.\r
+     */\r
+}\r
+\r
+void fatal_message_box(void *window, char *msg)\r
+{\r
+    messagebox(window, "PuTTY Fatal Error", msg,\r
+               string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"),\r
+               "OK", 'o', 1, 1, NULL);\r
+}\r
+\r
+void fatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    char *msg;\r
+    va_start(ap, p);\r
+    msg = dupvprintf(p, ap);\r
+    va_end(ap);\r
+    fatal_message_box(NULL, msg);\r
+    sfree(msg);\r
+    cleanup_exit(1);\r
+}\r
+\r
+static GtkWidget *aboutbox = NULL;\r
+\r
+static void about_close_clicked(GtkButton *button, gpointer data)\r
+{\r
+    gtk_widget_destroy(aboutbox);\r
+    aboutbox = NULL;\r
+}\r
+\r
+static void licence_clicked(GtkButton *button, gpointer data)\r
+{\r
+    char *title;\r
+\r
+    char *licence =\r
+       "Copyright 1997-2011 Simon Tatham.\n\n"\r
+\r
+       "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "\r
+       "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "\r
+       "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "\r
+       "Markus Kuhn, Colin Watson, and CORE SDI S.A.\n\n"\r
+\r
+       "Permission is hereby granted, free of charge, to any person "\r
+       "obtaining a copy of this software and associated documentation "\r
+       "files (the ""Software""), to deal in the Software without restriction, "\r
+       "including without limitation the rights to use, copy, modify, merge, "\r
+       "publish, distribute, sublicense, and/or sell copies of the Software, "\r
+       "and to permit persons to whom the Software is furnished to do so, "\r
+       "subject to the following conditions:\n\n"\r
+\r
+       "The above copyright notice and this permission notice shall be "\r
+       "included in all copies or substantial portions of the Software.\n\n"\r
+\r
+       "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "\r
+       "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "\r
+       "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "\r
+       "MERCHANTABILITY, FITNESS FOR A PARTICULAR "\r
+       "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE "\r
+       "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "\r
+       "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "\r
+       "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "\r
+       "CONNECTION WITH THE SOFTWARE OR THE USE OR "\r
+       "OTHER DEALINGS IN THE SOFTWARE.";\r
+\r
+    title = dupcat(appname, " Licence", NULL);\r
+    assert(aboutbox != NULL);\r
+    messagebox(aboutbox, title, licence,\r
+              string_width("LONGISH LINE OF TEXT SO THE LICENCE"\r
+                           " BOX ISN'T EXCESSIVELY TALL AND THIN"),\r
+              "OK", 'o', 1, 1, NULL);\r
+    sfree(title);\r
+}\r
+\r
+void about_box(void *window)\r
+{\r
+    GtkWidget *w;\r
+    char *title;\r
+\r
+    if (aboutbox) {\r
+        gtk_widget_grab_focus(aboutbox);\r
+       return;\r
+    }\r
+\r
+    aboutbox = gtk_dialog_new();\r
+    gtk_container_set_border_width(GTK_CONTAINER(aboutbox), 10);\r
+    title = dupcat("About ", appname, NULL);\r
+    gtk_window_set_title(GTK_WINDOW(aboutbox), title);\r
+    sfree(title);\r
+\r
+    w = gtk_button_new_with_label("Close");\r
+    GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);\r
+    gtk_window_set_default(GTK_WINDOW(aboutbox), w);\r
+    gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox)->action_area),\r
+                    w, FALSE, FALSE, 0);\r
+    gtk_signal_connect(GTK_OBJECT(w), "clicked",\r
+                      GTK_SIGNAL_FUNC(about_close_clicked), NULL);\r
+    gtk_widget_show(w);\r
+\r
+    w = gtk_button_new_with_label("View Licence");\r
+    GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);\r
+    gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox)->action_area),\r
+                    w, FALSE, FALSE, 0);\r
+    gtk_signal_connect(GTK_OBJECT(w), "clicked",\r
+                      GTK_SIGNAL_FUNC(licence_clicked), NULL);\r
+    gtk_widget_show(w);\r
+\r
+    w = gtk_label_new(appname);\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox),\r
+                      w, FALSE, FALSE, 0);\r
+    gtk_widget_show(w);\r
+\r
+    w = gtk_label_new(ver);\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox),\r
+                      w, FALSE, FALSE, 5);\r
+    gtk_widget_show(w);\r
+\r
+    w = gtk_label_new("Copyright 1997-2011 Simon Tatham. All rights reserved");\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox),\r
+                      w, FALSE, FALSE, 5);\r
+    gtk_widget_show(w);\r
+\r
+    set_transient_window_pos(GTK_WIDGET(window), aboutbox);\r
+    gtk_window_set_transient_for(GTK_WINDOW(aboutbox),\r
+                                GTK_WINDOW(window));\r
+    gtk_widget_show(aboutbox);\r
+}\r
+\r
+struct eventlog_stuff {\r
+    GtkWidget *parentwin, *window;\r
+    struct controlbox *eventbox;\r
+    struct Shortcuts scs;\r
+    struct dlgparam dp;\r
+    union control *listctrl;\r
+    char **events;\r
+    int nevents, negsize;\r
+    char *seldata;\r
+    int sellen;\r
+    int ignore_selchange;\r
+};\r
+\r
+static void eventlog_destroy(GtkWidget *widget, gpointer data)\r
+{\r
+    struct eventlog_stuff *es = (struct eventlog_stuff *)data;\r
+\r
+    es->window = NULL;\r
+    sfree(es->seldata);\r
+    dlg_cleanup(&es->dp);\r
+    ctrl_free_box(es->eventbox);\r
+}\r
+static void eventlog_ok_handler(union control *ctrl, void *dlg,\r
+                               void *data, int event)\r
+{\r
+    if (event == EVENT_ACTION)\r
+       dlg_end(dlg, 0);\r
+}\r
+static void eventlog_list_handler(union control *ctrl, void *dlg,\r
+                                 void *data, int event)\r
+{\r
+    struct eventlog_stuff *es = (struct eventlog_stuff *)data;\r
+\r
+    if (event == EVENT_REFRESH) {\r
+       int i;\r
+\r
+       dlg_update_start(ctrl, dlg);\r
+       dlg_listbox_clear(ctrl, dlg);\r
+       for (i = 0; i < es->nevents; i++) {\r
+           dlg_listbox_add(ctrl, dlg, es->events[i]);\r
+       }\r
+       dlg_update_done(ctrl, dlg);\r
+    } else if (event == EVENT_SELCHANGE) {\r
+        int i;\r
+        int selsize = 0;\r
+\r
+        /*\r
+         * If this SELCHANGE event is happening as a result of\r
+         * deliberate deselection because someone else has grabbed\r
+         * the selection, the last thing we want to do is pre-empt\r
+         * them.\r
+         */\r
+        if (es->ignore_selchange)\r
+            return;\r
+\r
+        /*\r
+         * Construct the data to use as the selection.\r
+         */\r
+        sfree(es->seldata);\r
+        es->seldata = NULL;\r
+        es->sellen = 0;\r
+        for (i = 0; i < es->nevents; i++) {\r
+            if (dlg_listbox_issel(ctrl, dlg, i)) {\r
+                int extralen = strlen(es->events[i]);\r
+\r
+                if (es->sellen + extralen + 2 > selsize) {\r
+                    selsize = es->sellen + extralen + 512;\r
+                    es->seldata = sresize(es->seldata, selsize, char);\r
+                }\r
+\r
+                strcpy(es->seldata + es->sellen, es->events[i]);\r
+                es->sellen += extralen;\r
+                es->seldata[es->sellen++] = '\n';\r
+            }\r
+        }\r
+\r
+        if (gtk_selection_owner_set(es->window, GDK_SELECTION_PRIMARY,\r
+                                    GDK_CURRENT_TIME)) {\r
+            extern GdkAtom compound_text_atom;\r
+\r
+            gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY,\r
+                                     GDK_SELECTION_TYPE_STRING, 1);\r
+            gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY,\r
+                                     compound_text_atom, 1);\r
+        }\r
+\r
+    }\r
+}\r
+\r
+void eventlog_selection_get(GtkWidget *widget, GtkSelectionData *seldata,\r
+                            guint info, guint time_stamp, gpointer data)\r
+{\r
+    struct eventlog_stuff *es = (struct eventlog_stuff *)data;\r
+\r
+    gtk_selection_data_set(seldata, seldata->target, 8,\r
+                           (unsigned char *)es->seldata, es->sellen);\r
+}\r
+\r
+gint eventlog_selection_clear(GtkWidget *widget, GdkEventSelection *seldata,\r
+                              gpointer data)\r
+{\r
+    struct eventlog_stuff *es = (struct eventlog_stuff *)data;\r
+    struct uctrl *uc;\r
+\r
+    /*\r
+     * Deselect everything in the list box.\r
+     */\r
+    uc = dlg_find_byctrl(&es->dp, es->listctrl);\r
+    es->ignore_selchange = 1;\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+    assert(uc->list);\r
+    gtk_list_unselect_all(GTK_LIST(uc->list));\r
+#else\r
+    assert(uc->treeview);\r
+    gtk_tree_selection_unselect_all\r
+       (gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview)));\r
+#endif\r
+    es->ignore_selchange = 0;\r
+\r
+    sfree(es->seldata);\r
+    es->sellen = 0;\r
+    es->seldata = NULL;\r
+    return TRUE;\r
+}\r
+\r
+void showeventlog(void *estuff, void *parentwin)\r
+{\r
+    struct eventlog_stuff *es = (struct eventlog_stuff *)estuff;\r
+    GtkWidget *window, *w0, *w1;\r
+    GtkWidget *parent = GTK_WIDGET(parentwin);\r
+    struct controlset *s0, *s1;\r
+    union control *c;\r
+    int index;\r
+    char *title;\r
+\r
+    if (es->window) {\r
+        gtk_widget_grab_focus(es->window);\r
+       return;\r
+    }\r
+\r
+    dlg_init(&es->dp);\r
+\r
+    for (index = 0; index < lenof(es->scs.sc); index++) {\r
+       es->scs.sc[index].action = SHORTCUT_EMPTY;\r
+    }\r
+\r
+    es->eventbox = ctrl_new_box();\r
+\r
+    s0 = ctrl_getset(es->eventbox, "", "", "");\r
+    ctrl_columns(s0, 3, 33, 34, 33);\r
+    c = ctrl_pushbutton(s0, "Close", 'c', HELPCTX(no_help),\r
+                       eventlog_ok_handler, P(NULL));\r
+    c->button.column = 1;\r
+    c->button.isdefault = TRUE;\r
+\r
+    s1 = ctrl_getset(es->eventbox, "x", "", "");\r
+    es->listctrl = c = ctrl_listbox(s1, NULL, NO_SHORTCUT, HELPCTX(no_help),\r
+                                   eventlog_list_handler, P(es));\r
+    c->listbox.height = 10;\r
+    c->listbox.multisel = 2;\r
+    c->listbox.ncols = 3;\r
+    c->listbox.percentages = snewn(3, int);\r
+    c->listbox.percentages[0] = 25;\r
+    c->listbox.percentages[1] = 10;\r
+    c->listbox.percentages[2] = 65;\r
+\r
+    es->window = window = gtk_dialog_new();\r
+    title = dupcat(appname, " Event Log", NULL);\r
+    gtk_window_set_title(GTK_WINDOW(window), title);\r
+    sfree(title);\r
+    w0 = layout_ctrls(&es->dp, &es->scs, s0, GTK_WINDOW(window));\r
+    set_dialog_action_area(GTK_DIALOG(window), w0);\r
+    gtk_widget_show(w0);\r
+    w1 = layout_ctrls(&es->dp, &es->scs, s1, GTK_WINDOW(window));\r
+    gtk_container_set_border_width(GTK_CONTAINER(w1), 10);\r
+    gtk_widget_set_usize(w1, 20 +\r
+                        string_width("LINE OF TEXT GIVING WIDTH OF EVENT LOG"\r
+                                     " IS QUITE LONG 'COS SSH LOG ENTRIES"\r
+                                     " ARE WIDE"), -1);\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),\r
+                      w1, TRUE, TRUE, 0);\r
+    gtk_widget_show(w1);\r
+\r
+    es->dp.data = es;\r
+    es->dp.shortcuts = &es->scs;\r
+    es->dp.lastfocus = NULL;\r
+    es->dp.retval = 0;\r
+    es->dp.window = window;\r
+\r
+    dlg_refresh(NULL, &es->dp);\r
+\r
+    if (parent) {\r
+        set_transient_window_pos(parent, window);\r
+       gtk_window_set_transient_for(GTK_WINDOW(window),\r
+                                    GTK_WINDOW(parent));\r
+    } else\r
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);\r
+    gtk_widget_show(window);\r
+\r
+    gtk_signal_connect(GTK_OBJECT(window), "destroy",\r
+                      GTK_SIGNAL_FUNC(eventlog_destroy), es);\r
+    gtk_signal_connect(GTK_OBJECT(window), "key_press_event",\r
+                      GTK_SIGNAL_FUNC(win_key_press), &es->dp);\r
+    gtk_signal_connect(GTK_OBJECT(window), "selection_get",\r
+                      GTK_SIGNAL_FUNC(eventlog_selection_get), es);\r
+    gtk_signal_connect(GTK_OBJECT(window), "selection_clear_event",\r
+                      GTK_SIGNAL_FUNC(eventlog_selection_clear), es);\r
+}\r
+\r
+void *eventlogstuff_new(void)\r
+{\r
+    struct eventlog_stuff *es;\r
+    es = snew(struct eventlog_stuff);\r
+    memset(es, 0, sizeof(*es));\r
+    return es;\r
+}\r
+\r
+void logevent_dlg(void *estuff, const char *string)\r
+{\r
+    struct eventlog_stuff *es = (struct eventlog_stuff *)estuff;\r
+\r
+    char timebuf[40];\r
+    struct tm tm;\r
+\r
+    if (es->nevents >= es->negsize) {\r
+       es->negsize += 64;\r
+       es->events = sresize(es->events, es->negsize, char *);\r
+    }\r
+\r
+    tm=ltime();\r
+    strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);\r
+\r
+    es->events[es->nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);\r
+    strcpy(es->events[es->nevents], timebuf);\r
+    strcat(es->events[es->nevents], string);\r
+    if (es->window) {\r
+       dlg_listbox_add(es->listctrl, &es->dp, es->events[es->nevents]);\r
+    }\r
+    es->nevents++;\r
+}\r
+\r
+int askappend(void *frontend, Filename filename,\r
+             void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char msgtemplate[] =\r
+       "The session log file \"%.*s\" already exists. "\r
+       "You can overwrite it with a new session log, "\r
+       "append your session log to the end of it, "\r
+       "or disable session logging for this session.";\r
+    char *message;\r
+    char *mbtitle;\r
+    int mbret;\r
+\r
+    message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);\r
+    mbtitle = dupprintf("%s Log to File", appname);\r
+\r
+    mbret = messagebox(get_window(frontend), mbtitle, message,\r
+                      string_width("LINE OF TEXT SUITABLE FOR THE"\r
+                                   " ASKAPPEND WIDTH"),\r
+                      "Overwrite", 'o', 1, 2,\r
+                      "Append", 'a', 0, 1,\r
+                      "Disable", 'd', -1, 0,\r
+                      NULL);\r
+\r
+    sfree(message);\r
+    sfree(mbtitle);\r
+\r
+    return mbret;\r
+}\r
diff --git a/putty/UNIX/GTKFONT.C b/putty/UNIX/GTKFONT.C
new file mode 100644 (file)
index 0000000..e382183
--- /dev/null
@@ -0,0 +1,2575 @@
+/*\r
+ * Unified font management for GTK.\r
+ * \r
+ * PuTTY is willing to use both old-style X server-side bitmap\r
+ * fonts _and_ GTK2/Pango client-side fonts. This requires us to\r
+ * do a bit of work to wrap the two wildly different APIs into\r
+ * forms the rest of the code can switch between seamlessly, and\r
+ * also requires a custom font selector capable of handling both\r
+ * types of font.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <gtk/gtk.h>\r
+#include <gdk/gdkkeysyms.h>\r
+#include <gdk/gdkx.h>\r
+#include <X11/Xlib.h>\r
+#include <X11/Xutil.h>\r
+#include <X11/Xatom.h>\r
+\r
+#include "putty.h"\r
+#include "gtkfont.h"\r
+#include "tree234.h"\r
+\r
+/*\r
+ * Future work:\r
+ * \r
+ *  - it would be nice to have a display of the current font name,\r
+ *    and in particular whether it's client- or server-side,\r
+ *    during the progress of the font selector.\r
+ * \r
+ *  - all the GDK font functions used in the x11font subclass are\r
+ *    deprecated, so one day they may go away. When this happens -\r
+ *    or before, if I'm feeling proactive - it oughtn't to be too\r
+ *    difficult in principle to convert the whole thing to use\r
+ *    actual Xlib font calls.\r
+ * \r
+ *  - it would be nice if we could move the processing of\r
+ *    underline and VT100 double width into this module, so that\r
+ *    instead of using the ghastly pixmap-stretching technique\r
+ *    everywhere we could tell the Pango backend to scale its\r
+ *    fonts to double size properly and at full resolution.\r
+ *    However, this requires me to learn how to make Pango stretch\r
+ *    text to an arbitrary aspect ratio (for double-width only\r
+ *    text, which perversely is harder than DW+DH), and right now\r
+ *    I haven't the energy.\r
+ */\r
+\r
+/*\r
+ * Ad-hoc vtable mechanism to allow font structures to be\r
+ * polymorphic.\r
+ * \r
+ * Any instance of `unifont' used in the vtable functions will\r
+ * actually be the first element of a larger structure containing\r
+ * data specific to the subtype. This is permitted by the ISO C\r
+ * provision that one may safely cast between a pointer to a\r
+ * structure and a pointer to its first element.\r
+ */\r
+\r
+#define FONTFLAG_CLIENTSIDE    0x0001\r
+#define FONTFLAG_SERVERSIDE    0x0002\r
+#define FONTFLAG_SERVERALIAS   0x0004\r
+#define FONTFLAG_NONMONOSPACED 0x0008\r
+\r
+#define FONTFLAG_SORT_MASK     0x0007 /* used to disambiguate font families */\r
+\r
+typedef void (*fontsel_add_entry)(void *ctx, const char *realfontname,\r
+                                 const char *family, const char *charset,\r
+                                 const char *style, const char *stylekey,\r
+                                 int size, int flags,\r
+                                 const struct unifont_vtable *fontclass);\r
+\r
+struct unifont_vtable {\r
+    /*\r
+     * `Methods' of the `class'.\r
+     */\r
+    unifont *(*create)(GtkWidget *widget, const char *name, int wide, int bold,\r
+                      int shadowoffset, int shadowalways);\r
+    void (*destroy)(unifont *font);\r
+    void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font,\r
+                     int x, int y, const char *string, int len, int wide,\r
+                     int bold, int cellwidth);\r
+    void (*enum_fonts)(GtkWidget *widget,\r
+                      fontsel_add_entry callback, void *callback_ctx);\r
+    char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size,\r
+                              int *flags, int resolve_aliases);\r
+    char *(*scale_fontname)(GtkWidget *widget, const char *name, int size);\r
+\r
+    /*\r
+     * `Static data members' of the `class'.\r
+     */\r
+    const char *prefix;\r
+};\r
+\r
+/* ----------------------------------------------------------------------\r
+ * GDK-based X11 font implementation.\r
+ */\r
+\r
+static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,\r
+                             int x, int y, const char *string, int len,\r
+                             int wide, int bold, int cellwidth);\r
+static unifont *x11font_create(GtkWidget *widget, const char *name,\r
+                              int wide, int bold,\r
+                              int shadowoffset, int shadowalways);\r
+static void x11font_destroy(unifont *font);\r
+static void x11font_enum_fonts(GtkWidget *widget,\r
+                              fontsel_add_entry callback, void *callback_ctx);\r
+static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,\r
+                                      int *size, int *flags,\r
+                                      int resolve_aliases);\r
+static char *x11font_scale_fontname(GtkWidget *widget, const char *name,\r
+                                   int size);\r
+\r
+struct x11font {\r
+    struct unifont u;\r
+    /*\r
+     * Actual font objects. We store a number of these, for\r
+     * automatically guessed bold and wide variants.\r
+     * \r
+     * The parallel array `allocated' indicates whether we've\r
+     * tried to fetch a subfont already (thus distinguishing NULL\r
+     * because we haven't tried yet from NULL because we tried and\r
+     * failed, so that we don't keep trying and failing\r
+     * subsequently).\r
+     */\r
+    GdkFont *fonts[4];\r
+    int allocated[4];\r
+    /*\r
+     * `sixteen_bit' is true iff the font object is indexed by\r
+     * values larger than a byte. That is, this flag tells us\r
+     * whether we use gdk_draw_text_wc() or gdk_draw_text().\r
+     */\r
+    int sixteen_bit;\r
+    /*\r
+     * `variable' is true iff the font is non-fixed-pitch. This\r
+     * enables some code which takes greater care over character\r
+     * positioning during text drawing.\r
+     */\r
+    int variable;\r
+    /*\r
+     * Data passed in to unifont_create().\r
+     */\r
+    int wide, bold, shadowoffset, shadowalways;\r
+};\r
+\r
+static const struct unifont_vtable x11font_vtable = {\r
+    x11font_create,\r
+    x11font_destroy,\r
+    x11font_draw_text,\r
+    x11font_enum_fonts,\r
+    x11font_canonify_fontname,\r
+    x11font_scale_fontname,\r
+    "server",\r
+};\r
+\r
+char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide)\r
+{\r
+    XFontStruct *xfs = GDK_FONT_XFONT(font);\r
+    Display *disp = GDK_FONT_XDISPLAY(font);\r
+    Atom fontprop = XInternAtom(disp, "FONT", False);\r
+    unsigned long ret;\r
+    if (XGetFontProperty(xfs, fontprop, &ret)) {\r
+       char *name = XGetAtomName(disp, (Atom)ret);\r
+       if (name && name[0] == '-') {\r
+           char *strings[13];\r
+           char *dupname, *extrafree = NULL, *ret;\r
+           char *p, *q;\r
+           int nstr;\r
+\r
+           p = q = dupname = dupstr(name); /* skip initial minus */\r
+           nstr = 0;\r
+\r
+           while (*p && nstr < lenof(strings)) {\r
+               if (*p == '-') {\r
+                   *p = '\0';\r
+                   strings[nstr++] = p+1;\r
+               }\r
+               p++;\r
+           }\r
+\r
+           if (nstr < lenof(strings))\r
+               return NULL;           /* XLFD was malformed */\r
+\r
+           if (bold)\r
+               strings[2] = "bold";\r
+\r
+           if (wide) {\r
+               /* 4 is `wideness', which obviously may have changed. */\r
+               /* 5 is additional style, which may be e.g. `ja' or `ko'. */\r
+               strings[4] = strings[5] = "*";\r
+               strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11]));\r
+           }\r
+\r
+           ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2],\r
+                        "-", strings[ 3], "-", strings[ 4], "-", strings[ 5],\r
+                        "-", strings[ 6], "-", strings[ 7], "-", strings[ 8],\r
+                        "-", strings[ 9], "-", strings[10], "-", strings[11],\r
+                        "-", strings[12], NULL);\r
+           sfree(extrafree);\r
+           sfree(dupname);\r
+\r
+           return ret;\r
+       }\r
+    }\r
+    return NULL;\r
+}\r
+\r
+static int x11_font_width(GdkFont *font, int sixteen_bit)\r
+{\r
+    if (sixteen_bit) {\r
+       XChar2b space;\r
+       space.byte1 = 0;\r
+       space.byte2 = '0';\r
+       return gdk_text_width(font, (const gchar *)&space, 2);\r
+    } else {\r
+       return gdk_char_width(font, '0');\r
+    }\r
+}\r
+\r
+static unifont *x11font_create(GtkWidget *widget, const char *name,\r
+                              int wide, int bold,\r
+                              int shadowoffset, int shadowalways)\r
+{\r
+    struct x11font *xfont;\r
+    GdkFont *font;\r
+    XFontStruct *xfs;\r
+    Display *disp;\r
+    Atom charset_registry, charset_encoding, spacing;\r
+    unsigned long registry_ret, encoding_ret, spacing_ret;\r
+    int pubcs, realcs, sixteen_bit, variable;\r
+    int i;\r
+\r
+    font = gdk_font_load(name);\r
+    if (!font)\r
+       return NULL;\r
+\r
+    xfs = GDK_FONT_XFONT(font);\r
+    disp = GDK_FONT_XDISPLAY(font);\r
+\r
+    charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False);\r
+    charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False);\r
+\r
+    pubcs = realcs = CS_NONE;\r
+    sixteen_bit = FALSE;\r
+    variable = TRUE;\r
+\r
+    if (XGetFontProperty(xfs, charset_registry, &registry_ret) &&\r
+       XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {\r
+       char *reg, *enc;\r
+       reg = XGetAtomName(disp, (Atom)registry_ret);\r
+       enc = XGetAtomName(disp, (Atom)encoding_ret);\r
+       if (reg && enc) {\r
+           char *encoding = dupcat(reg, "-", enc, NULL);\r
+           pubcs = realcs = charset_from_xenc(encoding);\r
+\r
+           /*\r
+            * iso10646-1 is the only wide font encoding we\r
+            * support. In this case, we expect clients to give us\r
+            * UTF-8, which this module must internally convert\r
+            * into 16-bit Unicode.\r
+            */\r
+           if (!strcasecmp(encoding, "iso10646-1")) {\r
+               sixteen_bit = TRUE;\r
+               pubcs = realcs = CS_UTF8;\r
+           }\r
+\r
+           /*\r
+            * Hack for X line-drawing characters: if the primary\r
+            * font is encoded as ISO-8859-1, and has valid glyphs\r
+            * in the first 32 char positions, it is assumed that\r
+            * those glyphs are the VT100 line-drawing character\r
+            * set.\r
+            * \r
+            * Actually, we'll hack even harder by only checking\r
+            * position 0x19 (vertical line, VT100 linedrawing\r
+            * `x'). Then we can check it easily by seeing if the\r
+            * ascent and descent differ.\r
+            */\r
+           if (pubcs == CS_ISO8859_1) {\r
+               int lb, rb, wid, asc, desc;\r
+               gchar text[2];\r
+\r
+               text[1] = '\0';\r
+               text[0] = '\x12';\r
+               gdk_string_extents(font, text, &lb, &rb, &wid, &asc, &desc);\r
+               if (asc != desc)\r
+                   realcs = CS_ISO8859_1_X11;\r
+           }\r
+\r
+           sfree(encoding);\r
+       }\r
+    }\r
+\r
+    spacing = XInternAtom(disp, "SPACING", False);\r
+    if (XGetFontProperty(xfs, spacing, &spacing_ret)) {\r
+       char *spc;\r
+       spc = XGetAtomName(disp, (Atom)spacing_ret);\r
+\r
+       if (spc && strchr("CcMm", spc[0]))\r
+           variable = FALSE;\r
+    }\r
+\r
+    xfont = snew(struct x11font);\r
+    xfont->u.vt = &x11font_vtable;\r
+    xfont->u.width = x11_font_width(font, sixteen_bit);\r
+    xfont->u.ascent = font->ascent;\r
+    xfont->u.descent = font->descent;\r
+    xfont->u.height = xfont->u.ascent + xfont->u.descent;\r
+    xfont->u.public_charset = pubcs;\r
+    xfont->u.real_charset = realcs;\r
+    xfont->fonts[0] = font;\r
+    xfont->allocated[0] = TRUE;\r
+    xfont->sixteen_bit = sixteen_bit;\r
+    xfont->variable = variable;\r
+    xfont->wide = wide;\r
+    xfont->bold = bold;\r
+    xfont->shadowoffset = shadowoffset;\r
+    xfont->shadowalways = shadowalways;\r
+\r
+    for (i = 1; i < lenof(xfont->fonts); i++) {\r
+       xfont->fonts[i] = NULL;\r
+       xfont->allocated[i] = FALSE;\r
+    }\r
+\r
+    return (unifont *)xfont;\r
+}\r
+\r
+static void x11font_destroy(unifont *font)\r
+{\r
+    struct x11font *xfont = (struct x11font *)font;\r
+    int i;\r
+\r
+    for (i = 0; i < lenof(xfont->fonts); i++)\r
+       if (xfont->fonts[i])\r
+           gdk_font_unref(xfont->fonts[i]);\r
+    sfree(font);\r
+}\r
+\r
+static void x11_alloc_subfont(struct x11font *xfont, int sfid)\r
+{\r
+    char *derived_name = x11_guess_derived_font_name\r
+       (xfont->fonts[0], sfid & 1, !!(sfid & 2));\r
+    xfont->fonts[sfid] = gdk_font_load(derived_name);   /* may be NULL */\r
+    xfont->allocated[sfid] = TRUE;\r
+    sfree(derived_name);\r
+}\r
+\r
+static void x11font_really_draw_text(GdkDrawable *target, GdkFont *font,\r
+                                    GdkGC *gc, int x, int y,\r
+                                    const gchar *string, int clen, int nchars,\r
+                                    int shadowbold, int shadowoffset,\r
+                                    int fontvariable, int cellwidth)\r
+{\r
+    int step = clen * nchars, nsteps = 1, centre = FALSE;\r
+\r
+    if (fontvariable) {\r
+       /*\r
+        * In a variable-pitch font, we draw one character at a\r
+        * time, and centre it in the character cell.\r
+        */\r
+       step = clen;\r
+       nsteps = nchars;\r
+       centre = TRUE;\r
+    }\r
+\r
+    while (nsteps-- > 0) {\r
+       int X = x;\r
+       if (centre)\r
+           X += (cellwidth - gdk_text_width(font, string, step)) / 2;\r
+\r
+       gdk_draw_text(target, font, gc, X, y, string, step);\r
+       if (shadowbold)\r
+           gdk_draw_text(target, font, gc, X + shadowoffset, y, string, step);\r
+\r
+       x += cellwidth;\r
+       string += step;\r
+    }\r
+}\r
+\r
+static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,\r
+                             int x, int y, const char *string, int len,\r
+                             int wide, int bold, int cellwidth)\r
+{\r
+    struct x11font *xfont = (struct x11font *)font;\r
+    int sfid;\r
+    int shadowbold = FALSE;\r
+    int mult = (wide ? 2 : 1);\r
+\r
+    wide -= xfont->wide;\r
+    bold -= xfont->bold;\r
+\r
+    /*\r
+     * Decide which subfont we're using, and whether we have to\r
+     * use shadow bold.\r
+     */\r
+    if (xfont->shadowalways && bold) {\r
+       shadowbold = TRUE;\r
+       bold = 0;\r
+    }\r
+    sfid = 2 * wide + bold;\r
+    if (!xfont->allocated[sfid])\r
+       x11_alloc_subfont(xfont, sfid);\r
+    if (bold && !xfont->fonts[sfid]) {\r
+       bold = 0;\r
+       shadowbold = TRUE;\r
+       sfid = 2 * wide + bold;\r
+       if (!xfont->allocated[sfid])\r
+           x11_alloc_subfont(xfont, sfid);\r
+    }\r
+\r
+    if (!xfont->fonts[sfid])\r
+       return;                        /* we've tried our best, but no luck */\r
+\r
+    if (xfont->sixteen_bit) {\r
+       /*\r
+        * This X font has 16-bit character indices, which means\r
+        * we expect our string to have been passed in UTF-8.\r
+        */\r
+       XChar2b *xcs;\r
+       wchar_t *wcs;\r
+       int nchars, maxchars, i;\r
+\r
+       /*\r
+        * Convert the input string to wide-character Unicode.\r
+        */\r
+       maxchars = 0;\r
+       for (i = 0; i < len; i++)\r
+           if ((unsigned char)string[i] <= 0x7F ||\r
+               (unsigned char)string[i] >= 0xC0)\r
+               maxchars++;\r
+       wcs = snewn(maxchars+1, wchar_t);\r
+       nchars = charset_to_unicode((char **)&string, &len, wcs, maxchars,\r
+                                   CS_UTF8, NULL, NULL, 0);\r
+       assert(nchars <= maxchars);\r
+       wcs[nchars] = L'\0';\r
+\r
+       xcs = snewn(nchars, XChar2b);\r
+       for (i = 0; i < nchars; i++) {\r
+           xcs[i].byte1 = wcs[i] >> 8;\r
+           xcs[i].byte2 = wcs[i];\r
+       }\r
+\r
+       x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y,\r
+                                (gchar *)xcs, 2, nchars,\r
+                                shadowbold, xfont->shadowoffset,\r
+                                xfont->variable, cellwidth * mult);\r
+       sfree(xcs);\r
+       sfree(wcs);\r
+    } else {\r
+       x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y,\r
+                                string, 1, len,\r
+                                shadowbold, xfont->shadowoffset,\r
+                                xfont->variable, cellwidth * mult);\r
+    }\r
+}\r
+\r
+static void x11font_enum_fonts(GtkWidget *widget,\r
+                              fontsel_add_entry callback, void *callback_ctx)\r
+{\r
+    char **fontnames;\r
+    char *tmp = NULL;\r
+    int nnames, i, max, tmpsize;\r
+\r
+    max = 32768;\r
+    while (1) {\r
+       fontnames = XListFonts(GDK_DISPLAY(), "*", max, &nnames);\r
+       if (nnames >= max) {\r
+           XFreeFontNames(fontnames);\r
+           max *= 2;\r
+       } else\r
+           break;\r
+    }\r
+\r
+    tmpsize = 0;\r
+\r
+    for (i = 0; i < nnames; i++) {\r
+       if (fontnames[i][0] == '-') {\r
+           /*\r
+            * Dismember an XLFD and convert it into the format\r
+            * we'll be using in the font selector.\r
+            */\r
+           char *components[14];\r
+           char *p, *font, *style, *stylekey, *charset;\r
+           int j, weightkey, slantkey, setwidthkey;\r
+           int thistmpsize, fontsize, flags;\r
+\r
+           thistmpsize = 4 * strlen(fontnames[i]) + 256;\r
+           if (tmpsize < thistmpsize) {\r
+               tmpsize = thistmpsize;\r
+               tmp = sresize(tmp, tmpsize, char);\r
+           }\r
+           strcpy(tmp, fontnames[i]);\r
+\r
+           p = tmp;\r
+           for (j = 0; j < 14; j++) {\r
+               if (*p)\r
+                   *p++ = '\0';\r
+               components[j] = p;\r
+               while (*p && *p != '-')\r
+                   p++;\r
+           }\r
+           *p++ = '\0';\r
+\r
+           /*\r
+            * Font name is made up of fields 0 and 1, in reverse\r
+            * order with parentheses. (This is what the GTK 1.2 X\r
+            * font selector does, and it seems to come out\r
+            * looking reasonably sensible.)\r
+            */\r
+           font = p;\r
+           p += 1 + sprintf(p, "%s (%s)", components[1], components[0]);\r
+\r
+           /*\r
+            * Charset is made up of fields 12 and 13.\r
+            */\r
+           charset = p;\r
+           p += 1 + sprintf(p, "%s-%s", components[12], components[13]);\r
+\r
+           /*\r
+            * Style is a mixture of quite a lot of the fields,\r
+            * with some strange formatting.\r
+            */\r
+           style = p;\r
+           p += sprintf(p, "%s", components[2][0] ? components[2] :\r
+                        "regular");\r
+           if (!g_strcasecmp(components[3], "i"))\r
+               p += sprintf(p, " italic");\r
+           else if (!g_strcasecmp(components[3], "o"))\r
+               p += sprintf(p, " oblique");\r
+           else if (!g_strcasecmp(components[3], "ri"))\r
+               p += sprintf(p, " reverse italic");\r
+           else if (!g_strcasecmp(components[3], "ro"))\r
+               p += sprintf(p, " reverse oblique");\r
+           else if (!g_strcasecmp(components[3], "ot"))\r
+               p += sprintf(p, " other-slant");\r
+           if (components[4][0] && g_strcasecmp(components[4], "normal"))\r
+               p += sprintf(p, " %s", components[4]);\r
+           if (!g_strcasecmp(components[10], "m"))\r
+               p += sprintf(p, " [M]");\r
+           if (!g_strcasecmp(components[10], "c"))\r
+               p += sprintf(p, " [C]");\r
+           if (components[5][0])\r
+               p += sprintf(p, " %s", components[5]);\r
+\r
+           /*\r
+            * Style key is the same stuff as above, but with a\r
+            * couple of transformations done on it to make it\r
+            * sort more sensibly.\r
+            */\r
+           p++;\r
+           stylekey = p;\r
+           if (!g_strcasecmp(components[2], "medium") ||\r
+               !g_strcasecmp(components[2], "regular") ||\r
+               !g_strcasecmp(components[2], "normal") ||\r
+               !g_strcasecmp(components[2], "book"))\r
+               weightkey = 0;\r
+           else if (!g_strncasecmp(components[2], "demi", 4) ||\r
+                    !g_strncasecmp(components[2], "semi", 4))\r
+               weightkey = 1;\r
+           else\r
+               weightkey = 2;\r
+           if (!g_strcasecmp(components[3], "r"))\r
+               slantkey = 0;\r
+           else if (!g_strncasecmp(components[3], "r", 1))\r
+               slantkey = 2;\r
+           else\r
+               slantkey = 1;\r
+           if (!g_strcasecmp(components[4], "normal"))\r
+               setwidthkey = 0;\r
+           else\r
+               setwidthkey = 1;\r
+\r
+           p += sprintf(p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s",\r
+                        weightkey,\r
+                        (int)strlen(components[2]), components[2],\r
+                        slantkey,\r
+                        (int)strlen(components[3]), components[3],\r
+                        setwidthkey,\r
+                        (int)strlen(components[4]), components[4],\r
+                        (int)strlen(components[10]), components[10],\r
+                        (int)strlen(components[5]), components[5]);\r
+\r
+           assert(p - tmp < thistmpsize);\r
+\r
+           /*\r
+            * Size is in pixels, for our application, so we\r
+            * derive it directly from the pixel size field,\r
+            * number 6.\r
+            */\r
+           fontsize = atoi(components[6]);\r
+\r
+           /*\r
+            * Flags: we need to know whether this is a monospaced\r
+            * font, which we do by examining the spacing field\r
+            * again.\r
+            */\r
+           flags = FONTFLAG_SERVERSIDE;\r
+           if (!strchr("CcMm", components[10][0]))\r
+               flags |= FONTFLAG_NONMONOSPACED;\r
+\r
+           /*\r
+            * Not sure why, but sometimes the X server will\r
+            * deliver dummy font types in which fontsize comes\r
+            * out as zero. Filter those out.\r
+            */\r
+           if (fontsize)\r
+               callback(callback_ctx, fontnames[i], font, charset,\r
+                        style, stylekey, fontsize, flags, &x11font_vtable);\r
+       } else {\r
+           /*\r
+            * This isn't an XLFD, so it must be an alias.\r
+            * Transmit it with mostly null data.\r
+            * \r
+            * It would be nice to work out if it's monospaced\r
+            * here, but at the moment I can't see that being\r
+            * anything but computationally hideous. Ah well.\r
+            */\r
+           callback(callback_ctx, fontnames[i], fontnames[i], NULL,\r
+                    NULL, NULL, 0, FONTFLAG_SERVERALIAS, &x11font_vtable);\r
+       }\r
+    }\r
+    XFreeFontNames(fontnames);\r
+}\r
+\r
+static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,\r
+                                      int *size, int *flags,\r
+                                      int resolve_aliases)\r
+{\r
+    /*\r
+     * When given an X11 font name to try to make sense of for a\r
+     * font selector, we must attempt to load it (to see if it\r
+     * exists), and then canonify it by extracting its FONT\r
+     * property, which should give its full XLFD even if what we\r
+     * originally had was a wildcard.\r
+     * \r
+     * However, we must carefully avoid canonifying font\r
+     * _aliases_, unless specifically asked to, because the font\r
+     * selector treats them as worthwhile in their own right.\r
+     */\r
+    GdkFont *font = gdk_font_load(name);\r
+    XFontStruct *xfs;\r
+    Display *disp;\r
+    Atom fontprop, fontprop2;\r
+    unsigned long ret;\r
+\r
+    if (!font)\r
+       return NULL;                   /* didn't make sense to us, sorry */\r
+\r
+    gdk_font_ref(font);\r
+\r
+    xfs = GDK_FONT_XFONT(font);\r
+    disp = GDK_FONT_XDISPLAY(font);\r
+    fontprop = XInternAtom(disp, "FONT", False);\r
+\r
+    if (XGetFontProperty(xfs, fontprop, &ret)) {\r
+       char *newname = XGetAtomName(disp, (Atom)ret);\r
+       if (newname) {\r
+           unsigned long fsize = 12;\r
+\r
+           fontprop2 = XInternAtom(disp, "PIXEL_SIZE", False);\r
+           if (XGetFontProperty(xfs, fontprop2, &fsize) && fsize > 0) {\r
+               *size = fsize;\r
+               gdk_font_unref(font);\r
+               if (flags) {\r
+                   if (name[0] == '-' || resolve_aliases)\r
+                       *flags = FONTFLAG_SERVERSIDE;\r
+                   else\r
+                       *flags = FONTFLAG_SERVERALIAS;\r
+               }\r
+               return dupstr(name[0] == '-' || resolve_aliases ?\r
+                             newname : name);\r
+           }\r
+       }\r
+    }\r
+\r
+    gdk_font_unref(font);\r
+    return NULL;                      /* something went wrong */\r
+}\r
+\r
+static char *x11font_scale_fontname(GtkWidget *widget, const char *name,\r
+                                   int size)\r
+{\r
+    return NULL;                      /* shan't */\r
+}\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Pango font implementation (for GTK 2 only).\r
+ */\r
+\r
+#if defined PANGO_PRE_1POINT4 && !defined PANGO_PRE_1POINT6\r
+#define PANGO_PRE_1POINT6             /* make life easier for pre-1.4 folk */\r
+#endif\r
+\r
+static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,\r
+                               int x, int y, const char *string, int len,\r
+                               int wide, int bold, int cellwidth);\r
+static unifont *pangofont_create(GtkWidget *widget, const char *name,\r
+                                int wide, int bold,\r
+                                int shadowoffset, int shadowalways);\r
+static void pangofont_destroy(unifont *font);\r
+static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback,\r
+                                void *callback_ctx);\r
+static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,\r
+                                        int *size, int *flags,\r
+                                        int resolve_aliases);\r
+static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,\r
+                                     int size);\r
+\r
+struct pangofont {\r
+    struct unifont u;\r
+    /*\r
+     * Pango objects.\r
+     */\r
+    PangoFontDescription *desc;\r
+    PangoFontset *fset;\r
+    /*\r
+     * The containing widget.\r
+     */\r
+    GtkWidget *widget;\r
+    /*\r
+     * Data passed in to unifont_create().\r
+     */\r
+    int bold, shadowoffset, shadowalways;\r
+};\r
+\r
+static const struct unifont_vtable pangofont_vtable = {\r
+    pangofont_create,\r
+    pangofont_destroy,\r
+    pangofont_draw_text,\r
+    pangofont_enum_fonts,\r
+    pangofont_canonify_fontname,\r
+    pangofont_scale_fontname,\r
+    "client",\r
+};\r
+\r
+/*\r
+ * This function is used to rigorously validate a\r
+ * PangoFontDescription. Later versions of Pango have a nasty\r
+ * habit of accepting _any_ old string as input to\r
+ * pango_font_description_from_string and returning a font\r
+ * description which can actually be used to display text, even if\r
+ * they have to do it by falling back to their most default font.\r
+ * This is doubtless helpful in some situations, but not here,\r
+ * because we need to know if a Pango font string actually _makes\r
+ * sense_ in order to fall back to treating it as an X font name\r
+ * if it doesn't. So we check that the font family is actually one\r
+ * supported by Pango.\r
+ */\r
+static int pangofont_check_desc_makes_sense(PangoContext *ctx,\r
+                                           PangoFontDescription *desc)\r
+{\r
+#ifndef PANGO_PRE_1POINT6\r
+    PangoFontMap *map;\r
+#endif\r
+    PangoFontFamily **families;\r
+    int i, nfamilies, matched;\r
+\r
+    /*\r
+     * Ask Pango for a list of font families, and iterate through\r
+     * them to see if one of them matches the family in the\r
+     * PangoFontDescription.\r
+     */\r
+#ifndef PANGO_PRE_1POINT6\r
+    map = pango_context_get_font_map(ctx);\r
+    if (!map)\r
+       return FALSE;\r
+    pango_font_map_list_families(map, &families, &nfamilies);\r
+#else\r
+    pango_context_list_families(ctx, &families, &nfamilies);\r
+#endif\r
+\r
+    matched = FALSE;\r
+    for (i = 0; i < nfamilies; i++) {\r
+       if (!g_strcasecmp(pango_font_family_get_name(families[i]),\r
+                         pango_font_description_get_family(desc))) {\r
+           matched = TRUE;\r
+           break;\r
+       }\r
+    }\r
+    g_free(families);\r
+\r
+    return matched;\r
+}\r
+\r
+static unifont *pangofont_create(GtkWidget *widget, const char *name,\r
+                                int wide, int bold,\r
+                                int shadowoffset, int shadowalways)\r
+{\r
+    struct pangofont *pfont;\r
+    PangoContext *ctx;\r
+#ifndef PANGO_PRE_1POINT6\r
+    PangoFontMap *map;\r
+#endif\r
+    PangoFontDescription *desc;\r
+    PangoFontset *fset;\r
+    PangoFontMetrics *metrics;\r
+\r
+    desc = pango_font_description_from_string(name);\r
+    if (!desc)\r
+       return NULL;\r
+    ctx = gtk_widget_get_pango_context(widget);\r
+    if (!ctx) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+    if (!pangofont_check_desc_makes_sense(ctx, desc)) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+#ifndef PANGO_PRE_1POINT6\r
+    map = pango_context_get_font_map(ctx);\r
+    if (!map) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+    fset = pango_font_map_load_fontset(map, ctx, desc,\r
+                                      pango_context_get_language(ctx));\r
+#else\r
+    fset = pango_context_load_fontset(ctx, desc,\r
+                                      pango_context_get_language(ctx));\r
+#endif\r
+    if (!fset) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+    metrics = pango_fontset_get_metrics(fset);\r
+    if (!metrics ||\r
+       pango_font_metrics_get_approximate_digit_width(metrics) == 0) {\r
+       pango_font_description_free(desc);\r
+       g_object_unref(fset);\r
+       return NULL;\r
+    }\r
+\r
+    pfont = snew(struct pangofont);\r
+    pfont->u.vt = &pangofont_vtable;\r
+    pfont->u.width =\r
+       PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics));\r
+    pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));\r
+    pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));\r
+    pfont->u.height = pfont->u.ascent + pfont->u.descent;\r
+    /* The Pango API is hardwired to UTF-8 */\r
+    pfont->u.public_charset = CS_UTF8;\r
+    pfont->u.real_charset = CS_UTF8;\r
+    pfont->desc = desc;\r
+    pfont->fset = fset;\r
+    pfont->widget = widget;\r
+    pfont->bold = bold;\r
+    pfont->shadowoffset = shadowoffset;\r
+    pfont->shadowalways = shadowalways;\r
+\r
+    pango_font_metrics_unref(metrics);\r
+\r
+    return (unifont *)pfont;\r
+}\r
+\r
+static void pangofont_destroy(unifont *font)\r
+{\r
+    struct pangofont *pfont = (struct pangofont *)font;\r
+    pango_font_description_free(pfont->desc);\r
+    g_object_unref(pfont->fset);\r
+    sfree(font);\r
+}\r
+\r
+static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,\r
+                               int x, int y, const char *string, int len,\r
+                               int wide, int bold, int cellwidth)\r
+{\r
+    struct pangofont *pfont = (struct pangofont *)font;\r
+    PangoLayout *layout;\r
+    PangoRectangle rect;\r
+    int shadowbold = FALSE;\r
+\r
+    if (wide)\r
+       cellwidth *= 2;\r
+\r
+    y -= pfont->u.ascent;\r
+\r
+    layout = pango_layout_new(gtk_widget_get_pango_context(pfont->widget));\r
+    pango_layout_set_font_description(layout, pfont->desc);\r
+    if (bold > pfont->bold) {\r
+       if (pfont->shadowalways)\r
+           shadowbold = TRUE;\r
+       else {\r
+           PangoFontDescription *desc2 =\r
+               pango_font_description_copy_static(pfont->desc);\r
+           pango_font_description_set_weight(desc2, PANGO_WEIGHT_BOLD);\r
+           pango_layout_set_font_description(layout, desc2);\r
+       }\r
+    }\r
+\r
+    while (len > 0) {\r
+       int clen, n;\r
+\r
+       /*\r
+        * We want to display every character from this string in\r
+        * the centre of its own character cell. In the worst case,\r
+        * this requires a separate text-drawing call for each\r
+        * character; but in the common case where the font is\r
+        * properly fixed-width, we can draw many characters in one\r
+        * go which is much faster.\r
+        *\r
+        * This still isn't really ideal. If you look at what\r
+        * happens in the X protocol as a result of all of this, you\r
+        * find - naturally enough - that each call to\r
+        * gdk_draw_layout() generates a separate set of X RENDER\r
+        * operations involving creating a picture, setting a clip\r
+        * rectangle, doing some drawing and undoing the whole lot.\r
+        * In an ideal world, we should _always_ be able to turn the\r
+        * contents of this loop into a single RenderCompositeGlyphs\r
+        * operation which internally specifies inter-character\r
+        * deltas to get the spacing right, which would give us full\r
+        * speed _even_ in the worst case of a non-fixed-width font.\r
+        * However, Pango's architecture and documentation are so\r
+        * unhelpful that I have no idea how if at all to persuade\r
+        * them to do that.\r
+        */\r
+\r
+       /*\r
+        * Start by extracting a single UTF-8 character from the\r
+        * string.\r
+        */\r
+       clen = 1;\r
+       while (clen < len &&\r
+              (unsigned char)string[clen] >= 0x80 &&\r
+              (unsigned char)string[clen] < 0xC0)\r
+           clen++;\r
+       n = 1;\r
+\r
+       /*\r
+        * See if that character has the width we expect.\r
+        */\r
+       pango_layout_set_text(layout, string, clen);\r
+       pango_layout_get_pixel_extents(layout, NULL, &rect);\r
+\r
+       if (rect.width == cellwidth) {\r
+           /*\r
+            * Try extracting more characters, for as long as they\r
+            * stay well-behaved.\r
+            */\r
+           while (clen < len) {\r
+               int oldclen = clen;\r
+               clen++;                /* skip UTF-8 introducer byte */\r
+               while (clen < len &&\r
+                      (unsigned char)string[clen] >= 0x80 &&\r
+                      (unsigned char)string[clen] < 0xC0)\r
+                   clen++;\r
+               n++;\r
+               pango_layout_set_text(layout, string, clen);\r
+               pango_layout_get_pixel_extents(layout, NULL, &rect);\r
+               if (rect.width != n * cellwidth) {\r
+                   clen = oldclen;\r
+                   n--;\r
+                   break;\r
+               }\r
+           }\r
+       }\r
+\r
+       pango_layout_set_text(layout, string, clen);\r
+       pango_layout_get_pixel_extents(layout, NULL, &rect);\r
+       gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2,\r
+                       y + (pfont->u.height - rect.height)/2, layout);\r
+       if (shadowbold)\r
+           gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset,\r
+                           y + (pfont->u.height - rect.height)/2, layout);\r
+\r
+       len -= clen;\r
+       string += clen;\r
+       x += n * cellwidth;\r
+    }\r
+\r
+    g_object_unref(layout);\r
+}\r
+\r
+/*\r
+ * Dummy size value to be used when converting a\r
+ * PangoFontDescription of a scalable font to a string for\r
+ * internal use.\r
+ */\r
+#define PANGO_DUMMY_SIZE 12\r
+\r
+static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback,\r
+                                void *callback_ctx)\r
+{\r
+    PangoContext *ctx;\r
+#ifndef PANGO_PRE_1POINT6\r
+    PangoFontMap *map;\r
+#endif\r
+    PangoFontFamily **families;\r
+    int i, nfamilies;\r
+\r
+    ctx = gtk_widget_get_pango_context(widget);\r
+    if (!ctx)\r
+       return;\r
+\r
+    /*\r
+     * Ask Pango for a list of font families, and iterate through\r
+     * them.\r
+     */\r
+#ifndef PANGO_PRE_1POINT6\r
+    map = pango_context_get_font_map(ctx);\r
+    if (!map)\r
+       return;\r
+    pango_font_map_list_families(map, &families, &nfamilies);\r
+#else\r
+    pango_context_list_families(ctx, &families, &nfamilies);\r
+#endif\r
+    for (i = 0; i < nfamilies; i++) {\r
+       PangoFontFamily *family = families[i];\r
+       const char *familyname;\r
+       int flags;\r
+       PangoFontFace **faces;\r
+       int j, nfaces;\r
+\r
+       /*\r
+        * Set up our flags for this font family, and get the name\r
+        * string.\r
+        */\r
+       flags = FONTFLAG_CLIENTSIDE;\r
+#ifndef PANGO_PRE_1POINT4\r
+        /*\r
+         * In very early versions of Pango, we can't tell\r
+         * monospaced fonts from non-monospaced.\r
+         */\r
+       if (!pango_font_family_is_monospace(family))\r
+           flags |= FONTFLAG_NONMONOSPACED;\r
+#endif\r
+       familyname = pango_font_family_get_name(family);\r
+\r
+       /*\r
+        * Go through the available font faces in this family.\r
+        */\r
+       pango_font_family_list_faces(family, &faces, &nfaces);\r
+       for (j = 0; j < nfaces; j++) {\r
+           PangoFontFace *face = faces[j];\r
+           PangoFontDescription *desc;\r
+           const char *facename;\r
+           int *sizes;\r
+           int k, nsizes, dummysize;\r
+\r
+           /*\r
+            * Get the face name string.\r
+            */\r
+           facename = pango_font_face_get_face_name(face);\r
+\r
+           /*\r
+            * Set up a font description with what we've got so\r
+            * far. We'll fill in the size field manually and then\r
+            * call pango_font_description_to_string() to give the\r
+            * full real name of the specific font.\r
+            */\r
+           desc = pango_font_face_describe(face);\r
+\r
+           /*\r
+            * See if this font has a list of specific sizes.\r
+            */\r
+#ifndef PANGO_PRE_1POINT4\r
+           pango_font_face_list_sizes(face, &sizes, &nsizes);\r
+#else\r
+            /*\r
+             * In early versions of Pango, that call wasn't\r
+             * supported; we just have to assume everything is\r
+             * scalable.\r
+             */\r
+            sizes = NULL;\r
+#endif\r
+           if (!sizes) {\r
+               /*\r
+                * Write a single entry with a dummy size.\r
+                */\r
+               dummysize = PANGO_DUMMY_SIZE * PANGO_SCALE;\r
+               sizes = &dummysize;\r
+               nsizes = 1;\r
+           }\r
+\r
+           /*\r
+            * If so, go through them one by one.\r
+            */\r
+           for (k = 0; k < nsizes; k++) {\r
+               char *fullname;\r
+               char stylekey[128];\r
+\r
+               pango_font_description_set_size(desc, sizes[k]);\r
+\r
+               fullname = pango_font_description_to_string(desc);\r
+\r
+               /*\r
+                * Construct the sorting key for font styles.\r
+                */\r
+               {\r
+                   char *p = stylekey;\r
+                   int n;\r
+\r
+                   n = pango_font_description_get_weight(desc);\r
+                   /* Weight: normal, then lighter, then bolder */\r
+                   if (n <= PANGO_WEIGHT_NORMAL)\r
+                       n = PANGO_WEIGHT_NORMAL - n;\r
+                   p += sprintf(p, "%4d", n);\r
+\r
+                   n = pango_font_description_get_style(desc);\r
+                   p += sprintf(p, " %2d", n);\r
+\r
+                   n = pango_font_description_get_stretch(desc);\r
+                   /* Stretch: closer to normal sorts earlier */\r
+                   n = 2 * abs(PANGO_STRETCH_NORMAL - n) +\r
+                       (n < PANGO_STRETCH_NORMAL);\r
+                   p += sprintf(p, " %2d", n);\r
+\r
+                   n = pango_font_description_get_variant(desc);\r
+                   p += sprintf(p, " %2d", n);\r
+                   \r
+               }\r
+\r
+               /*\r
+                * Got everything. Hand off to the callback.\r
+                * (The charset string is NULL, because only\r
+                * server-side X fonts use it.)\r
+                */\r
+               callback(callback_ctx, fullname, familyname, NULL, facename,\r
+                        stylekey,\r
+                        (sizes == &dummysize ? 0 : PANGO_PIXELS(sizes[k])),\r
+                        flags, &pangofont_vtable);\r
+\r
+               g_free(fullname);\r
+           }\r
+           if (sizes != &dummysize)\r
+               g_free(sizes);\r
+\r
+           pango_font_description_free(desc);\r
+       }\r
+       g_free(faces);\r
+    }\r
+    g_free(families);\r
+}\r
+\r
+static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,\r
+                                        int *size, int *flags,\r
+                                        int resolve_aliases)\r
+{\r
+    /*\r
+     * When given a Pango font name to try to make sense of for a\r
+     * font selector, we must normalise it to PANGO_DUMMY_SIZE and\r
+     * extract its original size (in pixels) into the `size' field.\r
+     */\r
+    PangoContext *ctx;\r
+#ifndef PANGO_PRE_1POINT6\r
+    PangoFontMap *map;\r
+#endif\r
+    PangoFontDescription *desc;\r
+    PangoFontset *fset;\r
+    PangoFontMetrics *metrics;\r
+    char *newname, *retname;\r
+\r
+    desc = pango_font_description_from_string(name);\r
+    if (!desc)\r
+       return NULL;\r
+    ctx = gtk_widget_get_pango_context(widget);\r
+    if (!ctx) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+    if (!pangofont_check_desc_makes_sense(ctx, desc)) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+#ifndef PANGO_PRE_1POINT6\r
+    map = pango_context_get_font_map(ctx);\r
+    if (!map) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+    fset = pango_font_map_load_fontset(map, ctx, desc,\r
+                                      pango_context_get_language(ctx));\r
+#else\r
+    fset = pango_context_load_fontset(ctx, desc,\r
+                                      pango_context_get_language(ctx));\r
+#endif\r
+    if (!fset) {\r
+       pango_font_description_free(desc);\r
+       return NULL;\r
+    }\r
+    metrics = pango_fontset_get_metrics(fset);\r
+    if (!metrics ||\r
+       pango_font_metrics_get_approximate_digit_width(metrics) == 0) {\r
+       pango_font_description_free(desc);\r
+       g_object_unref(fset);\r
+       return NULL;\r
+    }\r
+\r
+    *size = PANGO_PIXELS(pango_font_description_get_size(desc));\r
+    *flags = FONTFLAG_CLIENTSIDE;\r
+    pango_font_description_set_size(desc, PANGO_DUMMY_SIZE * PANGO_SCALE);\r
+    newname = pango_font_description_to_string(desc);\r
+    retname = dupstr(newname);\r
+    g_free(newname);\r
+\r
+    pango_font_metrics_unref(metrics);\r
+    pango_font_description_free(desc);\r
+    g_object_unref(fset);\r
+\r
+    return retname;\r
+}\r
+\r
+static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,\r
+                                     int size)\r
+{\r
+    PangoFontDescription *desc;\r
+    char *newname, *retname;\r
+\r
+    desc = pango_font_description_from_string(name);\r
+    if (!desc)\r
+       return NULL;\r
+    pango_font_description_set_size(desc, size * PANGO_SCALE);\r
+    newname = pango_font_description_to_string(desc);\r
+    retname = dupstr(newname);\r
+    g_free(newname);\r
+    pango_font_description_free(desc);\r
+\r
+    return retname;\r
+}\r
+\r
+#endif /* GTK_CHECK_VERSION(2,0,0) */\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Outermost functions which do the vtable dispatch.\r
+ */\r
+\r
+/*\r
+ * Complete list of font-type subclasses. Listed in preference\r
+ * order for unifont_create(). (That is, in the extremely unlikely\r
+ * event that the same font name is valid as both a Pango and an\r
+ * X11 font, it will be interpreted as the former in the absence\r
+ * of an explicit type-disambiguating prefix.)\r
+ */\r
+static const struct unifont_vtable *unifont_types[] = {\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    &pangofont_vtable,\r
+#endif\r
+    &x11font_vtable,\r
+};\r
+\r
+/*\r
+ * Function which takes a font name and processes the optional\r
+ * scheme prefix. Returns the tail of the font name suitable for\r
+ * passing to individual font scheme functions, and also provides\r
+ * a subrange of the unifont_types[] array above.\r
+ * \r
+ * The return values `start' and `end' denote a half-open interval\r
+ * in unifont_types[]; that is, the correct way to iterate over\r
+ * them is\r
+ * \r
+ *   for (i = start; i < end; i++) {...}\r
+ */\r
+static const char *unifont_do_prefix(const char *name, int *start, int *end)\r
+{\r
+    int colonpos = strcspn(name, ":");\r
+    int i;\r
+\r
+    if (name[colonpos]) {\r
+       /*\r
+        * There's a colon prefix on the font name. Use it to work\r
+        * out which subclass to use.\r
+        */\r
+       for (i = 0; i < lenof(unifont_types); i++) {\r
+           if (strlen(unifont_types[i]->prefix) == colonpos &&\r
+               !strncmp(unifont_types[i]->prefix, name, colonpos)) {\r
+               *start = i;\r
+               *end = i+1;\r
+               return name + colonpos + 1;\r
+           }\r
+       }\r
+       /*\r
+        * None matched, so return an empty scheme list to prevent\r
+        * any scheme from being called at all.\r
+        */\r
+       *start = *end = 0;\r
+       return name + colonpos + 1;\r
+    } else {\r
+       /*\r
+        * No colon prefix, so just use all the subclasses.\r
+        */\r
+       *start = 0;\r
+       *end = lenof(unifont_types);\r
+       return name;\r
+    }\r
+}\r
+\r
+unifont *unifont_create(GtkWidget *widget, const char *name, int wide,\r
+                       int bold, int shadowoffset, int shadowalways)\r
+{\r
+    int i, start, end;\r
+\r
+    name = unifont_do_prefix(name, &start, &end);\r
+\r
+    for (i = start; i < end; i++) {\r
+       unifont *ret = unifont_types[i]->create(widget, name, wide, bold,\r
+                                               shadowoffset, shadowalways);\r
+       if (ret)\r
+           return ret;\r
+    }\r
+    return NULL;                      /* font not found in any scheme */\r
+}\r
+\r
+void unifont_destroy(unifont *font)\r
+{\r
+    font->vt->destroy(font);\r
+}\r
+\r
+void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,\r
+                      int x, int y, const char *string, int len,\r
+                      int wide, int bold, int cellwidth)\r
+{\r
+    font->vt->draw_text(target, gc, font, x, y, string, len,\r
+                       wide, bold, cellwidth);\r
+}\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Implementation of a unified font selector. Used on GTK 2 only;\r
+ * for GTK 1 we still use the standard font selector.\r
+ */\r
+\r
+typedef struct fontinfo fontinfo;\r
+\r
+typedef struct unifontsel_internal {\r
+    /* This must be the structure's first element, for cross-casting */\r
+    unifontsel u;\r
+    GtkListStore *family_model, *style_model, *size_model;\r
+    GtkWidget *family_list, *style_list, *size_entry, *size_list;\r
+    GtkWidget *filter_buttons[4];\r
+    GtkWidget *preview_area;\r
+    GdkPixmap *preview_pixmap;\r
+    int preview_width, preview_height;\r
+    GdkColor preview_fg, preview_bg;\r
+    int filter_flags;\r
+    tree234 *fonts_by_realname, *fonts_by_selorder;\r
+    fontinfo *selected;\r
+    int selsize, intendedsize;\r
+    int inhibit_response;  /* inhibit callbacks when we change GUI controls */\r
+} unifontsel_internal;\r
+\r
+/*\r
+ * The structure held in the tree234s. All the string members are\r
+ * part of the same allocated area, so don't need freeing\r
+ * separately.\r
+ */\r
+struct fontinfo {\r
+    char *realname;\r
+    char *family, *charset, *style, *stylekey;\r
+    int size, flags;\r
+    /*\r
+     * Fallback sorting key, to permit multiple identical entries\r
+     * to exist in the selorder tree.\r
+     */\r
+    int index;\r
+    /*\r
+     * Indices mapping fontinfo structures to indices in the list\r
+     * boxes. sizeindex is irrelevant if the font is scalable\r
+     * (size==0).\r
+     */\r
+    int familyindex, styleindex, sizeindex;\r
+    /*\r
+     * The class of font.\r
+     */\r
+    const struct unifont_vtable *fontclass;\r
+};\r
+\r
+struct fontinfo_realname_find {\r
+    const char *realname;\r
+    int flags;\r
+};\r
+\r
+static int strnullcasecmp(const char *a, const char *b)\r
+{\r
+    int i;\r
+\r
+    /*\r
+     * If exactly one of the inputs is NULL, it compares before\r
+     * the other one.\r
+     */\r
+    if ((i = (!b) - (!a)) != 0)\r
+       return i;\r
+\r
+    /*\r
+     * NULL compares equal.\r
+     */\r
+    if (!a)\r
+       return 0;\r
+\r
+    /*\r
+     * Otherwise, ordinary strcasecmp.\r
+     */\r
+    return g_strcasecmp(a, b);\r
+}\r
+\r
+static int fontinfo_realname_compare(void *av, void *bv)\r
+{\r
+    fontinfo *a = (fontinfo *)av;\r
+    fontinfo *b = (fontinfo *)bv;\r
+    int i;\r
+\r
+    if ((i = strnullcasecmp(a->realname, b->realname)) != 0)\r
+       return i;\r
+    if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))\r
+       return ((a->flags & FONTFLAG_SORT_MASK) <\r
+               (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);\r
+    return 0;\r
+}\r
+\r
+static int fontinfo_realname_find(void *av, void *bv)\r
+{\r
+    struct fontinfo_realname_find *a = (struct fontinfo_realname_find *)av;\r
+    fontinfo *b = (fontinfo *)bv;\r
+    int i;\r
+\r
+    if ((i = strnullcasecmp(a->realname, b->realname)) != 0)\r
+       return i;\r
+    if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))\r
+       return ((a->flags & FONTFLAG_SORT_MASK) <\r
+               (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);\r
+    return 0;\r
+}\r
+\r
+static int fontinfo_selorder_compare(void *av, void *bv)\r
+{\r
+    fontinfo *a = (fontinfo *)av;\r
+    fontinfo *b = (fontinfo *)bv;\r
+    int i;\r
+    if ((i = strnullcasecmp(a->family, b->family)) != 0)\r
+       return i;\r
+    /*\r
+     * Font class comes immediately after family, so that fonts\r
+     * from different classes with the same family\r
+     */\r
+    if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))\r
+       return ((a->flags & FONTFLAG_SORT_MASK) <\r
+               (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);\r
+    if ((i = strnullcasecmp(a->charset, b->charset)) != 0)\r
+       return i;\r
+    if ((i = strnullcasecmp(a->stylekey, b->stylekey)) != 0)\r
+       return i;\r
+    if ((i = strnullcasecmp(a->style, b->style)) != 0)\r
+       return i;\r
+    if (a->size != b->size)\r
+       return (a->size < b->size ? -1 : +1);\r
+    if (a->index != b->index)\r
+       return (a->index < b->index ? -1 : +1);\r
+    return 0;\r
+}\r
+\r
+static void unifontsel_deselect(unifontsel_internal *fs)\r
+{\r
+    fs->selected = NULL;\r
+    gtk_list_store_clear(fs->style_model);\r
+    gtk_list_store_clear(fs->size_model);\r
+    gtk_widget_set_sensitive(fs->u.ok_button, FALSE);\r
+    gtk_widget_set_sensitive(fs->size_entry, FALSE);\r
+}\r
+\r
+static void unifontsel_setup_familylist(unifontsel_internal *fs)\r
+{\r
+    GtkTreeIter iter;\r
+    int i, listindex, minpos = -1, maxpos = -1;\r
+    char *currfamily = NULL;\r
+    int currflags = -1;\r
+    fontinfo *info;\r
+\r
+    gtk_list_store_clear(fs->family_model);\r
+    listindex = 0;\r
+\r
+    /*\r
+     * Search through the font tree for anything matching our\r
+     * current filter criteria. When we find one, add its font\r
+     * name to the list box.\r
+     */\r
+    for (i = 0 ;; i++) {\r
+       info = (fontinfo *)index234(fs->fonts_by_selorder, i);\r
+       /*\r
+        * info may be NULL if we've just run off the end of the\r
+        * tree. We must still do a processing pass in that\r
+        * situation, in case we had an unfinished font record in\r
+        * progress.\r
+        */\r
+       if (info && (info->flags &~ fs->filter_flags)) {\r
+           info->familyindex = -1;\r
+           continue;                  /* we're filtering out this font */\r
+       }\r
+       if (!info || strnullcasecmp(currfamily, info->family) ||\r
+           currflags != (info->flags & FONTFLAG_SORT_MASK)) {\r
+           /*\r
+            * We've either finished a family, or started a new\r
+            * one, or both.\r
+            */\r
+           if (currfamily) {\r
+               gtk_list_store_append(fs->family_model, &iter);\r
+               gtk_list_store_set(fs->family_model, &iter,\r
+                                  0, currfamily, 1, minpos, 2, maxpos+1, -1);\r
+               listindex++;\r
+           }\r
+           if (info) {\r
+               minpos = i;\r
+               currfamily = info->family;\r
+               currflags = info->flags & FONTFLAG_SORT_MASK;\r
+           }\r
+       }\r
+       if (!info)\r
+           break;                     /* now we're done */\r
+       info->familyindex = listindex;\r
+       maxpos = i;\r
+    }\r
+\r
+    /*\r
+     * If we've just filtered out the previously selected font,\r
+     * deselect it thoroughly.\r
+     */\r
+    if (fs->selected && fs->selected->familyindex < 0)\r
+       unifontsel_deselect(fs);\r
+}\r
+\r
+static void unifontsel_setup_stylelist(unifontsel_internal *fs,\r
+                                      int start, int end)\r
+{\r
+    GtkTreeIter iter;\r
+    int i, listindex, minpos = -1, maxpos = -1, started = FALSE;\r
+    char *currcs = NULL, *currstyle = NULL;\r
+    fontinfo *info;\r
+\r
+    gtk_list_store_clear(fs->style_model);\r
+    listindex = 0;\r
+    started = FALSE;\r
+\r
+    /*\r
+     * Search through the font tree for anything matching our\r
+     * current filter criteria. When we find one, add its charset\r
+     * and/or style name to the list box.\r
+     */\r
+    for (i = start; i <= end; i++) {\r
+       if (i == end)\r
+           info = NULL;\r
+       else\r
+           info = (fontinfo *)index234(fs->fonts_by_selorder, i);\r
+       /*\r
+        * info may be NULL if we've just run off the end of the\r
+        * relevant data. We must still do a processing pass in\r
+        * that situation, in case we had an unfinished font\r
+        * record in progress.\r
+        */\r
+       if (info && (info->flags &~ fs->filter_flags)) {\r
+           info->styleindex = -1;\r
+           continue;                  /* we're filtering out this font */\r
+       }\r
+       if (!info || !started || strnullcasecmp(currcs, info->charset) ||\r
+            strnullcasecmp(currstyle, info->style)) {\r
+           /*\r
+            * We've either finished a style/charset, or started a\r
+            * new one, or both.\r
+            */\r
+           started = TRUE;\r
+           if (currstyle) {\r
+               gtk_list_store_append(fs->style_model, &iter);\r
+               gtk_list_store_set(fs->style_model, &iter,\r
+                                  0, currstyle, 1, minpos, 2, maxpos+1,\r
+                                  3, TRUE, -1);\r
+               listindex++;\r
+           }\r
+           if (info) {\r
+               minpos = i;\r
+               if (info->charset && strnullcasecmp(currcs, info->charset)) {\r
+                   gtk_list_store_append(fs->style_model, &iter);\r
+                   gtk_list_store_set(fs->style_model, &iter,\r
+                                      0, info->charset, 1, -1, 2, -1,\r
+                                      3, FALSE, -1);\r
+                   listindex++;\r
+               }\r
+               currcs = info->charset;\r
+               currstyle = info->style;\r
+           }\r
+       }\r
+       if (!info)\r
+           break;                     /* now we're done */\r
+       info->styleindex = listindex;\r
+       maxpos = i;\r
+    }\r
+}\r
+\r
+static const int unifontsel_default_sizes[] = { 10, 12, 14, 16, 20, 24, 32 };\r
+\r
+static void unifontsel_setup_sizelist(unifontsel_internal *fs,\r
+                                     int start, int end)\r
+{\r
+    GtkTreeIter iter;\r
+    int i, listindex;\r
+    char sizetext[40];\r
+    fontinfo *info;\r
+\r
+    gtk_list_store_clear(fs->size_model);\r
+    listindex = 0;\r
+\r
+    /*\r
+     * Search through the font tree for anything matching our\r
+     * current filter criteria. When we find one, add its font\r
+     * name to the list box.\r
+     */\r
+    for (i = start; i < end; i++) {\r
+       info = (fontinfo *)index234(fs->fonts_by_selorder, i);\r
+       if (info->flags &~ fs->filter_flags) {\r
+           info->sizeindex = -1;\r
+           continue;                  /* we're filtering out this font */\r
+       }\r
+       if (info->size) {\r
+           sprintf(sizetext, "%d", info->size);\r
+           info->sizeindex = listindex;\r
+           gtk_list_store_append(fs->size_model, &iter);\r
+           gtk_list_store_set(fs->size_model, &iter,\r
+                              0, sizetext, 1, i, 2, info->size, -1);\r
+           listindex++;\r
+       } else {\r
+           int j;\r
+\r
+           assert(i == start);\r
+           assert(i+1 == end);\r
+\r
+           for (j = 0; j < lenof(unifontsel_default_sizes); j++) {\r
+               sprintf(sizetext, "%d", unifontsel_default_sizes[j]);\r
+               gtk_list_store_append(fs->size_model, &iter);\r
+               gtk_list_store_set(fs->size_model, &iter, 0, sizetext, 1, i,\r
+                                  2, unifontsel_default_sizes[j], -1);\r
+               listindex++;\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+static void unifontsel_set_filter_buttons(unifontsel_internal *fs)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < lenof(fs->filter_buttons); i++) {\r
+       int flagbit = GPOINTER_TO_INT(gtk_object_get_data\r
+                                     (GTK_OBJECT(fs->filter_buttons[i]),\r
+                                      "user-data"));\r
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fs->filter_buttons[i]),\r
+                                    !!(fs->filter_flags & flagbit));\r
+    }\r
+}\r
+\r
+static void unifontsel_draw_preview_text(unifontsel_internal *fs)\r
+{\r
+    unifont *font;\r
+    char *sizename = NULL;\r
+    fontinfo *info = fs->selected;\r
+\r
+    if (info) {\r
+       sizename = info->fontclass->scale_fontname\r
+           (GTK_WIDGET(fs->u.window), info->realname, fs->selsize);\r
+       font = info->fontclass->create(GTK_WIDGET(fs->u.window),\r
+                                      sizename ? sizename : info->realname,\r
+                                      FALSE, FALSE, 0, 0);\r
+    } else\r
+       font = NULL;\r
+\r
+    if (fs->preview_pixmap) {\r
+       GdkGC *gc = gdk_gc_new(fs->preview_pixmap);\r
+       gdk_gc_set_foreground(gc, &fs->preview_bg);\r
+       gdk_draw_rectangle(fs->preview_pixmap, gc, 1, 0, 0,\r
+                          fs->preview_width, fs->preview_height);\r
+       gdk_gc_set_foreground(gc, &fs->preview_fg);\r
+       if (font) {\r
+           /*\r
+            * The pangram used here is rather carefully\r
+            * constructed: it contains a sequence of very narrow\r
+            * letters (`jil') and a pair of adjacent very wide\r
+            * letters (`wm').\r
+            *\r
+            * If the user selects a proportional font, it will be\r
+            * coerced into fixed-width character cells when used\r
+            * in the actual terminal window. We therefore display\r
+            * it the same way in the preview pane, so as to show\r
+            * it the way it will actually be displayed - and we\r
+            * deliberately pick a pangram which will show the\r
+            * resulting miskerning at its worst.\r
+            *\r
+            * We aren't trying to sell people these fonts; we're\r
+            * trying to let them make an informed choice. Better\r
+            * that they find out the problems with using\r
+            * proportional fonts in terminal windows here than\r
+            * that they go to the effort of selecting their font\r
+            * and _then_ realise it was a mistake.\r
+            */\r
+           info->fontclass->draw_text(fs->preview_pixmap, gc, font,\r
+                                      0, font->ascent,\r
+                                      "bankrupt jilted showmen quiz convex fogey",\r
+                                      41, FALSE, FALSE, font->width);\r
+           info->fontclass->draw_text(fs->preview_pixmap, gc, font,\r
+                                      0, font->ascent + font->height,\r
+                                      "BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY",\r
+                                      41, FALSE, FALSE, font->width);\r
+           /*\r
+            * The ordering of punctuation here is also selected\r
+            * with some specific aims in mind. I put ` and '\r
+            * together because some software (and people) still\r
+            * use them as matched quotes no matter what Unicode\r
+            * might say on the matter, so people can quickly\r
+            * check whether they look silly in a candidate font.\r
+            * The sequence #_@ is there to let people judge the\r
+            * suitability of the underscore as an effectively\r
+            * alphabetic character (since that's how it's often\r
+            * used in practice, at least by programmers).\r
+            */\r
+           info->fontclass->draw_text(fs->preview_pixmap, gc, font,\r
+                                      0, font->ascent + font->height * 2,\r
+                                      "0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$",\r
+                                      42, FALSE, FALSE, font->width);\r
+       }\r
+       gdk_gc_unref(gc);\r
+       gdk_window_invalidate_rect(fs->preview_area->window, NULL, FALSE);\r
+    }\r
+    if (font)\r
+       info->fontclass->destroy(font);\r
+\r
+    sfree(sizename);\r
+}\r
+\r
+static void unifontsel_select_font(unifontsel_internal *fs,\r
+                                  fontinfo *info, int size, int leftlist,\r
+                                  int size_is_explicit)\r
+{\r
+    int index;\r
+    int minval, maxval;\r
+    GtkTreePath *treepath;\r
+    GtkTreeIter iter;\r
+\r
+    fs->inhibit_response = TRUE;\r
+\r
+    fs->selected = info;\r
+    fs->selsize = size;\r
+    if (size_is_explicit)\r
+       fs->intendedsize = size;\r
+\r
+    gtk_widget_set_sensitive(fs->u.ok_button, TRUE);\r
+\r
+    /*\r
+     * Find the index of this fontinfo in the selorder list. \r
+     */\r
+    index = -1;\r
+    findpos234(fs->fonts_by_selorder, info, NULL, &index);\r
+    assert(index >= 0);\r
+\r
+    /*\r
+     * Adjust the font selector flags and redo the font family\r
+     * list box, if necessary.\r
+     */\r
+    if (leftlist <= 0 &&\r
+       (fs->filter_flags | info->flags) != fs->filter_flags) {\r
+       fs->filter_flags |= info->flags;\r
+       unifontsel_set_filter_buttons(fs);\r
+       unifontsel_setup_familylist(fs);\r
+    }\r
+\r
+    /*\r
+     * Find the appropriate family name and select it in the list.\r
+     */\r
+    assert(info->familyindex >= 0);\r
+    treepath = gtk_tree_path_new_from_indices(info->familyindex, -1);\r
+    gtk_tree_selection_select_path\r
+       (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->family_list)),\r
+        treepath);\r
+    gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->family_list),\r
+                                treepath, NULL, FALSE, 0.0, 0.0);\r
+    gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, treepath);\r
+    gtk_tree_path_free(treepath);\r
+\r
+    /*\r
+     * Now set up the font style list.\r
+     */\r
+    gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter,\r
+                      1, &minval, 2, &maxval, -1);\r
+    if (leftlist <= 1)\r
+       unifontsel_setup_stylelist(fs, minval, maxval);\r
+\r
+    /*\r
+     * Find the appropriate style name and select it in the list.\r
+     */\r
+    if (info->style) {\r
+       assert(info->styleindex >= 0);\r
+       treepath = gtk_tree_path_new_from_indices(info->styleindex, -1);\r
+       gtk_tree_selection_select_path\r
+           (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->style_list)),\r
+            treepath);\r
+       gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->style_list),\r
+                                    treepath, NULL, FALSE, 0.0, 0.0);\r
+       gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->style_model),\r
+                               &iter, treepath);\r
+       gtk_tree_path_free(treepath);\r
+\r
+       /*\r
+        * And set up the size list.\r
+        */\r
+       gtk_tree_model_get(GTK_TREE_MODEL(fs->style_model), &iter,\r
+                          1, &minval, 2, &maxval, -1);\r
+       if (leftlist <= 2)\r
+           unifontsel_setup_sizelist(fs, minval, maxval);\r
+\r
+       /*\r
+        * Find the appropriate size, and select it in the list.\r
+        */\r
+       if (info->size) {\r
+           assert(info->sizeindex >= 0);\r
+           treepath = gtk_tree_path_new_from_indices(info->sizeindex, -1);\r
+           gtk_tree_selection_select_path\r
+               (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->size_list)),\r
+                treepath);\r
+           gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list),\r
+                                        treepath, NULL, FALSE, 0.0, 0.0);\r
+           gtk_tree_path_free(treepath);\r
+           size = info->size;\r
+       } else {\r
+           int j;\r
+           for (j = 0; j < lenof(unifontsel_default_sizes); j++)\r
+               if (unifontsel_default_sizes[j] == size) {\r
+                   treepath = gtk_tree_path_new_from_indices(j, -1);\r
+                   gtk_tree_view_set_cursor(GTK_TREE_VIEW(fs->size_list),\r
+                                            treepath, NULL, FALSE);\r
+                   gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list),\r
+                                                treepath, NULL, FALSE, 0.0,\r
+                                                0.0);\r
+                   gtk_tree_path_free(treepath);\r
+               }\r
+       }\r
+\r
+       /*\r
+        * And set up the font size text entry box.\r
+        */\r
+       {\r
+           char sizetext[40];\r
+           sprintf(sizetext, "%d", size);\r
+           gtk_entry_set_text(GTK_ENTRY(fs->size_entry), sizetext);\r
+       }\r
+    } else {\r
+       if (leftlist <= 2)\r
+           unifontsel_setup_sizelist(fs, 0, 0);\r
+       gtk_entry_set_text(GTK_ENTRY(fs->size_entry), "");\r
+    }\r
+\r
+    /*\r
+     * Grey out the font size edit box if we're not using a\r
+     * scalable font.\r
+     */\r
+    gtk_entry_set_editable(GTK_ENTRY(fs->size_entry), fs->selected->size == 0);\r
+    gtk_widget_set_sensitive(fs->size_entry, fs->selected->size == 0);\r
+\r
+    unifontsel_draw_preview_text(fs);\r
+\r
+    fs->inhibit_response = FALSE;\r
+}\r
+\r
+static void unifontsel_button_toggled(GtkToggleButton *tb, gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+    int newstate = gtk_toggle_button_get_active(tb);\r
+    int newflags;\r
+    int flagbit = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(tb),\r
+                                                     "user-data"));\r
+\r
+    if (newstate)\r
+       newflags = fs->filter_flags | flagbit;\r
+    else\r
+       newflags = fs->filter_flags & ~flagbit;\r
+\r
+    if (fs->filter_flags != newflags) {\r
+       fs->filter_flags = newflags;\r
+       unifontsel_setup_familylist(fs);\r
+    }\r
+}\r
+\r
+static void unifontsel_add_entry(void *ctx, const char *realfontname,\r
+                                const char *family, const char *charset,\r
+                                const char *style, const char *stylekey,\r
+                                int size, int flags,\r
+                                const struct unifont_vtable *fontclass)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)ctx;\r
+    fontinfo *info;\r
+    int totalsize;\r
+    char *p;\r
+\r
+    totalsize = sizeof(fontinfo) + strlen(realfontname) +\r
+       (family ? strlen(family) : 0) + (charset ? strlen(charset) : 0) +\r
+       (style ? strlen(style) : 0) + (stylekey ? strlen(stylekey) : 0) + 10;\r
+    info = (fontinfo *)smalloc(totalsize);\r
+    info->fontclass = fontclass;\r
+    p = (char *)info + sizeof(fontinfo);\r
+    info->realname = p;\r
+    strcpy(p, realfontname);\r
+    p += 1+strlen(p);\r
+    if (family) {\r
+       info->family = p;\r
+       strcpy(p, family);\r
+       p += 1+strlen(p);\r
+    } else\r
+       info->family = NULL;\r
+    if (charset) {\r
+       info->charset = p;\r
+       strcpy(p, charset);\r
+       p += 1+strlen(p);\r
+    } else\r
+       info->charset = NULL;\r
+    if (style) {\r
+       info->style = p;\r
+       strcpy(p, style);\r
+       p += 1+strlen(p);\r
+    } else\r
+       info->style = NULL;\r
+    if (stylekey) {\r
+       info->stylekey = p;\r
+       strcpy(p, stylekey);\r
+       p += 1+strlen(p);\r
+    } else\r
+       info->stylekey = NULL;\r
+    assert(p - (char *)info <= totalsize);\r
+    info->size = size;\r
+    info->flags = flags;\r
+    info->index = count234(fs->fonts_by_selorder);\r
+\r
+    /*\r
+     * It's just conceivable that a misbehaving font enumerator\r
+     * might tell us about the same font real name more than once,\r
+     * in which case we should silently drop the new one.\r
+     */\r
+    if (add234(fs->fonts_by_realname, info) != info) {\r
+       sfree(info);\r
+       return;\r
+    }\r
+    /*\r
+     * However, we should never get a duplicate key in the\r
+     * selorder tree, because the index field carefully\r
+     * disambiguates otherwise identical records.\r
+     */\r
+    add234(fs->fonts_by_selorder, info);\r
+}\r
+\r
+static fontinfo *update_for_intended_size(unifontsel_internal *fs,\r
+                                         fontinfo *info)\r
+{\r
+    fontinfo info2, *below, *above;\r
+    int pos;\r
+\r
+    /*\r
+     * Copy the info structure. This doesn't copy its dynamic\r
+     * string fields, but that's unimportant because all we're\r
+     * going to do is to adjust the size field and use it in one\r
+     * tree search.\r
+     */\r
+    info2 = *info;\r
+    info2.size = fs->intendedsize;\r
+\r
+    /*\r
+     * Search in the tree to find the fontinfo structure which\r
+     * best approximates the size the user last requested.\r
+     */\r
+    below = findrelpos234(fs->fonts_by_selorder, &info2, NULL,\r
+                         REL234_LE, &pos);\r
+    above = index234(fs->fonts_by_selorder, pos+1);\r
+\r
+    /*\r
+     * See if we've found it exactly, which is an easy special\r
+     * case. If we have, it'll be in `below' and not `above',\r
+     * because we did a REL234_LE rather than REL234_LT search.\r
+     */\r
+    if (!fontinfo_selorder_compare(&info2, below))\r
+       return below;\r
+\r
+    /*\r
+     * Now we've either found two suitable fonts, one smaller and\r
+     * one larger, or we're at one or other extreme end of the\r
+     * scale. Find out which, by NULLing out either of below and\r
+     * above if it differs from this one in any respect but size\r
+     * (and the disambiguating index field). Bear in mind, also,\r
+     * that either one might _already_ be NULL if we're at the\r
+     * extreme ends of the font list.\r
+     */\r
+    if (below) {\r
+       info2.size = below->size;\r
+       info2.index = below->index;\r
+       if (fontinfo_selorder_compare(&info2, below))\r
+           below = NULL;\r
+    }\r
+    if (above) {\r
+       info2.size = above->size;\r
+       info2.index = above->index;\r
+       if (fontinfo_selorder_compare(&info2, above))\r
+           above = NULL;\r
+    }\r
+\r
+    /*\r
+     * Now return whichever of above and below is non-NULL, if\r
+     * that's unambiguous.\r
+     */\r
+    if (!above)\r
+       return below;\r
+    if (!below)\r
+       return above;\r
+\r
+    /*\r
+     * And now we really do have to make a choice about whether to\r
+     * round up or down. We'll do it by rounding to nearest,\r
+     * breaking ties by rounding up.\r
+     */\r
+    if (above->size - fs->intendedsize <= fs->intendedsize - below->size)\r
+       return above;\r
+    else\r
+       return below;\r
+}\r
+\r
+static void family_changed(GtkTreeSelection *treeselection, gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+    GtkTreeModel *treemodel;\r
+    GtkTreeIter treeiter;\r
+    int minval;\r
+    fontinfo *info;\r
+\r
+    if (fs->inhibit_response)         /* we made this change ourselves */\r
+       return;\r
+\r
+    if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))\r
+       return;\r
+\r
+    gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1);\r
+    info = (fontinfo *)index234(fs->fonts_by_selorder, minval);\r
+    info = update_for_intended_size(fs, info);\r
+    if (!info)\r
+       return; /* _shouldn't_ happen unless font list is completely funted */\r
+    if (!info->size)\r
+       fs->selsize = fs->intendedsize;   /* font is scalable */\r
+    unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,\r
+                          1, FALSE);\r
+}\r
+\r
+static void style_changed(GtkTreeSelection *treeselection, gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+    GtkTreeModel *treemodel;\r
+    GtkTreeIter treeiter;\r
+    int minval;\r
+    fontinfo *info;\r
+\r
+    if (fs->inhibit_response)         /* we made this change ourselves */\r
+       return;\r
+\r
+    if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))\r
+       return;\r
+\r
+    gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1);\r
+    if (minval < 0)\r
+        return;                    /* somehow a charset heading got clicked */\r
+    info = (fontinfo *)index234(fs->fonts_by_selorder, minval);\r
+    info = update_for_intended_size(fs, info);\r
+    if (!info)\r
+       return; /* _shouldn't_ happen unless font list is completely funted */\r
+    if (!info->size)\r
+       fs->selsize = fs->intendedsize;   /* font is scalable */\r
+    unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,\r
+                          2, FALSE);\r
+}\r
+\r
+static void size_changed(GtkTreeSelection *treeselection, gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+    GtkTreeModel *treemodel;\r
+    GtkTreeIter treeiter;\r
+    int minval, size;\r
+    fontinfo *info;\r
+\r
+    if (fs->inhibit_response)         /* we made this change ourselves */\r
+       return;\r
+\r
+    if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))\r
+       return;\r
+\r
+    gtk_tree_model_get(treemodel, &treeiter, 1, &minval, 2, &size, -1);\r
+    info = (fontinfo *)index234(fs->fonts_by_selorder, minval);\r
+    unifontsel_select_font(fs, info, info->size ? info->size : size, 3, TRUE);\r
+}\r
+\r
+static void size_entry_changed(GtkEditable *ed, gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+    const char *text;\r
+    int size;\r
+\r
+    if (fs->inhibit_response)         /* we made this change ourselves */\r
+       return;\r
+\r
+    text = gtk_entry_get_text(GTK_ENTRY(ed));\r
+    size = atoi(text);\r
+\r
+    if (size > 0) {\r
+       assert(fs->selected->size == 0);\r
+       unifontsel_select_font(fs, fs->selected, size, 3, TRUE);\r
+    }\r
+}\r
+\r
+static void alias_resolve(GtkTreeView *treeview, GtkTreePath *path,\r
+                         GtkTreeViewColumn *column, gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+    GtkTreeIter iter;\r
+    int minval, newsize;\r
+    fontinfo *info, *newinfo;\r
+    char *newname;\r
+\r
+    if (fs->inhibit_response)         /* we made this change ourselves */\r
+       return;\r
+\r
+    gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, path);\r
+    gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter, 1,&minval, -1);\r
+    info = (fontinfo *)index234(fs->fonts_by_selorder, minval);\r
+    if (info) {\r
+       int flags;\r
+       struct fontinfo_realname_find f;\r
+\r
+       newname = info->fontclass->canonify_fontname\r
+           (GTK_WIDGET(fs->u.window), info->realname, &newsize, &flags, TRUE);\r
+\r
+       f.realname = newname;\r
+       f.flags = flags;\r
+       newinfo = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);\r
+\r
+       sfree(newname);\r
+       if (!newinfo)\r
+           return;                    /* font name not in our index */\r
+       if (newinfo == info)\r
+           return;   /* didn't change under canonification => not an alias */\r
+       unifontsel_select_font(fs, newinfo,\r
+                              newinfo->size ? newinfo->size : newsize,\r
+                              1, TRUE);\r
+    }\r
+}\r
+\r
+static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,\r
+                                  gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+\r
+    if (fs->preview_pixmap) {\r
+       gdk_draw_pixmap(widget->window,\r
+                       widget->style->fg_gc[GTK_WIDGET_STATE(widget)],\r
+                       fs->preview_pixmap,\r
+                       event->area.x, event->area.y,\r
+                       event->area.x, event->area.y,\r
+                       event->area.width, event->area.height);\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+static gint unifontsel_configure_area(GtkWidget *widget,\r
+                                     GdkEventConfigure *event, gpointer data)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)data;\r
+    int ox, oy, nx, ny, x, y;\r
+\r
+    /*\r
+     * Enlarge the pixmap, but never shrink it.\r
+     */\r
+    ox = fs->preview_width;\r
+    oy = fs->preview_height;\r
+    x = event->width;\r
+    y = event->height;\r
+    if (x > ox || y > oy) {\r
+       if (fs->preview_pixmap)\r
+           gdk_pixmap_unref(fs->preview_pixmap);\r
+       \r
+       nx = (x > ox ? x : ox);\r
+       ny = (y > oy ? y : oy);\r
+       fs->preview_pixmap = gdk_pixmap_new(widget->window, nx, ny, -1);\r
+       fs->preview_width = nx;\r
+       fs->preview_height = ny;\r
+\r
+       unifontsel_draw_preview_text(fs);\r
+    }\r
+\r
+    gdk_window_invalidate_rect(widget->window, NULL, FALSE);\r
+\r
+    return TRUE;\r
+}\r
+\r
+unifontsel *unifontsel_new(const char *wintitle)\r
+{\r
+    unifontsel_internal *fs = snew(unifontsel_internal);\r
+    GtkWidget *table, *label, *w, *ww, *scroll;\r
+    GtkListStore *model;\r
+    GtkTreeViewColumn *column;\r
+    int lists_height, preview_height, font_width, style_width, size_width;\r
+    int i;\r
+\r
+    fs->inhibit_response = FALSE;\r
+    fs->selected = NULL;\r
+\r
+    {\r
+       /*\r
+        * Invent some magic size constants.\r
+        */\r
+       GtkRequisition req;\r
+       label = gtk_label_new("Quite Long Font Name (Foundry)");\r
+       gtk_widget_size_request(label, &req);\r
+       font_width = req.width;\r
+       lists_height = 14 * req.height;\r
+       preview_height = 5 * req.height;\r
+       gtk_label_set_text(GTK_LABEL(label), "Italic Extra Condensed");\r
+       gtk_widget_size_request(label, &req);\r
+       style_width = req.width;\r
+       gtk_label_set_text(GTK_LABEL(label), "48000");\r
+       gtk_widget_size_request(label, &req);\r
+       size_width = req.width;\r
+#if GTK_CHECK_VERSION(2,10,0)\r
+       g_object_ref_sink(label);\r
+       g_object_unref(label);\r
+#else\r
+        gtk_object_sink(GTK_OBJECT(label));\r
+#endif\r
+    }\r
+\r
+    /*\r
+     * Create the dialog box and initialise the user-visible\r
+     * fields in the returned structure.\r
+     */\r
+    fs->u.user_data = NULL;\r
+    fs->u.window = GTK_WINDOW(gtk_dialog_new());\r
+    gtk_window_set_title(fs->u.window, wintitle);\r
+    fs->u.cancel_button = gtk_dialog_add_button\r
+       (GTK_DIALOG(fs->u.window), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);\r
+    fs->u.ok_button = gtk_dialog_add_button\r
+       (GTK_DIALOG(fs->u.window), GTK_STOCK_OK, GTK_RESPONSE_OK);\r
+    gtk_widget_grab_default(fs->u.ok_button);\r
+\r
+    /*\r
+     * Now set up the internal fields, including in particular all\r
+     * the controls that actually allow the user to select fonts.\r
+     */\r
+    table = gtk_table_new(8, 3, FALSE);\r
+    gtk_widget_show(table);\r
+    gtk_table_set_col_spacings(GTK_TABLE(table), 8);\r
+#if GTK_CHECK_VERSION(2,4,0)\r
+    /* GtkAlignment seems to be the simplest way to put padding round things */\r
+    w = gtk_alignment_new(0, 0, 1, 1);\r
+    gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);\r
+    gtk_container_add(GTK_CONTAINER(w), table);\r
+    gtk_widget_show(w);\r
+#else\r
+    w = table;\r
+#endif\r
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(fs->u.window)->vbox),\r
+                      w, TRUE, TRUE, 0);\r
+\r
+    label = gtk_label_new_with_mnemonic("_Font:");\r
+    gtk_widget_show(label);\r
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);\r
+    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);\r
+\r
+    /*\r
+     * The Font list box displays only a string, but additionally\r
+     * stores two integers which give the limits within the\r
+     * tree234 of the font entries covered by this list entry.\r
+     */\r
+    model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);\r
+    w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));\r
+    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);\r
+    gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);\r
+    gtk_widget_show(w);\r
+    column = gtk_tree_view_column_new_with_attributes\r
+       ("Font", gtk_cell_renderer_text_new(),\r
+        "text", 0, (char *)NULL);\r
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);\r
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);\r
+    g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),\r
+                    "changed", G_CALLBACK(family_changed), fs);\r
+    g_signal_connect(G_OBJECT(w), "row-activated",\r
+                    G_CALLBACK(alias_resolve), fs);\r
+\r
+    scroll = gtk_scrolled_window_new(NULL, NULL);\r
+    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),\r
+                                       GTK_SHADOW_IN);\r
+    gtk_container_add(GTK_CONTAINER(scroll), w);\r
+    gtk_widget_show(scroll);\r
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),\r
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);\r
+    gtk_widget_set_size_request(scroll, font_width, lists_height);\r
+    gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL,\r
+                    GTK_EXPAND | GTK_FILL, 0, 0);\r
+    fs->family_model = model;\r
+    fs->family_list = w;\r
+\r
+    label = gtk_label_new_with_mnemonic("_Style:");\r
+    gtk_widget_show(label);\r
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);\r
+    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);\r
+\r
+    /*\r
+     * The Style list box can contain insensitive elements\r
+     * (character set headings for server-side fonts), so we add\r
+     * an extra column to the list store to hold that information.\r
+     */\r
+    model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,\r
+                              G_TYPE_BOOLEAN);\r
+    w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));\r
+    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);\r
+    gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);\r
+    gtk_widget_show(w);\r
+    column = gtk_tree_view_column_new_with_attributes\r
+       ("Style", gtk_cell_renderer_text_new(),\r
+        "text", 0, "sensitive", 3, (char *)NULL);\r
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);\r
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);\r
+    g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),\r
+                    "changed", G_CALLBACK(style_changed), fs);\r
+\r
+    scroll = gtk_scrolled_window_new(NULL, NULL);\r
+    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),\r
+                                       GTK_SHADOW_IN);\r
+    gtk_container_add(GTK_CONTAINER(scroll), w);\r
+    gtk_widget_show(scroll);\r
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),\r
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);\r
+    gtk_widget_set_size_request(scroll, style_width, lists_height);\r
+    gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL,\r
+                    GTK_EXPAND | GTK_FILL, 0, 0);\r
+    fs->style_model = model;\r
+    fs->style_list = w;\r
+\r
+    label = gtk_label_new_with_mnemonic("Si_ze:");\r
+    gtk_widget_show(label);\r
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);\r
+    gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);\r
+\r
+    /*\r
+     * The Size label attaches primarily to a text input box so\r
+     * that the user can select a size of their choice. The list\r
+     * of available sizes is secondary.\r
+     */\r
+    fs->size_entry = w = gtk_entry_new();\r
+    gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);\r
+    gtk_widget_set_size_request(w, size_width, -1);\r
+    gtk_widget_show(w);\r
+    gtk_table_attach(GTK_TABLE(table), w, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);\r
+    g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(size_entry_changed),\r
+                    fs);\r
+\r
+    model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);\r
+    w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));\r
+    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);\r
+    gtk_widget_show(w);\r
+    column = gtk_tree_view_column_new_with_attributes\r
+       ("Size", gtk_cell_renderer_text_new(),\r
+        "text", 0, (char *)NULL);\r
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);\r
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);\r
+    g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),\r
+                    "changed", G_CALLBACK(size_changed), fs);\r
+\r
+    scroll = gtk_scrolled_window_new(NULL, NULL);\r
+    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),\r
+                                       GTK_SHADOW_IN);\r
+    gtk_container_add(GTK_CONTAINER(scroll), w);\r
+    gtk_widget_show(scroll);\r
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),\r
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);\r
+    gtk_table_attach(GTK_TABLE(table), scroll, 2, 3, 2, 3, GTK_FILL,\r
+                    GTK_EXPAND | GTK_FILL, 0, 0);\r
+    fs->size_model = model;\r
+    fs->size_list = w;\r
+\r
+    /*\r
+     * Preview widget.\r
+     */\r
+    fs->preview_area = gtk_drawing_area_new();\r
+    fs->preview_pixmap = NULL;\r
+    fs->preview_width = 0;\r
+    fs->preview_height = 0;\r
+    fs->preview_fg.pixel = fs->preview_bg.pixel = 0;\r
+    fs->preview_fg.red = fs->preview_fg.green = fs->preview_fg.blue = 0x0000;\r
+    fs->preview_bg.red = fs->preview_bg.green = fs->preview_bg.blue = 0xFFFF;\r
+    gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_fg,\r
+                            FALSE, FALSE);\r
+    gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_bg,\r
+                            FALSE, FALSE);\r
+    gtk_signal_connect(GTK_OBJECT(fs->preview_area), "expose_event",\r
+                      GTK_SIGNAL_FUNC(unifontsel_expose_area), fs);\r
+    gtk_signal_connect(GTK_OBJECT(fs->preview_area), "configure_event",\r
+                      GTK_SIGNAL_FUNC(unifontsel_configure_area), fs);\r
+    gtk_widget_set_size_request(fs->preview_area, 1, preview_height);\r
+    gtk_widget_show(fs->preview_area);\r
+    ww = fs->preview_area;\r
+    w = gtk_frame_new(NULL);\r
+    gtk_container_add(GTK_CONTAINER(w), ww);\r
+    gtk_widget_show(w);\r
+#if GTK_CHECK_VERSION(2,4,0)\r
+    ww = w;\r
+    /* GtkAlignment seems to be the simplest way to put padding round things */\r
+    w = gtk_alignment_new(0, 0, 1, 1);\r
+    gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);\r
+    gtk_container_add(GTK_CONTAINER(w), ww);\r
+    gtk_widget_show(w);\r
+#endif\r
+    ww = w;\r
+    w = gtk_frame_new("Preview of font");\r
+    gtk_container_add(GTK_CONTAINER(w), ww);\r
+    gtk_widget_show(w);\r
+    gtk_table_attach(GTK_TABLE(table), w, 0, 3, 3, 4,\r
+                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 8);\r
+\r
+    i = 0;\r
+    w = gtk_check_button_new_with_label("Show client-side fonts");\r
+    gtk_object_set_data(GTK_OBJECT(w), "user-data",\r
+                       GINT_TO_POINTER(FONTFLAG_CLIENTSIDE));\r
+    gtk_signal_connect(GTK_OBJECT(w), "toggled",\r
+                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);\r
+    gtk_widget_show(w);\r
+    fs->filter_buttons[i++] = w;\r
+    gtk_table_attach(GTK_TABLE(table), w, 0, 3, 4, 5, GTK_FILL, 0, 0, 0);\r
+    w = gtk_check_button_new_with_label("Show server-side fonts");\r
+    gtk_object_set_data(GTK_OBJECT(w), "user-data",\r
+                       GINT_TO_POINTER(FONTFLAG_SERVERSIDE));\r
+    gtk_signal_connect(GTK_OBJECT(w), "toggled",\r
+                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);\r
+    gtk_widget_show(w);\r
+    fs->filter_buttons[i++] = w;\r
+    gtk_table_attach(GTK_TABLE(table), w, 0, 3, 5, 6, GTK_FILL, 0, 0, 0);\r
+    w = gtk_check_button_new_with_label("Show server-side font aliases");\r
+    gtk_object_set_data(GTK_OBJECT(w), "user-data",\r
+                       GINT_TO_POINTER(FONTFLAG_SERVERALIAS));\r
+    gtk_signal_connect(GTK_OBJECT(w), "toggled",\r
+                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);\r
+    gtk_widget_show(w);\r
+    fs->filter_buttons[i++] = w;\r
+    gtk_table_attach(GTK_TABLE(table), w, 0, 3, 6, 7, GTK_FILL, 0, 0, 0);\r
+    w = gtk_check_button_new_with_label("Show non-monospaced fonts");\r
+    gtk_object_set_data(GTK_OBJECT(w), "user-data",\r
+                       GINT_TO_POINTER(FONTFLAG_NONMONOSPACED));\r
+    gtk_signal_connect(GTK_OBJECT(w), "toggled",\r
+                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);\r
+    gtk_widget_show(w);\r
+    fs->filter_buttons[i++] = w;\r
+    gtk_table_attach(GTK_TABLE(table), w, 0, 3, 7, 8, GTK_FILL, 0, 0, 0);\r
+\r
+    assert(i == lenof(fs->filter_buttons));\r
+    fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE |\r
+       FONTFLAG_SERVERALIAS;\r
+    unifontsel_set_filter_buttons(fs);\r
+\r
+    /*\r
+     * Go and find all the font names, and set up our master font\r
+     * list.\r
+     */\r
+    fs->fonts_by_realname = newtree234(fontinfo_realname_compare);\r
+    fs->fonts_by_selorder = newtree234(fontinfo_selorder_compare);\r
+    for (i = 0; i < lenof(unifont_types); i++)\r
+       unifont_types[i]->enum_fonts(GTK_WIDGET(fs->u.window),\r
+                                    unifontsel_add_entry, fs);\r
+\r
+    /*\r
+     * And set up the initial font names list.\r
+     */\r
+    unifontsel_setup_familylist(fs);\r
+\r
+    fs->selsize = fs->intendedsize = 13;   /* random default */\r
+    gtk_widget_set_sensitive(fs->u.ok_button, FALSE);\r
+\r
+    return (unifontsel *)fs;\r
+}\r
+\r
+void unifontsel_destroy(unifontsel *fontsel)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)fontsel;\r
+    fontinfo *info;\r
+\r
+    if (fs->preview_pixmap)\r
+       gdk_pixmap_unref(fs->preview_pixmap);\r
+\r
+    freetree234(fs->fonts_by_selorder);\r
+    while ((info = delpos234(fs->fonts_by_realname, 0)) != NULL)\r
+       sfree(info);\r
+    freetree234(fs->fonts_by_realname);\r
+\r
+    gtk_widget_destroy(GTK_WIDGET(fs->u.window));\r
+    sfree(fs);\r
+}\r
+\r
+void unifontsel_set_name(unifontsel *fontsel, const char *fontname)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)fontsel;\r
+    int i, start, end, size, flags;\r
+    const char *fontname2 = NULL;\r
+    fontinfo *info;\r
+\r
+    /*\r
+     * Provide a default if given an empty or null font name.\r
+     */\r
+    if (!fontname || !*fontname)\r
+       fontname = "server:fixed";\r
+\r
+    /*\r
+     * Call the canonify_fontname function.\r
+     */\r
+    fontname = unifont_do_prefix(fontname, &start, &end);\r
+    for (i = start; i < end; i++) {\r
+       fontname2 = unifont_types[i]->canonify_fontname\r
+           (GTK_WIDGET(fs->u.window), fontname, &size, &flags, FALSE);\r
+       if (fontname2)\r
+           break;\r
+    }\r
+    if (i == end)\r
+       return;                        /* font name not recognised */\r
+\r
+    /*\r
+     * Now look up the canonified font name in our index.\r
+     */\r
+    {\r
+       struct fontinfo_realname_find f;\r
+       f.realname = fontname2;\r
+       f.flags = flags;\r
+       info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);\r
+    }\r
+\r
+    /*\r
+     * If we've found the font, and its size field is either\r
+     * correct or zero (the latter indicating a scalable font),\r
+     * then we're done. Otherwise, try looking up the original\r
+     * font name instead.\r
+     */\r
+    if (!info || (info->size != size && info->size != 0)) {\r
+       struct fontinfo_realname_find f;\r
+       f.realname = fontname;\r
+       f.flags = flags;\r
+\r
+       info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);\r
+       if (!info || info->size != size)\r
+           return;                    /* font name not in our index */\r
+    }\r
+\r
+    /*\r
+     * Now we've got a fontinfo structure and a font size, so we\r
+     * know everything we need to fill in all the fields in the\r
+     * dialog.\r
+     */\r
+    unifontsel_select_font(fs, info, size, 0, TRUE);\r
+}\r
+\r
+char *unifontsel_get_name(unifontsel *fontsel)\r
+{\r
+    unifontsel_internal *fs = (unifontsel_internal *)fontsel;\r
+    char *name;\r
+\r
+    if (!fs->selected)\r
+       return NULL;\r
+\r
+    if (fs->selected->size == 0) {\r
+       name = fs->selected->fontclass->scale_fontname\r
+           (GTK_WIDGET(fs->u.window), fs->selected->realname, fs->selsize);\r
+       if (name) {\r
+           char *ret = dupcat(fs->selected->fontclass->prefix, ":",\r
+                              name, NULL);\r
+           sfree(name);\r
+           return ret;\r
+       }\r
+    }\r
+\r
+    return dupcat(fs->selected->fontclass->prefix, ":",\r
+                 fs->selected->realname, NULL);\r
+}\r
+\r
+#endif /* GTK_CHECK_VERSION(2,0,0) */\r
diff --git a/putty/UNIX/GTKFONT.H b/putty/UNIX/GTKFONT.H
new file mode 100644 (file)
index 0000000..41c0505
--- /dev/null
@@ -0,0 +1,67 @@
+/*\r
+ * Header file for gtkfont.c. Has to be separate from unix.h\r
+ * because it depends on GTK data types, hence can't be included\r
+ * from cross-platform code (which doesn't go near GTK).\r
+ */\r
+\r
+#ifndef PUTTY_GTKFONT_H\r
+#define PUTTY_GTKFONT_H\r
+\r
+/*\r
+ * Exports from gtkfont.c.\r
+ */\r
+struct unifont_vtable;                /* contents internal to gtkfont.c */\r
+typedef struct unifont {\r
+    const struct unifont_vtable *vt;\r
+    /*\r
+     * `Non-static data members' of the `class', accessible to\r
+     * external code.\r
+     */\r
+\r
+    /*\r
+     * public_charset is the charset used when the user asks for\r
+     * `Use font encoding'.\r
+     * \r
+     * real_charset is the charset used when translating text into\r
+     * a form suitable for sending to unifont_draw_text().\r
+     * \r
+     * They can differ. For example, public_charset might be\r
+     * CS_ISO8859_1 while real_charset is CS_ISO8859_1_X11.\r
+     */\r
+    int public_charset, real_charset;\r
+\r
+    /*\r
+     * Font dimensions needed by clients.\r
+     */\r
+    int width, height, ascent, descent;\r
+} unifont;\r
+\r
+unifont *unifont_create(GtkWidget *widget, const char *name,\r
+                       int wide, int bold,\r
+                       int shadowoffset, int shadowalways);\r
+void unifont_destroy(unifont *font);\r
+void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,\r
+                      int x, int y, const char *string, int len,\r
+                      int wide, int bold, int cellwidth);\r
+\r
+/*\r
+ * Unified font selector dialog. I can't be bothered to do a\r
+ * proper GTK subclassing today, so this will just be an ordinary\r
+ * data structure with some useful members.\r
+ * \r
+ * (Of course, these aren't the only members; this structure is\r
+ * contained within a bigger one which holds data visible only to\r
+ * the implementation.)\r
+ */\r
+typedef struct unifontsel {\r
+    void *user_data;                  /* settable by the user */\r
+    GtkWindow *window;\r
+    GtkWidget *ok_button, *cancel_button;\r
+} unifontsel;\r
+\r
+unifontsel *unifontsel_new(const char *wintitle);\r
+void unifontsel_destroy(unifontsel *fontsel);\r
+void unifontsel_set_name(unifontsel *fontsel, const char *fontname);\r
+char *unifontsel_get_name(unifontsel *fontsel);\r
+\r
+#endif /* PUTTY_GTKFONT_H */\r
diff --git a/putty/UNIX/GTKWIN.C b/putty/UNIX/GTKWIN.C
new file mode 100644 (file)
index 0000000..f2a13ae
--- /dev/null
@@ -0,0 +1,3645 @@
+/*\r
+ * gtkwin.c: the main code that runs a PuTTY terminal emulator and\r
+ * backend in a GTK window.\r
+ */\r
+\r
+#define _GNU_SOURCE\r
+\r
+#include <string.h>\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <signal.h>\r
+#include <stdio.h>\r
+#include <time.h>\r
+#include <errno.h>\r
+#include <fcntl.h>\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/wait.h>\r
+#include <gtk/gtk.h>\r
+#include <gdk/gdkkeysyms.h>\r
+#include <gdk/gdkx.h>\r
+#include <X11/Xlib.h>\r
+#include <X11/Xutil.h>\r
+#include <X11/Xatom.h>\r
+\r
+#define PUTTY_DO_GLOBALS              /* actually _define_ globals */\r
+\r
+#include "putty.h"\r
+#include "terminal.h"\r
+#include "gtkfont.h"\r
+\r
+#define CAT2(x,y) x ## y\r
+#define CAT(x,y) CAT2(x,y)\r
+#define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+ASSERT(sizeof(long) <= sizeof(gsize));\r
+#define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)\r
+#define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)\r
+#else /* Gtk 1.2 */\r
+ASSERT(sizeof(long) <= sizeof(gpointer));\r
+#define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))\r
+#define GPOINTER_TO_LONG(p) ((long)(p))\r
+#endif\r
+\r
+/* Colours come in two flavours: configurable, and xterm-extended. */\r
+#define NCFGCOLOURS (lenof(((Config *)0)->colours))\r
+#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */\r
+#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)\r
+\r
+GdkAtom compound_text_atom, utf8_string_atom;\r
+\r
+extern char **pty_argv;               /* declared in pty.c */\r
+extern int use_pty_argv;\r
+\r
+/*\r
+ * Timers are global across all sessions (even if we were handling\r
+ * multiple sessions, which we aren't), so the current timer ID is\r
+ * a global variable.\r
+ */\r
+static guint timer_id = 0;\r
+\r
+struct gui_data {\r
+    GtkWidget *window, *area, *sbar;\r
+    GtkBox *hbox;\r
+    GtkAdjustment *sbar_adjust;\r
+    GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2,\r
+       *restartitem;\r
+    GtkWidget *sessionsmenu;\r
+    GdkPixmap *pixmap;\r
+    unifont *fonts[4];                 /* normal, bold, wide, widebold */\r
+    int xpos, ypos, gotpos, gravity;\r
+    GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor;\r
+    GdkColor cols[NALLCOLOURS];\r
+    GdkColormap *colmap;\r
+    wchar_t *pastein_data;\r
+    int direct_to_font;\r
+    int pastein_data_len;\r
+    char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8;\r
+    int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len;\r
+    int font_width, font_height;\r
+    int width, height;\r
+    int ignore_sbar;\r
+    int mouseptr_visible;\r
+    int busy_status;\r
+    guint term_paste_idle_id;\r
+    guint term_exit_idle_id;\r
+    int alt_keycode;\r
+    int alt_digits;\r
+    char wintitle[sizeof(((Config *)0)->wintitle)];\r
+    char icontitle[sizeof(((Config *)0)->wintitle)];\r
+    int master_fd, master_func_id;\r
+    void *ldisc;\r
+    Backend *back;\r
+    void *backhandle;\r
+    Terminal *term;\r
+    void *logctx;\r
+    int exited;\r
+    struct unicode_data ucsdata;\r
+    Config cfg;\r
+    void *eventlogstuff;\r
+    char *progname, **gtkargvstart;\r
+    int ngtkargs;\r
+    guint32 input_event_time; /* Timestamp of the most recent input event. */\r
+    int reconfiguring;\r
+};\r
+\r
+struct draw_ctx {\r
+    GdkGC *gc;\r
+    struct gui_data *inst;\r
+};\r
+\r
+static int send_raw_mouse;\r
+\r
+static char *app_name = "pterm";\r
+\r
+static void start_backend(struct gui_data *inst);\r
+\r
+char *x_get_default(const char *key)\r
+{\r
+    return XGetDefault(GDK_DISPLAY(), app_name, key);\r
+}\r
+\r
+void connection_fatal(void *frontend, char *p, ...)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+\r
+    va_list ap;\r
+    char *msg;\r
+    va_start(ap, p);\r
+    msg = dupvprintf(p, ap);\r
+    va_end(ap);\r
+    inst->exited = TRUE;\r
+    fatal_message_box(inst->window, msg);\r
+    sfree(msg);\r
+    if (inst->cfg.close_on_exit == FORCE_ON)\r
+        cleanup_exit(1);\r
+}\r
+\r
+/*\r
+ * Default settings that are specific to pterm.\r
+ */\r
+FontSpec platform_default_fontspec(const char *name)\r
+{\r
+    FontSpec ret;\r
+    if (!strcmp(name, "Font"))\r
+       strcpy(ret.name, "server:fixed");\r
+    else\r
+       *ret.name = '\0';\r
+    return ret;\r
+}\r
+\r
+Filename platform_default_filename(const char *name)\r
+{\r
+    Filename ret;\r
+    if (!strcmp(name, "LogFileName"))\r
+       strcpy(ret.path, "putty.log");\r
+    else\r
+       *ret.path = '\0';\r
+    return ret;\r
+}\r
+\r
+char *platform_default_s(const char *name)\r
+{\r
+    if (!strcmp(name, "SerialLine"))\r
+       return dupstr("/dev/ttyS0");\r
+    return NULL;\r
+}\r
+\r
+int platform_default_i(const char *name, int def)\r
+{\r
+    if (!strcmp(name, "CloseOnExit"))\r
+       return 2;  /* maps to FORCE_ON after painful rearrangement :-( */\r
+    if (!strcmp(name, "WinNameAlways"))\r
+       return 0;  /* X natively supports icon titles, so use 'em by default */\r
+    return def;\r
+}\r
+\r
+/* Dummy routine, only required in plink. */\r
+void ldisc_update(void *frontend, int echo, int edit)\r
+{\r
+}\r
+\r
+char *get_ttymode(void *frontend, const char *mode)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    return term_get_ttymode(inst->term, mode);\r
+}\r
+\r
+int from_backend(void *frontend, int is_stderr, const char *data, int len)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    return term_data(inst->term, is_stderr, data, len);\r
+}\r
+\r
+int from_backend_untrusted(void *frontend, const char *data, int len)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    return term_data_untrusted(inst->term, data, len);\r
+}\r
+\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)p->frontend;\r
+    int ret;\r
+    ret = cmdline_get_passwd_input(p, in, inlen);\r
+    if (ret == -1)\r
+       ret = term_get_userpass_input(inst->term, p, in, inlen);\r
+    return ret;\r
+}\r
+\r
+void logevent(void *frontend, const char *string)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+\r
+    log_eventlog(inst->logctx, string);\r
+\r
+    logevent_dlg(inst->eventlogstuff, string);\r
+}\r
+\r
+int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+\r
+    if (which)\r
+       return inst->font_height;\r
+    else\r
+       return inst->font_width;\r
+}\r
+\r
+/*\r
+ * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)\r
+ * into a cooked one (SELECT, EXTEND, PASTE).\r
+ * \r
+ * In Unix, this is not configurable; the X button arrangement is\r
+ * rock-solid across all applications, everyone has a three-button\r
+ * mouse or a means of faking it, and there is no need to switch\r
+ * buttons around at all.\r
+ */\r
+static Mouse_Button translate_button(Mouse_Button button)\r
+{\r
+    /* struct gui_data *inst = (struct gui_data *)frontend; */\r
+\r
+    if (button == MBT_LEFT)\r
+       return MBT_SELECT;\r
+    if (button == MBT_MIDDLE)\r
+       return MBT_PASTE;\r
+    if (button == MBT_RIGHT)\r
+       return MBT_EXTEND;\r
+    return 0;                         /* shouldn't happen */\r
+}\r
+\r
+/*\r
+ * Return the top-level GtkWindow associated with a particular\r
+ * front end instance.\r
+ */\r
+void *get_window(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    return inst->window;\r
+}\r
+\r
+/*\r
+ * Minimise or restore the window in response to a server-side\r
+ * request.\r
+ */\r
+void set_iconic(void *frontend, int iconic)\r
+{\r
+    /*\r
+     * GTK 1.2 doesn't know how to do this.\r
+     */\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    if (iconic)\r
+       gtk_window_iconify(GTK_WINDOW(inst->window));\r
+    else\r
+       gtk_window_deiconify(GTK_WINDOW(inst->window));\r
+#endif\r
+}\r
+\r
+/*\r
+ * Move the window in response to a server-side request.\r
+ */\r
+void move_window(void *frontend, int x, int y)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    /*\r
+     * I assume that when the GTK version of this call is available\r
+     * we should use it. Not sure how it differs from the GDK one,\r
+     * though.\r
+     */\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_window_move(GTK_WINDOW(inst->window), x, y);\r
+#else\r
+    gdk_window_move(inst->window->window, x, y);\r
+#endif\r
+}\r
+\r
+/*\r
+ * Move the window to the top or bottom of the z-order in response\r
+ * to a server-side request.\r
+ */\r
+void set_zorder(void *frontend, int top)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    if (top)\r
+       gdk_window_raise(inst->window->window);\r
+    else\r
+       gdk_window_lower(inst->window->window);\r
+}\r
+\r
+/*\r
+ * Refresh the window in response to a server-side request.\r
+ */\r
+void refresh_window(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    term_invalidate(inst->term);\r
+}\r
+\r
+/*\r
+ * Maximise or restore the window in response to a server-side\r
+ * request.\r
+ */\r
+void set_zoomed(void *frontend, int zoomed)\r
+{\r
+    /*\r
+     * GTK 1.2 doesn't know how to do this.\r
+     */\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    if (zoomed)\r
+       gtk_window_maximize(GTK_WINDOW(inst->window));\r
+    else\r
+       gtk_window_unmaximize(GTK_WINDOW(inst->window));\r
+#endif\r
+}\r
+\r
+/*\r
+ * Report whether the window is iconic, for terminal reports.\r
+ */\r
+int is_iconic(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    return !gdk_window_is_viewable(inst->window->window);\r
+}\r
+\r
+/*\r
+ * Report the window's position, for terminal reports.\r
+ */\r
+void get_window_pos(void *frontend, int *x, int *y)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    /*\r
+     * I assume that when the GTK version of this call is available\r
+     * we should use it. Not sure how it differs from the GDK one,\r
+     * though.\r
+     */\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_window_get_position(GTK_WINDOW(inst->window), x, y);\r
+#else\r
+    gdk_window_get_position(inst->window->window, x, y);\r
+#endif\r
+}\r
+\r
+/*\r
+ * Report the window's pixel size, for terminal reports.\r
+ */\r
+void get_window_pixels(void *frontend, int *x, int *y)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    /*\r
+     * I assume that when the GTK version of this call is available\r
+     * we should use it. Not sure how it differs from the GDK one,\r
+     * though.\r
+     */\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_window_get_size(GTK_WINDOW(inst->window), x, y);\r
+#else\r
+    gdk_window_get_size(inst->window->window, x, y);\r
+#endif\r
+}\r
+\r
+/*\r
+ * Return the window or icon title.\r
+ */\r
+char *get_window_title(void *frontend, int icon)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    return icon ? inst->icontitle : inst->wintitle;\r
+}\r
+\r
+gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    if (!inst->exited && inst->cfg.warn_on_close) {\r
+       if (!reallyclose(inst))\r
+           return TRUE;\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+static void update_mouseptr(struct gui_data *inst)\r
+{\r
+    switch (inst->busy_status) {\r
+      case BUSY_NOT:\r
+       if (!inst->mouseptr_visible) {\r
+           gdk_window_set_cursor(inst->area->window, inst->blankcursor);\r
+       } else if (send_raw_mouse) {\r
+           gdk_window_set_cursor(inst->area->window, inst->rawcursor);\r
+       } else {\r
+           gdk_window_set_cursor(inst->area->window, inst->textcursor);\r
+       }\r
+       break;\r
+      case BUSY_WAITING:    /* XXX can we do better? */\r
+      case BUSY_CPU:\r
+       /* We always display these cursors. */\r
+       gdk_window_set_cursor(inst->area->window, inst->waitcursor);\r
+       break;\r
+      default:\r
+       assert(0);\r
+    }\r
+}\r
+\r
+static void show_mouseptr(struct gui_data *inst, int show)\r
+{\r
+    if (!inst->cfg.hide_mouseptr)\r
+       show = 1;\r
+    inst->mouseptr_visible = show;\r
+    update_mouseptr(inst);\r
+}\r
+\r
+void draw_backing_rect(struct gui_data *inst)\r
+{\r
+    GdkGC *gc = gdk_gc_new(inst->area->window);\r
+    gdk_gc_set_foreground(gc, &inst->cols[258]);    /* default background */\r
+    gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,\r
+                      inst->cfg.width * inst->font_width + 2*inst->cfg.window_border,\r
+                      inst->cfg.height * inst->font_height + 2*inst->cfg.window_border);\r
+    gdk_gc_unref(gc);\r
+}\r
+\r
+gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    int w, h, need_size = 0;\r
+\r
+    /*\r
+     * See if the terminal size has changed, in which case we must\r
+     * let the terminal know.\r
+     */\r
+    w = (event->width - 2*inst->cfg.window_border) / inst->font_width;\r
+    h = (event->height - 2*inst->cfg.window_border) / inst->font_height;\r
+    if (w != inst->width || h != inst->height) {\r
+       inst->cfg.width = inst->width = w;\r
+       inst->cfg.height = inst->height = h;\r
+       need_size = 1;\r
+    }\r
+\r
+    if (inst->pixmap) {\r
+       gdk_pixmap_unref(inst->pixmap);\r
+       inst->pixmap = NULL;\r
+    }\r
+\r
+    inst->pixmap = gdk_pixmap_new(widget->window,\r
+                                 (inst->cfg.width * inst->font_width +\r
+                                  2*inst->cfg.window_border),\r
+                                 (inst->cfg.height * inst->font_height +\r
+                                  2*inst->cfg.window_border), -1);\r
+\r
+    draw_backing_rect(inst);\r
+\r
+    if (need_size && inst->term) {\r
+       term_size(inst->term, h, w, inst->cfg.savelines);\r
+    }\r
+\r
+    if (inst->term)\r
+       term_invalidate(inst->term);\r
+\r
+    return TRUE;\r
+}\r
+\r
+gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+\r
+    /*\r
+     * Pass the exposed rectangle to terminal.c, which will call us\r
+     * back to do the actual painting.\r
+     */\r
+    if (inst->pixmap) {\r
+       gdk_draw_pixmap(widget->window,\r
+                       widget->style->fg_gc[GTK_WIDGET_STATE(widget)],\r
+                       inst->pixmap,\r
+                       event->area.x, event->area.y,\r
+                       event->area.x, event->area.y,\r
+                       event->area.width, event->area.height);\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+#define KEY_PRESSED(k) \\r
+    (inst->keystate[(k) / 32] & (1 << ((k) % 32)))\r
+\r
+gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    char output[256];\r
+    wchar_t ucsoutput[2];\r
+    int ucsval, start, end, special, output_charset, use_ucsoutput;\r
+\r
+    /* Remember the timestamp. */\r
+    inst->input_event_time = event->time;\r
+\r
+    /* By default, nothing is generated. */\r
+    end = start = 0;\r
+    special = use_ucsoutput = FALSE;\r
+    output_charset = CS_ISO8859_1;\r
+\r
+    /*\r
+     * If Alt is being released after typing an Alt+numberpad\r
+     * sequence, we should generate the code that was typed.\r
+     * \r
+     * Note that we only do this if more than one key was actually\r
+     * pressed - I don't think Alt+NumPad4 should be ^D or that\r
+     * Alt+NumPad3 should be ^C, for example. There's no serious\r
+     * inconvenience in having to type a zero before a single-digit\r
+     * character code.\r
+     */\r
+    if (event->type == GDK_KEY_RELEASE &&\r
+       (event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||\r
+        event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) &&\r
+       inst->alt_keycode >= 0 && inst->alt_digits > 1) {\r
+#ifdef KEY_DEBUGGING\r
+       printf("Alt key up, keycode = %d\n", inst->alt_keycode);\r
+#endif\r
+       /*\r
+        * FIXME: we might usefully try to do something clever here\r
+        * about interpreting the generated key code in a way that's\r
+        * appropriate to the line code page.\r
+        */\r
+       output[0] = inst->alt_keycode;\r
+       end = 1;\r
+       goto done;\r
+    }\r
+\r
+    if (event->type == GDK_KEY_PRESS) {\r
+#ifdef KEY_DEBUGGING\r
+       {\r
+           int i;\r
+           printf("keypress: keyval = %04x, state = %08x; string =",\r
+                  event->keyval, event->state);\r
+           for (i = 0; event->string[i]; i++)\r
+               printf(" %02x", (unsigned char) event->string[i]);\r
+           printf("\n");\r
+       }\r
+#endif\r
+\r
+       /*\r
+        * NYI: Compose key (!!! requires Unicode faff before even trying)\r
+        */\r
+\r
+       /*\r
+        * If Alt has just been pressed, we start potentially\r
+        * accumulating an Alt+numberpad code. We do this by\r
+        * setting alt_keycode to -1 (nothing yet but plausible).\r
+        */\r
+       if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||\r
+            event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R)) {\r
+           inst->alt_keycode = -1;\r
+            inst->alt_digits = 0;\r
+           goto done;                 /* this generates nothing else */\r
+       }\r
+\r
+       /*\r
+        * If we're seeing a numberpad key press with Mod1 down,\r
+        * consider adding it to alt_keycode if that's sensible.\r
+        * Anything _else_ with Mod1 down cancels any possibility\r
+        * of an ALT keycode: we set alt_keycode to -2.\r
+        */\r
+       if ((event->state & GDK_MOD1_MASK) && inst->alt_keycode != -2) {\r
+           int digit = -1;\r
+           switch (event->keyval) {\r
+             case GDK_KP_0: case GDK_KP_Insert: digit = 0; break;\r
+             case GDK_KP_1: case GDK_KP_End: digit = 1; break;\r
+             case GDK_KP_2: case GDK_KP_Down: digit = 2; break;\r
+             case GDK_KP_3: case GDK_KP_Page_Down: digit = 3; break;\r
+             case GDK_KP_4: case GDK_KP_Left: digit = 4; break;\r
+             case GDK_KP_5: case GDK_KP_Begin: digit = 5; break;\r
+             case GDK_KP_6: case GDK_KP_Right: digit = 6; break;\r
+             case GDK_KP_7: case GDK_KP_Home: digit = 7; break;\r
+             case GDK_KP_8: case GDK_KP_Up: digit = 8; break;\r
+             case GDK_KP_9: case GDK_KP_Page_Up: digit = 9; break;\r
+           }\r
+           if (digit < 0)\r
+               inst->alt_keycode = -2;   /* it's invalid */\r
+           else {\r
+#ifdef KEY_DEBUGGING\r
+               printf("Adding digit %d to keycode %d", digit,\r
+                      inst->alt_keycode);\r
+#endif\r
+               if (inst->alt_keycode == -1)\r
+                   inst->alt_keycode = digit;   /* one-digit code */\r
+               else\r
+                   inst->alt_keycode = inst->alt_keycode * 10 + digit;\r
+                inst->alt_digits++;\r
+#ifdef KEY_DEBUGGING\r
+               printf(" gives new code %d\n", inst->alt_keycode);\r
+#endif\r
+               /* Having used this digit, we now do nothing more with it. */\r
+               goto done;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Shift-PgUp and Shift-PgDn don't even generate keystrokes\r
+        * at all.\r
+        */\r
+       if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) {\r
+           term_scroll(inst->term, 0, -inst->cfg.height/2);\r
+           return TRUE;\r
+       }\r
+       if (event->keyval == GDK_Page_Up && (event->state & GDK_CONTROL_MASK)) {\r
+           term_scroll(inst->term, 0, -1);\r
+           return TRUE;\r
+       }\r
+       if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) {\r
+           term_scroll(inst->term, 0, +inst->cfg.height/2);\r
+           return TRUE;\r
+       }\r
+       if (event->keyval == GDK_Page_Down && (event->state & GDK_CONTROL_MASK)) {\r
+           term_scroll(inst->term, 0, +1);\r
+           return TRUE;\r
+       }\r
+\r
+       /*\r
+        * Neither does Shift-Ins.\r
+        */\r
+       if (event->keyval == GDK_Insert && (event->state & GDK_SHIFT_MASK)) {\r
+           request_paste(inst);\r
+           return TRUE;\r
+       }\r
+\r
+       special = FALSE;\r
+       use_ucsoutput = FALSE;\r
+\r
+       /* ALT+things gives leading Escape. */\r
+       output[0] = '\033';\r
+#if !GTK_CHECK_VERSION(2,0,0)\r
+       /*\r
+        * In vanilla X, and hence also GDK 1.2, the string received\r
+        * as part of a keyboard event is assumed to be in\r
+        * ISO-8859-1. (Seems woefully shortsighted in i18n terms,\r
+        * but it's true: see the man page for XLookupString(3) for\r
+        * confirmation.)\r
+        */\r
+       output_charset = CS_ISO8859_1;\r
+       strncpy(output+1, event->string, lenof(output)-1);\r
+#else\r
+       /*\r
+        * GDK 2.0 arranges to have done some translation for us: in\r
+        * GDK 2.0, event->string is encoded in the current locale.\r
+        *\r
+        * (However, it's also deprecated; we really ought to be\r
+        * using a GTKIMContext.)\r
+        *\r
+        * So we use the standard C library function mbstowcs() to\r
+        * convert from the current locale into Unicode; from there\r
+        * we can convert to whatever PuTTY is currently working in.\r
+        * (In fact I convert straight back to UTF-8 from\r
+        * wide-character Unicode, for the sake of simplicity: that\r
+        * way we can still use exactly the same code to manipulate\r
+        * the string, such as prefixing ESC.)\r
+        */\r
+       output_charset = CS_UTF8;\r
+       {\r
+           wchar_t widedata[32], *wp;\r
+           int wlen;\r
+           int ulen;\r
+\r
+           wlen = mb_to_wc(DEFAULT_CODEPAGE, 0,\r
+                           event->string, strlen(event->string),\r
+                           widedata, lenof(widedata)-1);\r
+\r
+           wp = widedata;\r
+           ulen = charset_from_unicode(&wp, &wlen, output+1, lenof(output)-2,\r
+                                       CS_UTF8, NULL, NULL, 0);\r
+           output[1+ulen] = '\0';\r
+       }\r
+#endif\r
+\r
+       if (!output[1] &&\r
+           (ucsval = keysym_to_unicode(event->keyval)) >= 0) {\r
+           ucsoutput[0] = '\033';\r
+           ucsoutput[1] = ucsval;\r
+           use_ucsoutput = TRUE;\r
+           end = 2;\r
+       } else {\r
+           output[lenof(output)-1] = '\0';\r
+           end = strlen(output);\r
+       }\r
+       if (event->state & GDK_MOD1_MASK) {\r
+           start = 0;\r
+           if (end == 1) end = 0;\r
+       } else\r
+           start = 1;\r
+\r
+       /* Control-` is the same as Control-\ (unless gtk has a better idea) */\r
+       if (!output[1] && event->keyval == '`' &&\r
+           (event->state & GDK_CONTROL_MASK)) {\r
+           output[1] = '\x1C';\r
+           use_ucsoutput = FALSE;\r
+           end = 2;\r
+       }\r
+\r
+       /* Control-Break sends a Break special to the backend */\r
+       if (event->keyval == GDK_Break &&\r
+           (event->state & GDK_CONTROL_MASK)) {\r
+           if (inst->back)\r
+               inst->back->special(inst->backhandle, TS_BRK);\r
+           return TRUE;\r
+       }\r
+\r
+       /* We handle Return ourselves, because it needs to be flagged as\r
+        * special to ldisc. */\r
+       if (event->keyval == GDK_Return) {\r
+           output[1] = '\015';\r
+           use_ucsoutput = FALSE;\r
+           end = 2;\r
+           special = TRUE;\r
+       }\r
+\r
+       /* Control-2, Control-Space and Control-@ are NUL */\r
+       if (!output[1] &&\r
+           (event->keyval == ' ' || event->keyval == '2' ||\r
+            event->keyval == '@') &&\r
+           (event->state & (GDK_SHIFT_MASK |\r
+                            GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) {\r
+           output[1] = '\0';\r
+           use_ucsoutput = FALSE;\r
+           end = 2;\r
+       }\r
+\r
+       /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */\r
+       if (!output[1] && event->keyval == ' ' &&\r
+           (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ==\r
+           (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {\r
+           output[1] = '\240';\r
+           output_charset = CS_ISO8859_1;\r
+           use_ucsoutput = FALSE;\r
+           end = 2;\r
+       }\r
+\r
+       /* We don't let GTK tell us what Backspace is! We know better. */\r
+       if (event->keyval == GDK_BackSpace &&\r
+           !(event->state & GDK_SHIFT_MASK)) {\r
+           output[1] = inst->cfg.bksp_is_delete ? '\x7F' : '\x08';\r
+           use_ucsoutput = FALSE;\r
+           end = 2;\r
+           special = TRUE;\r
+       }\r
+       /* For Shift Backspace, do opposite of what is configured. */\r
+       if (event->keyval == GDK_BackSpace &&\r
+           (event->state & GDK_SHIFT_MASK)) {\r
+           output[1] = inst->cfg.bksp_is_delete ? '\x08' : '\x7F';\r
+           use_ucsoutput = FALSE;\r
+           end = 2;\r
+           special = TRUE;\r
+       }\r
+\r
+       /* Shift-Tab is ESC [ Z */\r
+       if (event->keyval == GDK_ISO_Left_Tab ||\r
+           (event->keyval == GDK_Tab && (event->state & GDK_SHIFT_MASK))) {\r
+           end = 1 + sprintf(output+1, "\033[Z");\r
+           use_ucsoutput = FALSE;\r
+       }\r
+       /* And normal Tab is Tab, if the keymap hasn't already told us.\r
+        * (Curiously, at least one version of the MacOS 10.5 X server\r
+        * doesn't translate Tab for us. */\r
+       if (event->keyval == GDK_Tab && end <= 1) {\r
+           output[1] = '\t';\r
+           end = 2;\r
+       }\r
+\r
+       /*\r
+        * NetHack keypad mode.\r
+        */\r
+       if (inst->cfg.nethack_keypad) {\r
+           char *keys = NULL;\r
+           switch (event->keyval) {\r
+             case GDK_KP_1: case GDK_KP_End: keys = "bB\002"; break;\r
+             case GDK_KP_2: case GDK_KP_Down: keys = "jJ\012"; break;\r
+             case GDK_KP_3: case GDK_KP_Page_Down: keys = "nN\016"; break;\r
+             case GDK_KP_4: case GDK_KP_Left: keys = "hH\010"; break;\r
+             case GDK_KP_5: case GDK_KP_Begin: keys = "..."; break;\r
+             case GDK_KP_6: case GDK_KP_Right: keys = "lL\014"; break;\r
+             case GDK_KP_7: case GDK_KP_Home: keys = "yY\031"; break;\r
+             case GDK_KP_8: case GDK_KP_Up: keys = "kK\013"; break;\r
+             case GDK_KP_9: case GDK_KP_Page_Up: keys = "uU\025"; break;\r
+           }\r
+           if (keys) {\r
+               end = 2;\r
+               if (event->state & GDK_CONTROL_MASK)\r
+                   output[1] = keys[2];\r
+               else if (event->state & GDK_SHIFT_MASK)\r
+                   output[1] = keys[1];\r
+               else\r
+                   output[1] = keys[0];\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Application keypad mode.\r
+        */\r
+       if (inst->term->app_keypad_keys && !inst->cfg.no_applic_k) {\r
+           int xkey = 0;\r
+           switch (event->keyval) {\r
+             case GDK_Num_Lock: xkey = 'P'; break;\r
+             case GDK_KP_Divide: xkey = 'Q'; break;\r
+             case GDK_KP_Multiply: xkey = 'R'; break;\r
+             case GDK_KP_Subtract: xkey = 'S'; break;\r
+               /*\r
+                * Keypad + is tricky. It covers a space that would\r
+                * be taken up on the VT100 by _two_ keys; so we\r
+                * let Shift select between the two. Worse still,\r
+                * in xterm function key mode we change which two...\r
+                */\r
+             case GDK_KP_Add:\r
+               if (inst->cfg.funky_type == FUNKY_XTERM) {\r
+                   if (event->state & GDK_SHIFT_MASK)\r
+                       xkey = 'l';\r
+                   else\r
+                       xkey = 'k';\r
+               } else if (event->state & GDK_SHIFT_MASK)\r
+                       xkey = 'm';\r
+               else\r
+                   xkey = 'l';\r
+               break;\r
+             case GDK_KP_Enter: xkey = 'M'; break;\r
+             case GDK_KP_0: case GDK_KP_Insert: xkey = 'p'; break;\r
+             case GDK_KP_1: case GDK_KP_End: xkey = 'q'; break;\r
+             case GDK_KP_2: case GDK_KP_Down: xkey = 'r'; break;\r
+             case GDK_KP_3: case GDK_KP_Page_Down: xkey = 's'; break;\r
+             case GDK_KP_4: case GDK_KP_Left: xkey = 't'; break;\r
+             case GDK_KP_5: case GDK_KP_Begin: xkey = 'u'; break;\r
+             case GDK_KP_6: case GDK_KP_Right: xkey = 'v'; break;\r
+             case GDK_KP_7: case GDK_KP_Home: xkey = 'w'; break;\r
+             case GDK_KP_8: case GDK_KP_Up: xkey = 'x'; break;\r
+             case GDK_KP_9: case GDK_KP_Page_Up: xkey = 'y'; break;\r
+             case GDK_KP_Decimal: case GDK_KP_Delete: xkey = 'n'; break;\r
+           }\r
+           if (xkey) {\r
+               if (inst->term->vt52_mode) {\r
+                   if (xkey >= 'P' && xkey <= 'S')\r
+                       end = 1 + sprintf(output+1, "\033%c", xkey);\r
+                   else\r
+                       end = 1 + sprintf(output+1, "\033?%c", xkey);\r
+               } else\r
+                   end = 1 + sprintf(output+1, "\033O%c", xkey);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Next, all the keys that do tilde codes. (ESC '[' nn '~',\r
+        * for integer decimal nn.)\r
+        *\r
+        * We also deal with the weird ones here. Linux VCs replace F1\r
+        * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but\r
+        * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w\r
+        * respectively.\r
+        */\r
+       {\r
+           int code = 0;\r
+           switch (event->keyval) {\r
+             case GDK_F1:\r
+               code = (event->state & GDK_SHIFT_MASK ? 23 : 11);\r
+               break;\r
+             case GDK_F2:\r
+               code = (event->state & GDK_SHIFT_MASK ? 24 : 12);\r
+               break;\r
+             case GDK_F3:\r
+               code = (event->state & GDK_SHIFT_MASK ? 25 : 13);\r
+               break;\r
+             case GDK_F4:\r
+               code = (event->state & GDK_SHIFT_MASK ? 26 : 14);\r
+               break;\r
+             case GDK_F5:\r
+               code = (event->state & GDK_SHIFT_MASK ? 28 : 15);\r
+               break;\r
+             case GDK_F6:\r
+               code = (event->state & GDK_SHIFT_MASK ? 29 : 17);\r
+               break;\r
+             case GDK_F7:\r
+               code = (event->state & GDK_SHIFT_MASK ? 31 : 18);\r
+               break;\r
+             case GDK_F8:\r
+               code = (event->state & GDK_SHIFT_MASK ? 32 : 19);\r
+               break;\r
+             case GDK_F9:\r
+               code = (event->state & GDK_SHIFT_MASK ? 33 : 20);\r
+               break;\r
+             case GDK_F10:\r
+               code = (event->state & GDK_SHIFT_MASK ? 34 : 21);\r
+               break;\r
+             case GDK_F11:\r
+               code = 23;\r
+               break;\r
+             case GDK_F12:\r
+               code = 24;\r
+               break;\r
+             case GDK_F13:\r
+               code = 25;\r
+               break;\r
+             case GDK_F14:\r
+               code = 26;\r
+               break;\r
+             case GDK_F15:\r
+               code = 28;\r
+               break;\r
+             case GDK_F16:\r
+               code = 29;\r
+               break;\r
+             case GDK_F17:\r
+               code = 31;\r
+               break;\r
+             case GDK_F18:\r
+               code = 32;\r
+               break;\r
+             case GDK_F19:\r
+               code = 33;\r
+               break;\r
+             case GDK_F20:\r
+               code = 34;\r
+               break;\r
+           }\r
+           if (!(event->state & GDK_CONTROL_MASK)) switch (event->keyval) {\r
+             case GDK_Home: case GDK_KP_Home:\r
+               code = 1;\r
+               break;\r
+             case GDK_Insert: case GDK_KP_Insert:\r
+               code = 2;\r
+               break;\r
+             case GDK_Delete: case GDK_KP_Delete:\r
+               code = 3;\r
+               break;\r
+             case GDK_End: case GDK_KP_End:\r
+               code = 4;\r
+               break;\r
+             case GDK_Page_Up: case GDK_KP_Page_Up:\r
+               code = 5;\r
+               break;\r
+             case GDK_Page_Down: case GDK_KP_Page_Down:\r
+               code = 6;\r
+               break;\r
+           }\r
+           /* Reorder edit keys to physical order */\r
+           if (inst->cfg.funky_type == FUNKY_VT400 && code <= 6)\r
+               code = "\0\2\1\4\5\3\6"[code];\r
+\r
+           if (inst->term->vt52_mode && code > 0 && code <= 6) {\r
+               end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+\r
+           if (inst->cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */\r
+               code >= 11 && code <= 34) {\r
+               char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";\r
+               int index = 0;\r
+               switch (event->keyval) {\r
+                 case GDK_F1: index = 0; break;\r
+                 case GDK_F2: index = 1; break;\r
+                 case GDK_F3: index = 2; break;\r
+                 case GDK_F4: index = 3; break;\r
+                 case GDK_F5: index = 4; break;\r
+                 case GDK_F6: index = 5; break;\r
+                 case GDK_F7: index = 6; break;\r
+                 case GDK_F8: index = 7; break;\r
+                 case GDK_F9: index = 8; break;\r
+                 case GDK_F10: index = 9; break;\r
+                 case GDK_F11: index = 10; break;\r
+                 case GDK_F12: index = 11; break;\r
+               }\r
+               if (event->state & GDK_SHIFT_MASK) index += 12;\r
+               if (event->state & GDK_CONTROL_MASK) index += 24;\r
+               end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+           if (inst->cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */\r
+               code >= 1 && code <= 6) {\r
+               char codes[] = "HL.FIG";\r
+               if (code == 3) {\r
+                   output[1] = '\x7F';\r
+                   end = 2;\r
+               } else {\r
+                   end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);\r
+               }\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+           if ((inst->term->vt52_mode || inst->cfg.funky_type == FUNKY_VT100P) &&\r
+               code >= 11 && code <= 24) {\r
+               int offt = 0;\r
+               if (code > 15)\r
+                   offt++;\r
+               if (code > 21)\r
+                   offt++;\r
+               if (inst->term->vt52_mode)\r
+                   end = 1 + sprintf(output+1,\r
+                                     "\x1B%c", code + 'P' - 11 - offt);\r
+               else\r
+                   end = 1 + sprintf(output+1,\r
+                                     "\x1BO%c", code + 'P' - 11 - offt);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+           if (inst->cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {\r
+               end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+           if (inst->cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {\r
+               if (inst->term->vt52_mode)\r
+                   end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);\r
+               else\r
+                   end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+           if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) {\r
+               end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+           if (code) {\r
+               end = 1 + sprintf(output+1, "\x1B[%d~", code);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Cursor keys. (This includes the numberpad cursor keys,\r
+        * if we haven't already done them due to app keypad mode.)\r
+        * \r
+        * Here we also process un-numlocked un-appkeypadded KP5,\r
+        * which sends ESC [ G.\r
+        */\r
+       {\r
+           int xkey = 0;\r
+           switch (event->keyval) {\r
+             case GDK_Up: case GDK_KP_Up: xkey = 'A'; break;\r
+             case GDK_Down: case GDK_KP_Down: xkey = 'B'; break;\r
+             case GDK_Right: case GDK_KP_Right: xkey = 'C'; break;\r
+             case GDK_Left: case GDK_KP_Left: xkey = 'D'; break;\r
+             case GDK_Begin: case GDK_KP_Begin: xkey = 'G'; break;\r
+           }\r
+           if (xkey) {\r
+               end = 1 + format_arrow_key(output+1, inst->term, xkey,\r
+                                          event->state & GDK_CONTROL_MASK);\r
+               use_ucsoutput = FALSE;\r
+               goto done;\r
+           }\r
+       }\r
+       goto done;\r
+    }\r
+\r
+    done:\r
+\r
+    if (end-start > 0) {\r
+#ifdef KEY_DEBUGGING\r
+       int i;\r
+       printf("generating sequence:");\r
+       for (i = start; i < end; i++)\r
+           printf(" %02x", (unsigned char) output[i]);\r
+       printf("\n");\r
+#endif\r
+\r
+       if (special) {\r
+           /*\r
+            * For special control characters, the character set\r
+            * should never matter.\r
+            */\r
+           output[end] = '\0';        /* NUL-terminate */\r
+           if (inst->ldisc)\r
+               ldisc_send(inst->ldisc, output+start, -2, 1);\r
+       } else if (!inst->direct_to_font) {\r
+           if (!use_ucsoutput) {\r
+               if (inst->ldisc)\r
+                   lpage_send(inst->ldisc, output_charset, output+start,\r
+                              end-start, 1);\r
+           } else {\r
+               /*\r
+                * We generated our own Unicode key data from the\r
+                * keysym, so use that instead.\r
+                */\r
+               if (inst->ldisc)\r
+                   luni_send(inst->ldisc, ucsoutput+start, end-start, 1);\r
+           }\r
+       } else {\r
+           /*\r
+            * In direct-to-font mode, we just send the string\r
+            * exactly as we received it.\r
+            */\r
+           if (inst->ldisc)\r
+               ldisc_send(inst->ldisc, output+start, end-start, 1);\r
+       }\r
+\r
+       show_mouseptr(inst, 0);\r
+       term_seen_key_event(inst->term);\r
+    }\r
+\r
+    return TRUE;\r
+}\r
+\r
+gboolean button_internal(struct gui_data *inst, guint32 timestamp,\r
+                        GdkEventType type, guint ebutton, guint state,\r
+                        gdouble ex, gdouble ey)\r
+{\r
+    int shift, ctrl, alt, x, y, button, act;\r
+\r
+    /* Remember the timestamp. */\r
+    inst->input_event_time = timestamp;\r
+\r
+    show_mouseptr(inst, 1);\r
+\r
+    if (ebutton == 4 && type == GDK_BUTTON_PRESS) {\r
+       term_scroll(inst->term, 0, -5);\r
+       return TRUE;\r
+    }\r
+    if (ebutton == 5 && type == GDK_BUTTON_PRESS) {\r
+       term_scroll(inst->term, 0, +5);\r
+       return TRUE;\r
+    }\r
+\r
+    shift = state & GDK_SHIFT_MASK;\r
+    ctrl = state & GDK_CONTROL_MASK;\r
+    alt = state & GDK_MOD1_MASK;\r
+\r
+    if (ebutton == 3 && ctrl) {\r
+       gtk_menu_popup(GTK_MENU(inst->menu), NULL, NULL, NULL, NULL,\r
+                      ebutton, timestamp);\r
+       return TRUE;\r
+    }\r
+\r
+    if (ebutton == 1)\r
+       button = MBT_LEFT;\r
+    else if (ebutton == 2)\r
+       button = MBT_MIDDLE;\r
+    else if (ebutton == 3)\r
+       button = MBT_RIGHT;\r
+    else\r
+       return FALSE;                  /* don't even know what button! */\r
+\r
+    switch (type) {\r
+      case GDK_BUTTON_PRESS: act = MA_CLICK; break;\r
+      case GDK_BUTTON_RELEASE: act = MA_RELEASE; break;\r
+      case GDK_2BUTTON_PRESS: act = MA_2CLK; break;\r
+      case GDK_3BUTTON_PRESS: act = MA_3CLK; break;\r
+      default: return FALSE;          /* don't know this event type */\r
+    }\r
+\r
+    if (send_raw_mouse && !(inst->cfg.mouse_override && shift) &&\r
+       act != MA_CLICK && act != MA_RELEASE)\r
+       return TRUE;                   /* we ignore these in raw mouse mode */\r
+\r
+    x = (ex - inst->cfg.window_border) / inst->font_width;\r
+    y = (ey - inst->cfg.window_border) / inst->font_height;\r
+\r
+    term_mouse(inst->term, button, translate_button(button), act,\r
+              x, y, shift, ctrl, alt);\r
+\r
+    return TRUE;\r
+}\r
+\r
+gboolean button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    return button_internal(inst, event->time, event->type, event->button,\r
+                          event->state, event->x, event->y);\r
+}\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+/*\r
+ * In GTK 2, mouse wheel events have become a new type of event.\r
+ * This handler translates them back into button-4 and button-5\r
+ * presses so that I don't have to change my old code too much :-)\r
+ */\r
+gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    guint button;\r
+\r
+    if (event->direction == GDK_SCROLL_UP)\r
+       button = 4;\r
+    else if (event->direction == GDK_SCROLL_DOWN)\r
+       button = 5;\r
+    else\r
+       return FALSE;\r
+\r
+    return button_internal(inst, event->time, GDK_BUTTON_PRESS,\r
+                          button, event->state, event->x, event->y);\r
+}\r
+#endif\r
+\r
+gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    int shift, ctrl, alt, x, y, button;\r
+\r
+    /* Remember the timestamp. */\r
+    inst->input_event_time = event->time;\r
+\r
+    show_mouseptr(inst, 1);\r
+\r
+    shift = event->state & GDK_SHIFT_MASK;\r
+    ctrl = event->state & GDK_CONTROL_MASK;\r
+    alt = event->state & GDK_MOD1_MASK;\r
+    if (event->state & GDK_BUTTON1_MASK)\r
+       button = MBT_LEFT;\r
+    else if (event->state & GDK_BUTTON2_MASK)\r
+       button = MBT_MIDDLE;\r
+    else if (event->state & GDK_BUTTON3_MASK)\r
+       button = MBT_RIGHT;\r
+    else\r
+       return FALSE;                  /* don't even know what button! */\r
+\r
+    x = (event->x - inst->cfg.window_border) / inst->font_width;\r
+    y = (event->y - inst->cfg.window_border) / inst->font_height;\r
+\r
+    term_mouse(inst->term, button, translate_button(button), MA_DRAG,\r
+              x, y, shift, ctrl, alt);\r
+\r
+    return TRUE;\r
+}\r
+\r
+void frontend_keypress(void *handle)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)handle;\r
+\r
+    /*\r
+     * If our child process has exited but not closed, terminate on\r
+     * any keypress.\r
+     */\r
+    if (inst->exited)\r
+       exit(0);\r
+}\r
+\r
+static gint idle_exit_func(gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    int exitcode;\r
+\r
+    if (!inst->exited &&\r
+        (exitcode = inst->back->exitcode(inst->backhandle)) >= 0) {\r
+       inst->exited = TRUE;\r
+       if (inst->cfg.close_on_exit == FORCE_ON ||\r
+           (inst->cfg.close_on_exit == AUTO && exitcode == 0))\r
+           gtk_main_quit();           /* just go */\r
+       if (inst->ldisc) {\r
+           ldisc_free(inst->ldisc);\r
+           inst->ldisc = NULL;\r
+       }\r
+       if (inst->back) {\r
+           inst->back->free(inst->backhandle);\r
+           inst->backhandle = NULL;\r
+           inst->back = NULL;\r
+            term_provide_resize_fn(inst->term, NULL, NULL);\r
+           update_specials_menu(inst);\r
+       }\r
+       gtk_widget_set_sensitive(inst->restartitem, TRUE);\r
+    }\r
+\r
+    gtk_idle_remove(inst->term_exit_idle_id);\r
+    return TRUE;\r
+}\r
+\r
+void notify_remote_exit(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+\r
+    inst->term_exit_idle_id = gtk_idle_add(idle_exit_func, inst);\r
+}\r
+\r
+static gint timer_trigger(gpointer data)\r
+{\r
+    long now = GPOINTER_TO_LONG(data);\r
+    long next;\r
+    long ticks;\r
+\r
+    if (run_timers(now, &next)) {\r
+       ticks = next - GETTICKCOUNT();\r
+       timer_id = gtk_timeout_add(ticks > 0 ? ticks : 1, timer_trigger,\r
+                                  LONG_TO_GPOINTER(next));\r
+    }\r
+\r
+    /*\r
+     * Never let a timer resume. If we need another one, we've\r
+     * asked for it explicitly above.\r
+     */\r
+    return FALSE;\r
+}\r
+\r
+void timer_change_notify(long next)\r
+{\r
+    long ticks;\r
+\r
+    if (timer_id)\r
+       gtk_timeout_remove(timer_id);\r
+\r
+    ticks = next - GETTICKCOUNT();\r
+    if (ticks <= 0)\r
+       ticks = 1;                     /* just in case */\r
+\r
+    timer_id = gtk_timeout_add(ticks, timer_trigger,\r
+                              LONG_TO_GPOINTER(next));\r
+}\r
+\r
+void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)\r
+{\r
+    /*\r
+     * We must process exceptional notifications before ordinary\r
+     * readability ones, or we may go straight past the urgent\r
+     * marker.\r
+     */\r
+    if (condition & GDK_INPUT_EXCEPTION)\r
+        select_result(sourcefd, 4);\r
+    if (condition & GDK_INPUT_READ)\r
+        select_result(sourcefd, 1);\r
+    if (condition & GDK_INPUT_WRITE)\r
+        select_result(sourcefd, 2);\r
+}\r
+\r
+void destroy(GtkWidget *widget, gpointer data)\r
+{\r
+    gtk_main_quit();\r
+}\r
+\r
+gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    term_set_focus(inst->term, event->in);\r
+    term_update(inst->term);\r
+    show_mouseptr(inst, 1);\r
+    return FALSE;\r
+}\r
+\r
+void set_busy_status(void *frontend, int status)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    inst->busy_status = status;\r
+    update_mouseptr(inst);\r
+}\r
+\r
+/*\r
+ * set or clear the "raw mouse message" mode\r
+ */\r
+void set_raw_mouse_mode(void *frontend, int activate)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    activate = activate && !inst->cfg.no_mouse_rep;\r
+    send_raw_mouse = activate;\r
+    update_mouseptr(inst);\r
+}\r
+\r
+void request_resize(void *frontend, int w, int h)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    int large_x, large_y;\r
+    int offset_x, offset_y;\r
+    int area_x, area_y;\r
+    GtkRequisition inner, outer;\r
+\r
+    /*\r
+     * This is a heinous hack dreamed up by the gnome-terminal\r
+     * people to get around a limitation in gtk. The problem is\r
+     * that in order to set the size correctly we really need to be\r
+     * calling gtk_window_resize - but that needs to know the size\r
+     * of the _whole window_, not the drawing area. So what we do\r
+     * is to set an artificially huge size request on the drawing\r
+     * area, recompute the resulting size request on the window,\r
+     * and look at the difference between the two. That gives us\r
+     * the x and y offsets we need to translate drawing area size\r
+     * into window size for real, and then we call\r
+     * gtk_window_resize.\r
+     */\r
+\r
+    /*\r
+     * We start by retrieving the current size of the whole window.\r
+     * Adding a bit to _that_ will give us a value we can use as a\r
+     * bogus size request which guarantees to be bigger than the\r
+     * current size of the drawing area.\r
+     */\r
+    get_window_pixels(inst, &large_x, &large_y);\r
+    large_x += 32;\r
+    large_y += 32;\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_widget_set_size_request(inst->area, large_x, large_y);\r
+#else\r
+    gtk_widget_set_usize(inst->area, large_x, large_y);\r
+#endif\r
+    gtk_widget_size_request(inst->area, &inner);\r
+    gtk_widget_size_request(inst->window, &outer);\r
+\r
+    offset_x = outer.width - inner.width;\r
+    offset_y = outer.height - inner.height;\r
+\r
+    area_x = inst->font_width * w + 2*inst->cfg.window_border;\r
+    area_y = inst->font_height * h + 2*inst->cfg.window_border;\r
+\r
+    /*\r
+     * Now we must set the size request on the drawing area back to\r
+     * something sensible before we commit the real resize. Best\r
+     * way to do this, I think, is to set it to what the size is\r
+     * really going to end up being.\r
+     */\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_widget_set_size_request(inst->area, area_x, area_y);\r
+    gtk_window_resize(GTK_WINDOW(inst->window),\r
+                     area_x + offset_x, area_y + offset_y);\r
+#else\r
+    gtk_widget_set_usize(inst->area, area_x, area_y);\r
+    gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), area_x, area_y);\r
+    /*\r
+     * I can no longer remember what this call to\r
+     * gtk_container_dequeue_resize_handler is for. It was\r
+     * introduced in r3092 with no comment, and the commit log\r
+     * message was uninformative. I'm _guessing_ its purpose is to\r
+     * prevent gratuitous resize processing on the window given\r
+     * that we're about to resize it anyway, but I have no idea\r
+     * why that's so incredibly vital.\r
+     * \r
+     * I've tried removing the call, and nothing seems to go\r
+     * wrong. I've backtracked to r3092 and tried removing the\r
+     * call there, and still nothing goes wrong. So I'm going to\r
+     * adopt the working hypothesis that it's superfluous; I won't\r
+     * actually remove it from the GTK 1.2 code, but I won't\r
+     * attempt to replicate its functionality in the GTK 2 code\r
+     * above.\r
+     */\r
+    gtk_container_dequeue_resize_handler(GTK_CONTAINER(inst->window));\r
+    gdk_window_resize(inst->window->window,\r
+                     area_x + offset_x, area_y + offset_y);\r
+#endif\r
+}\r
+\r
+static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)\r
+{\r
+    gboolean success[1];\r
+\r
+    inst->cols[n].red = r * 0x0101;\r
+    inst->cols[n].green = g * 0x0101;\r
+    inst->cols[n].blue = b * 0x0101;\r
+\r
+    gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1);\r
+    gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,\r
+                             FALSE, TRUE, success);\r
+    if (!success[0])\r
+       g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname,\r
+               n, r, g, b);\r
+}\r
+\r
+void set_window_background(struct gui_data *inst)\r
+{\r
+    if (inst->area && inst->area->window)\r
+       gdk_window_set_background(inst->area->window, &inst->cols[258]);\r
+    if (inst->window && inst->window->window)\r
+       gdk_window_set_background(inst->window->window, &inst->cols[258]);\r
+}\r
+\r
+void palette_set(void *frontend, int n, int r, int g, int b)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    if (n >= 16)\r
+       n += 256 - 16;\r
+    if (n > NALLCOLOURS)\r
+       return;\r
+    real_palette_set(inst, n, r, g, b);\r
+    if (n == 258) {\r
+       /* Default Background changed. Ensure space between text area and\r
+        * window border is redrawn */\r
+       set_window_background(inst);\r
+       draw_backing_rect(inst);\r
+       gtk_widget_queue_draw(inst->area);\r
+    }\r
+}\r
+\r
+void palette_reset(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    /* This maps colour indices in inst->cfg to those used in inst->cols. */\r
+    static const int ww[] = {\r
+       256, 257, 258, 259, 260, 261,\r
+       0, 8, 1, 9, 2, 10, 3, 11,\r
+       4, 12, 5, 13, 6, 14, 7, 15\r
+    };\r
+    gboolean success[NALLCOLOURS];\r
+    int i;\r
+\r
+    assert(lenof(ww) == NCFGCOLOURS);\r
+\r
+    if (!inst->colmap) {\r
+       inst->colmap = gdk_colormap_get_system();\r
+    } else {\r
+       gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS);\r
+    }\r
+\r
+    for (i = 0; i < NCFGCOLOURS; i++) {\r
+       inst->cols[ww[i]].red = inst->cfg.colours[i][0] * 0x0101;\r
+       inst->cols[ww[i]].green = inst->cfg.colours[i][1] * 0x0101;\r
+       inst->cols[ww[i]].blue = inst->cfg.colours[i][2] * 0x0101;\r
+    }\r
+\r
+    for (i = 0; i < NEXTCOLOURS; i++) {\r
+       if (i < 216) {\r
+           int r = i / 36, g = (i / 6) % 6, b = i % 6;\r
+           inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0;\r
+           inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0;\r
+           inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0;\r
+       } else {\r
+           int shade = i - 216;\r
+           shade = shade * 0x0a0a + 0x0808;\r
+           inst->cols[i+16].red = inst->cols[i+16].green =\r
+               inst->cols[i+16].blue = shade;\r
+       }\r
+    }\r
+\r
+    gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS,\r
+                             FALSE, TRUE, success);\r
+    for (i = 0; i < NALLCOLOURS; i++) {\r
+       if (!success[i])\r
+           g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",\r
+                    appname, i, inst->cfg.colours[i][0],\r
+                    inst->cfg.colours[i][1], inst->cfg.colours[i][2]);\r
+    }\r
+\r
+    /* Since Default Background may have changed, ensure that space\r
+     * between text area and window border is refreshed. */\r
+    set_window_background(inst);\r
+    if (inst->area && inst->area->window) {\r
+       draw_backing_rect(inst);\r
+       gtk_widget_queue_draw(inst->area);\r
+    }\r
+}\r
+\r
+/* Ensure that all the cut buffers exist - according to the ICCCM, we must\r
+ * do this before we start using cut buffers.\r
+ */\r
+void init_cutbuffers()\r
+{\r
+    unsigned char empty[] = "";\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0);\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0);\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0);\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0);\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0);\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0);\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0);\r
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),\r
+                   XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0);\r
+}\r
+\r
+/* Store the data in a cut-buffer. */\r
+void store_cutbuffer(char * ptr, int len)\r
+{\r
+    /* ICCCM says we must rotate the buffers before storing to buffer 0. */\r
+    XRotateBuffers(GDK_DISPLAY(), 1);\r
+    XStoreBytes(GDK_DISPLAY(), ptr, len);\r
+}\r
+\r
+/* Retrieve data from a cut-buffer.\r
+ * Returned data needs to be freed with XFree().\r
+ */\r
+char * retrieve_cutbuffer(int * nbytes)\r
+{\r
+    char * ptr;\r
+    ptr = XFetchBytes(GDK_DISPLAY(), nbytes);\r
+    if (*nbytes <= 0 && ptr != 0) {\r
+       XFree(ptr);\r
+       ptr = 0;\r
+    }\r
+    return ptr;\r
+}\r
+\r
+void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    if (inst->pasteout_data)\r
+       sfree(inst->pasteout_data);\r
+    if (inst->pasteout_data_ctext)\r
+       sfree(inst->pasteout_data_ctext);\r
+    if (inst->pasteout_data_utf8)\r
+       sfree(inst->pasteout_data_utf8);\r
+\r
+    /*\r
+     * Set up UTF-8 and compound text paste data. This only happens\r
+     * if we aren't in direct-to-font mode using the D800 hack.\r
+     */\r
+    if (!inst->direct_to_font) {\r
+       wchar_t *tmp = data;\r
+       int tmplen = len;\r
+       XTextProperty tp;\r
+       char *list[1];\r
+\r
+       inst->pasteout_data_utf8 = snewn(len*6, char);\r
+       inst->pasteout_data_utf8_len = len*6;\r
+       inst->pasteout_data_utf8_len =\r
+           charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8,\r
+                                inst->pasteout_data_utf8_len,\r
+                                CS_UTF8, NULL, NULL, 0);\r
+       if (inst->pasteout_data_utf8_len == 0) {\r
+           sfree(inst->pasteout_data_utf8);\r
+           inst->pasteout_data_utf8 = NULL;\r
+       } else {\r
+           inst->pasteout_data_utf8 =\r
+               sresize(inst->pasteout_data_utf8,\r
+                       inst->pasteout_data_utf8_len + 1, char);\r
+           inst->pasteout_data_utf8[inst->pasteout_data_utf8_len] = '\0';\r
+       }\r
+\r
+       /*\r
+        * Now let Xlib convert our UTF-8 data into compound text.\r
+        */\r
+       list[0] = inst->pasteout_data_utf8;\r
+       if (Xutf8TextListToTextProperty(GDK_DISPLAY(), list, 1,\r
+                                       XCompoundTextStyle, &tp) == 0) {\r
+           inst->pasteout_data_ctext = snewn(tp.nitems+1, char);\r
+           memcpy(inst->pasteout_data_ctext, tp.value, tp.nitems);\r
+           inst->pasteout_data_ctext_len = tp.nitems;\r
+           XFree(tp.value);\r
+       } else {\r
+            inst->pasteout_data_ctext = NULL;\r
+            inst->pasteout_data_ctext_len = 0;\r
+        }\r
+    } else {\r
+       inst->pasteout_data_utf8 = NULL;\r
+       inst->pasteout_data_utf8_len = 0;\r
+       inst->pasteout_data_ctext = NULL;\r
+       inst->pasteout_data_ctext_len = 0;\r
+    }\r
+\r
+    inst->pasteout_data = snewn(len*6, char);\r
+    inst->pasteout_data_len = len*6;\r
+    inst->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0,\r
+                                      data, len, inst->pasteout_data,\r
+                                      inst->pasteout_data_len,\r
+                                      NULL, NULL, NULL);\r
+    if (inst->pasteout_data_len == 0) {\r
+       sfree(inst->pasteout_data);\r
+       inst->pasteout_data = NULL;\r
+    } else {\r
+       inst->pasteout_data =\r
+           sresize(inst->pasteout_data, inst->pasteout_data_len, char);\r
+    }\r
+\r
+    store_cutbuffer(inst->pasteout_data, inst->pasteout_data_len);\r
+\r
+    if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,\r
+                               inst->input_event_time)) {\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+       gtk_selection_clear_targets(inst->area, GDK_SELECTION_PRIMARY);\r
+#endif\r
+       gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,\r
+                                GDK_SELECTION_TYPE_STRING, 1);\r
+       if (inst->pasteout_data_ctext)\r
+           gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,\r
+                                    compound_text_atom, 1);\r
+       if (inst->pasteout_data_utf8)\r
+           gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,\r
+                                    utf8_string_atom, 1);\r
+    }\r
+\r
+    if (must_deselect)\r
+       term_deselect(inst->term);\r
+}\r
+\r
+void selection_get(GtkWidget *widget, GtkSelectionData *seldata,\r
+                  guint info, guint time_stamp, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    if (seldata->target == utf8_string_atom)\r
+       gtk_selection_data_set(seldata, seldata->target, 8,\r
+                              (unsigned char *)inst->pasteout_data_utf8,\r
+                              inst->pasteout_data_utf8_len);\r
+    else if (seldata->target == compound_text_atom)\r
+       gtk_selection_data_set(seldata, seldata->target, 8,\r
+                              (unsigned char *)inst->pasteout_data_ctext,\r
+                              inst->pasteout_data_ctext_len);\r
+    else\r
+       gtk_selection_data_set(seldata, seldata->target, 8,\r
+                              (unsigned char *)inst->pasteout_data,\r
+                              inst->pasteout_data_len);\r
+}\r
+\r
+gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,\r
+                    gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+\r
+    term_deselect(inst->term);\r
+    if (inst->pasteout_data)\r
+       sfree(inst->pasteout_data);\r
+    if (inst->pasteout_data_ctext)\r
+       sfree(inst->pasteout_data_ctext);\r
+    if (inst->pasteout_data_utf8)\r
+       sfree(inst->pasteout_data_utf8);\r
+    inst->pasteout_data = NULL;\r
+    inst->pasteout_data_len = 0;\r
+    inst->pasteout_data_ctext = NULL;\r
+    inst->pasteout_data_ctext_len = 0;\r
+    inst->pasteout_data_utf8 = NULL;\r
+    inst->pasteout_data_utf8_len = 0;\r
+    return TRUE;\r
+}\r
+\r
+void request_paste(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    /*\r
+     * In Unix, pasting is asynchronous: all we can do at the\r
+     * moment is to call gtk_selection_convert(), and when the data\r
+     * comes back _then_ we can call term_do_paste().\r
+     */\r
+\r
+    if (!inst->direct_to_font) {\r
+       /*\r
+        * First we attempt to retrieve the selection as a UTF-8\r
+        * string (which we will convert to the correct code page\r
+        * before sending to the session, of course). If that\r
+        * fails, selection_received() will be informed and will\r
+        * fall back to an ordinary string.\r
+        */\r
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
+                             utf8_string_atom,\r
+                             inst->input_event_time);\r
+    } else {\r
+       /*\r
+        * If we're in direct-to-font mode, we disable UTF-8\r
+        * pasting, and go straight to ordinary string data.\r
+        */\r
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
+                             GDK_SELECTION_TYPE_STRING,\r
+                             inst->input_event_time);\r
+    }\r
+}\r
+\r
+gint idle_paste_func(gpointer data);   /* forward ref */\r
+\r
+void selection_received(GtkWidget *widget, GtkSelectionData *seldata,\r
+                       guint time, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    XTextProperty tp;\r
+    char **list;\r
+    char *text;\r
+    int length, count, ret;\r
+    int free_list_required = 0;\r
+    int free_required = 0;\r
+    int charset;\r
+\r
+    if (seldata->target == utf8_string_atom && seldata->length <= 0) {\r
+       /*\r
+        * Failed to get a UTF-8 selection string. Try compound\r
+        * text next.\r
+        */\r
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
+                             compound_text_atom,\r
+                             inst->input_event_time);\r
+       return;\r
+    }\r
+\r
+    if (seldata->target == compound_text_atom && seldata->length <= 0) {\r
+       /*\r
+        * Failed to get UTF-8 or compound text. Try an ordinary\r
+        * string.\r
+        */\r
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
+                             GDK_SELECTION_TYPE_STRING,\r
+                             inst->input_event_time);\r
+       return;\r
+    }\r
+\r
+    /*\r
+     * If we have data, but it's not of a type we can deal with,\r
+     * we have to ignore the data.\r
+     */\r
+    if (seldata->length > 0 &&\r
+       seldata->type != GDK_SELECTION_TYPE_STRING &&\r
+       seldata->type != compound_text_atom &&\r
+       seldata->type != utf8_string_atom)\r
+       return;\r
+\r
+    /*\r
+     * If we have no data, try looking in a cut buffer.\r
+     */\r
+    if (seldata->length <= 0) {\r
+       text = retrieve_cutbuffer(&length);\r
+       if (length == 0)\r
+           return;\r
+       /* Xterm is rumoured to expect Latin-1, though I havn't checked the\r
+        * source, so use that as a de-facto standard. */\r
+       charset = CS_ISO8859_1;\r
+       free_required = 1;\r
+    } else {\r
+       /*\r
+        * Convert COMPOUND_TEXT into UTF-8.\r
+        */\r
+       if (seldata->type == compound_text_atom) {\r
+           tp.value = seldata->data;\r
+           tp.encoding = (Atom) seldata->type;\r
+           tp.format = seldata->format;\r
+           tp.nitems = seldata->length;\r
+           ret = Xutf8TextPropertyToTextList(GDK_DISPLAY(), &tp,\r
+                                             &list, &count);\r
+           if (ret != 0 || count != 1) {\r
+               /*\r
+                * Compound text failed; fall back to STRING.\r
+                */\r
+               gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,\r
+                                     GDK_SELECTION_TYPE_STRING,\r
+                                     inst->input_event_time);\r
+               return;\r
+           }\r
+           text = list[0];\r
+           length = strlen(list[0]);\r
+           charset = CS_UTF8;\r
+           free_list_required = 1;\r
+       } else {\r
+           text = (char *)seldata->data;\r
+           length = seldata->length;\r
+           charset = (seldata->type == utf8_string_atom ?\r
+                      CS_UTF8 : inst->ucsdata.line_codepage);\r
+       }\r
+    }\r
+\r
+    if (inst->pastein_data)\r
+       sfree(inst->pastein_data);\r
+\r
+    inst->pastein_data = snewn(length, wchar_t);\r
+    inst->pastein_data_len = length;\r
+    inst->pastein_data_len =\r
+       mb_to_wc(charset, 0, text, length,\r
+                inst->pastein_data, inst->pastein_data_len);\r
+\r
+    term_do_paste(inst->term);\r
+\r
+    if (term_paste_pending(inst->term))\r
+       inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst);\r
+\r
+    if (free_list_required)\r
+       XFreeStringList(list);\r
+    if (free_required)\r
+       XFree(text);\r
+}\r
+\r
+gint idle_paste_func(gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+\r
+    if (term_paste_pending(inst->term))\r
+       term_paste(inst->term);\r
+    else\r
+       gtk_idle_remove(inst->term_paste_idle_id);\r
+\r
+    return TRUE;\r
+}\r
+\r
+\r
+void get_clip(void *frontend, wchar_t ** p, int *len)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+\r
+    if (p) {\r
+       *p = inst->pastein_data;\r
+       *len = inst->pastein_data_len;\r
+    }\r
+}\r
+\r
+static void set_window_titles(struct gui_data *inst)\r
+{\r
+    /*\r
+     * We must always call set_icon_name after calling set_title,\r
+     * since set_title will write both names. Irritating, but such\r
+     * is life.\r
+     */\r
+    gtk_window_set_title(GTK_WINDOW(inst->window), inst->wintitle);\r
+    if (!inst->cfg.win_name_always)\r
+       gdk_window_set_icon_name(inst->window->window, inst->icontitle);\r
+}\r
+\r
+void set_title(void *frontend, char *title)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    strncpy(inst->wintitle, title, lenof(inst->wintitle));\r
+    inst->wintitle[lenof(inst->wintitle)-1] = '\0';\r
+    set_window_titles(inst);\r
+}\r
+\r
+void set_icon(void *frontend, char *title)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    strncpy(inst->icontitle, title, lenof(inst->icontitle));\r
+    inst->icontitle[lenof(inst->icontitle)-1] = '\0';\r
+    set_window_titles(inst);\r
+}\r
+\r
+void set_sbar(void *frontend, int total, int start, int page)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    if (!inst->cfg.scrollbar)\r
+       return;\r
+    inst->sbar_adjust->lower = 0;\r
+    inst->sbar_adjust->upper = total;\r
+    inst->sbar_adjust->value = start;\r
+    inst->sbar_adjust->page_size = page;\r
+    inst->sbar_adjust->step_increment = 1;\r
+    inst->sbar_adjust->page_increment = page/2;\r
+    inst->ignore_sbar = TRUE;\r
+    gtk_adjustment_changed(inst->sbar_adjust);\r
+    inst->ignore_sbar = FALSE;\r
+}\r
+\r
+void scrollbar_moved(GtkAdjustment *adj, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+\r
+    if (!inst->cfg.scrollbar)\r
+       return;\r
+    if (!inst->ignore_sbar)\r
+       term_scroll(inst->term, 1, (int)adj->value);\r
+}\r
+\r
+void sys_cursor(void *frontend, int x, int y)\r
+{\r
+    /*\r
+     * This is meaningless under X.\r
+     */\r
+}\r
+\r
+/*\r
+ * This is still called when mode==BELL_VISUAL, even though the\r
+ * visual bell is handled entirely within terminal.c, because we\r
+ * may want to perform additional actions on any kind of bell (for\r
+ * example, taskbar flashing in Windows).\r
+ */\r
+void do_beep(void *frontend, int mode)\r
+{\r
+    if (mode == BELL_DEFAULT)\r
+       gdk_beep();\r
+}\r
+\r
+int char_width(Context ctx, int uc)\r
+{\r
+    /*\r
+     * Under X, any fixed-width font really _is_ fixed-width.\r
+     * Double-width characters will be dealt with using a separate\r
+     * font. For the moment we can simply return 1.\r
+     * \r
+     * FIXME: but is that also true of Pango?\r
+     */\r
+    return 1;\r
+}\r
+\r
+Context get_ctx(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    struct draw_ctx *dctx;\r
+\r
+    if (!inst->area->window)\r
+       return NULL;\r
+\r
+    dctx = snew(struct draw_ctx);\r
+    dctx->inst = inst;\r
+    dctx->gc = gdk_gc_new(inst->area->window);\r
+    return dctx;\r
+}\r
+\r
+void free_ctx(Context ctx)\r
+{\r
+    struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
+    /* struct gui_data *inst = dctx->inst; */\r
+    GdkGC *gc = dctx->gc;\r
+    gdk_gc_unref(gc);\r
+    sfree(dctx);\r
+}\r
+\r
+/*\r
+ * Draw a line of text in the window, at given character\r
+ * coordinates, in given attributes.\r
+ *\r
+ * We are allowed to fiddle with the contents of `text'.\r
+ */\r
+void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,\r
+                     unsigned long attr, int lattr)\r
+{\r
+    struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
+    struct gui_data *inst = dctx->inst;\r
+    GdkGC *gc = dctx->gc;\r
+    int ncombining, combining;\r
+    int nfg, nbg, t, fontid, shadow, rlen, widefactor, bold;\r
+    int monochrome = gtk_widget_get_visual(inst->area)->depth == 1;\r
+\r
+    if (attr & TATTR_COMBINING) {\r
+       ncombining = len;\r
+       len = 1;\r
+    } else\r
+       ncombining = 1;\r
+\r
+    nfg = ((monochrome ? ATTR_DEFFG : (attr & ATTR_FGMASK)) >> ATTR_FGSHIFT);\r
+    nbg = ((monochrome ? ATTR_DEFBG : (attr & ATTR_BGMASK)) >> ATTR_BGSHIFT);\r
+    if (!!(attr & ATTR_REVERSE) ^ (monochrome && (attr & TATTR_ACTCURS))) {\r
+       t = nfg;\r
+       nfg = nbg;\r
+       nbg = t;\r
+    }\r
+    if (inst->cfg.bold_colour && (attr & ATTR_BOLD)) {\r
+       if (nfg < 16) nfg |= 8;\r
+       else if (nfg >= 256) nfg |= 1;\r
+    }\r
+    if (inst->cfg.bold_colour && (attr & ATTR_BLINK)) {\r
+       if (nbg < 16) nbg |= 8;\r
+       else if (nbg >= 256) nbg |= 1;\r
+    }\r
+    if ((attr & TATTR_ACTCURS) && !monochrome) {\r
+       nfg = 260;\r
+       nbg = 261;\r
+    }\r
+\r
+    fontid = shadow = 0;\r
+\r
+    if (attr & ATTR_WIDE) {\r
+       widefactor = 2;\r
+       fontid |= 2;\r
+    } else {\r
+       widefactor = 1;\r
+    }\r
+\r
+    if ((attr & ATTR_BOLD) && !inst->cfg.bold_colour) {\r
+       bold = 1;\r
+       fontid |= 1;\r
+    } else {\r
+       bold = 0;\r
+    }\r
+\r
+    if (!inst->fonts[fontid]) {\r
+       int i;\r
+       /*\r
+        * Fall back through font ids with subsets of this one's\r
+        * set bits, in order.\r
+        */\r
+       for (i = fontid; i-- > 0 ;) {\r
+           if (i & ~fontid)\r
+               continue;              /* some other bit is set */\r
+           if (inst->fonts[i]) {\r
+               fontid = i;\r
+               break;\r
+           }\r
+       }\r
+       assert(inst->fonts[fontid]);   /* we should at least have hit zero */\r
+    }\r
+\r
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
+       x *= 2;\r
+       if (x >= inst->term->cols)\r
+           return;\r
+       if (x + len*2*widefactor > inst->term->cols)\r
+           len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */\r
+       rlen = len * 2;\r
+    } else\r
+       rlen = len;\r
+\r
+    {\r
+       GdkRectangle r;\r
+\r
+       r.x = x*inst->font_width+inst->cfg.window_border;\r
+       r.y = y*inst->font_height+inst->cfg.window_border;\r
+       r.width = rlen*widefactor*inst->font_width;\r
+       r.height = inst->font_height;\r
+       gdk_gc_set_clip_rectangle(gc, &r);\r
+    }\r
+\r
+    gdk_gc_set_foreground(gc, &inst->cols[nbg]);\r
+    gdk_draw_rectangle(inst->pixmap, gc, 1,\r
+                      x*inst->font_width+inst->cfg.window_border,\r
+                      y*inst->font_height+inst->cfg.window_border,\r
+                      rlen*widefactor*inst->font_width, inst->font_height);\r
+\r
+    gdk_gc_set_foreground(gc, &inst->cols[nfg]);\r
+    {\r
+       gchar *gcs;\r
+\r
+       /*\r
+        * FIXME: this length is hardwired on the assumption that\r
+        * conversions from wide to multibyte characters will\r
+        * never generate more than 10 bytes for a single wide\r
+        * character.\r
+        */\r
+       gcs = snewn(len*10+1, gchar);\r
+\r
+       for (combining = 0; combining < ncombining; combining++) {\r
+           int mblen = wc_to_mb(inst->fonts[fontid]->real_charset, 0,\r
+                                text + combining, len, gcs, len*10+1, ".",\r
+                                NULL, NULL);\r
+           unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid],\r
+                             x*inst->font_width+inst->cfg.window_border,\r
+                             y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,\r
+                             gcs, mblen, widefactor > 1, bold, inst->font_width);\r
+       }\r
+\r
+       sfree(gcs);\r
+    }\r
+\r
+    if (attr & ATTR_UNDER) {\r
+       int uheight = inst->fonts[0]->ascent + 1;\r
+       if (uheight >= inst->font_height)\r
+           uheight = inst->font_height - 1;\r
+       gdk_draw_line(inst->pixmap, gc, x*inst->font_width+inst->cfg.window_border,\r
+                     y*inst->font_height + uheight + inst->cfg.window_border,\r
+                     (x+len)*widefactor*inst->font_width-1+inst->cfg.window_border,\r
+                     y*inst->font_height + uheight + inst->cfg.window_border);\r
+    }\r
+\r
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
+       /*\r
+        * I can't find any plausible StretchBlt equivalent in the\r
+        * X server, so I'm going to do this the slow and painful\r
+        * way. This will involve repeated calls to\r
+        * gdk_draw_pixmap() to stretch the text horizontally. It's\r
+        * O(N^2) in time and O(N) in network bandwidth, but you\r
+        * try thinking of a better way. :-(\r
+        */\r
+       int i;\r
+       for (i = 0; i < len * widefactor * inst->font_width; i++) {\r
+           gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,\r
+                           x*inst->font_width+inst->cfg.window_border + 2*i,\r
+                           y*inst->font_height+inst->cfg.window_border,\r
+                           x*inst->font_width+inst->cfg.window_border + 2*i+1,\r
+                           y*inst->font_height+inst->cfg.window_border,\r
+                           len * widefactor * inst->font_width - i, inst->font_height);\r
+       }\r
+       len *= 2;\r
+       if ((lattr & LATTR_MODE) != LATTR_WIDE) {\r
+           int dt, db;\r
+           /* Now stretch vertically, in the same way. */\r
+           if ((lattr & LATTR_MODE) == LATTR_BOT)\r
+               dt = 0, db = 1;\r
+           else\r
+               dt = 1, db = 0;\r
+           for (i = 0; i < inst->font_height; i+=2) {\r
+               gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,\r
+                               x*inst->font_width+inst->cfg.window_border,\r
+                               y*inst->font_height+inst->cfg.window_border+dt*i+db,\r
+                               x*inst->font_width+inst->cfg.window_border,\r
+                               y*inst->font_height+inst->cfg.window_border+dt*(i+1),\r
+                               len * widefactor * inst->font_width, inst->font_height-i-1);\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+void do_text(Context ctx, int x, int y, wchar_t *text, int len,\r
+            unsigned long attr, int lattr)\r
+{\r
+    struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
+    struct gui_data *inst = dctx->inst;\r
+    GdkGC *gc = dctx->gc;\r
+    int widefactor;\r
+\r
+    do_text_internal(ctx, x, y, text, len, attr, lattr);\r
+\r
+    if (attr & ATTR_WIDE) {\r
+       widefactor = 2;\r
+    } else {\r
+       widefactor = 1;\r
+    }\r
+\r
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
+       x *= 2;\r
+       if (x >= inst->term->cols)\r
+           return;\r
+       if (x + len*2*widefactor > inst->term->cols)\r
+           len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */\r
+       len *= 2;\r
+    }\r
+\r
+    gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,\r
+                   x*inst->font_width+inst->cfg.window_border,\r
+                   y*inst->font_height+inst->cfg.window_border,\r
+                   x*inst->font_width+inst->cfg.window_border,\r
+                   y*inst->font_height+inst->cfg.window_border,\r
+                   len*widefactor*inst->font_width, inst->font_height);\r
+}\r
+\r
+void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,\r
+              unsigned long attr, int lattr)\r
+{\r
+    struct draw_ctx *dctx = (struct draw_ctx *)ctx;\r
+    struct gui_data *inst = dctx->inst;\r
+    GdkGC *gc = dctx->gc;\r
+\r
+    int active, passive, widefactor;\r
+\r
+    if (attr & TATTR_PASCURS) {\r
+       attr &= ~TATTR_PASCURS;\r
+       passive = 1;\r
+    } else\r
+       passive = 0;\r
+    if ((attr & TATTR_ACTCURS) && inst->cfg.cursor_type != 0) {\r
+       attr &= ~TATTR_ACTCURS;\r
+        active = 1;\r
+    } else\r
+        active = 0;\r
+    do_text_internal(ctx, x, y, text, len, attr, lattr);\r
+\r
+    if (attr & TATTR_COMBINING)\r
+       len = 1;\r
+\r
+    if (attr & ATTR_WIDE) {\r
+       widefactor = 2;\r
+    } else {\r
+       widefactor = 1;\r
+    }\r
+\r
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {\r
+       x *= 2;\r
+       if (x >= inst->term->cols)\r
+           return;\r
+       if (x + len*2*widefactor > inst->term->cols)\r
+           len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */\r
+       len *= 2;\r
+    }\r
+\r
+    if (inst->cfg.cursor_type == 0) {\r
+       /*\r
+        * An active block cursor will already have been done by\r
+        * the above do_text call, so we only need to do anything\r
+        * if it's passive.\r
+        */\r
+       if (passive) {\r
+           gdk_gc_set_foreground(gc, &inst->cols[261]);\r
+           gdk_draw_rectangle(inst->pixmap, gc, 0,\r
+                              x*inst->font_width+inst->cfg.window_border,\r
+                              y*inst->font_height+inst->cfg.window_border,\r
+                              len*widefactor*inst->font_width-1, inst->font_height-1);\r
+       }\r
+    } else {\r
+       int uheight;\r
+       int startx, starty, dx, dy, length, i;\r
+\r
+       int char_width;\r
+\r
+       if ((attr & ATTR_WIDE) || (lattr & LATTR_MODE) != LATTR_NORM)\r
+           char_width = 2*inst->font_width;\r
+       else\r
+           char_width = inst->font_width;\r
+\r
+       if (inst->cfg.cursor_type == 1) {\r
+           uheight = inst->fonts[0]->ascent + 1;\r
+           if (uheight >= inst->font_height)\r
+               uheight = inst->font_height - 1;\r
+\r
+           startx = x * inst->font_width + inst->cfg.window_border;\r
+           starty = y * inst->font_height + inst->cfg.window_border + uheight;\r
+           dx = 1;\r
+           dy = 0;\r
+           length = len * widefactor * char_width;\r
+       } else {\r
+           int xadjust = 0;\r
+           if (attr & TATTR_RIGHTCURS)\r
+               xadjust = char_width - 1;\r
+           startx = x * inst->font_width + inst->cfg.window_border + xadjust;\r
+           starty = y * inst->font_height + inst->cfg.window_border;\r
+           dx = 0;\r
+           dy = 1;\r
+           length = inst->font_height;\r
+       }\r
+\r
+       gdk_gc_set_foreground(gc, &inst->cols[261]);\r
+       if (passive) {\r
+           for (i = 0; i < length; i++) {\r
+               if (i % 2 == 0) {\r
+                   gdk_draw_point(inst->pixmap, gc, startx, starty);\r
+               }\r
+               startx += dx;\r
+               starty += dy;\r
+           }\r
+       } else if (active) {\r
+           gdk_draw_line(inst->pixmap, gc, startx, starty,\r
+                         startx + (length-1) * dx, starty + (length-1) * dy);\r
+       } /* else no cursor (e.g., blinked off) */\r
+    }\r
+\r
+    gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,\r
+                   x*inst->font_width+inst->cfg.window_border,\r
+                   y*inst->font_height+inst->cfg.window_border,\r
+                   x*inst->font_width+inst->cfg.window_border,\r
+                   y*inst->font_height+inst->cfg.window_border,\r
+                   len*widefactor*inst->font_width, inst->font_height);\r
+}\r
+\r
+GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)\r
+{\r
+    /*\r
+     * Truly hideous hack: GTK doesn't allow us to set the mouse\r
+     * cursor foreground and background colours unless we've _also_\r
+     * created our own cursor from bitmaps. Therefore, I need to\r
+     * load the `cursor' font and draw glyphs from it on to\r
+     * pixmaps, in order to construct my cursors with the fg and bg\r
+     * I want. This is a gross hack, but it's more self-contained\r
+     * than linking in Xlib to find the X window handle to\r
+     * inst->area and calling XRecolorCursor, and it's more\r
+     * futureproof than hard-coding the shapes as bitmap arrays.\r
+     */\r
+    static GdkFont *cursor_font = NULL;\r
+    GdkPixmap *source, *mask;\r
+    GdkGC *gc;\r
+    GdkColor cfg = { 0, 65535, 65535, 65535 };\r
+    GdkColor cbg = { 0, 0, 0, 0 };\r
+    GdkColor dfg = { 1, 65535, 65535, 65535 };\r
+    GdkColor dbg = { 0, 0, 0, 0 };\r
+    GdkCursor *ret;\r
+    gchar text[2];\r
+    gint lb, rb, wid, asc, desc, w, h, x, y;\r
+\r
+    if (cursor_val == -2) {\r
+       gdk_font_unref(cursor_font);\r
+       return NULL;\r
+    }\r
+\r
+    if (cursor_val >= 0 && !cursor_font) {\r
+       cursor_font = gdk_font_load("cursor");\r
+       if (cursor_font)\r
+           gdk_font_ref(cursor_font);\r
+    }\r
+\r
+    /*\r
+     * Get the text extent of the cursor in question. We use the\r
+     * mask character for this, because it's typically slightly\r
+     * bigger than the main character.\r
+     */\r
+    if (cursor_val >= 0) {\r
+       text[1] = '\0';\r
+       text[0] = (char)cursor_val + 1;\r
+       gdk_string_extents(cursor_font, text, &lb, &rb, &wid, &asc, &desc);\r
+       w = rb-lb; h = asc+desc; x = -lb; y = asc;\r
+    } else {\r
+       w = h = 1;\r
+       x = y = 0;\r
+    }\r
+\r
+    source = gdk_pixmap_new(NULL, w, h, 1);\r
+    mask = gdk_pixmap_new(NULL, w, h, 1);\r
+\r
+    /*\r
+     * Draw the mask character on the mask pixmap.\r
+     */\r
+    gc = gdk_gc_new(mask);\r
+    gdk_gc_set_foreground(gc, &dbg);\r
+    gdk_draw_rectangle(mask, gc, 1, 0, 0, w, h);\r
+    if (cursor_val >= 0) {\r
+       text[1] = '\0';\r
+       text[0] = (char)cursor_val + 1;\r
+       gdk_gc_set_foreground(gc, &dfg);\r
+       gdk_draw_text(mask, cursor_font, gc, x, y, text, 1);\r
+    }\r
+    gdk_gc_unref(gc);\r
+\r
+    /*\r
+     * Draw the main character on the source pixmap.\r
+     */\r
+    gc = gdk_gc_new(source);\r
+    gdk_gc_set_foreground(gc, &dbg);\r
+    gdk_draw_rectangle(source, gc, 1, 0, 0, w, h);\r
+    if (cursor_val >= 0) {\r
+       text[1] = '\0';\r
+       text[0] = (char)cursor_val;\r
+       gdk_gc_set_foreground(gc, &dfg);\r
+       gdk_draw_text(source, cursor_font, gc, x, y, text, 1);\r
+    }\r
+    gdk_gc_unref(gc);\r
+\r
+    /*\r
+     * Create the cursor.\r
+     */\r
+    ret = gdk_cursor_new_from_pixmap(source, mask, &cfg, &cbg, x, y);\r
+\r
+    /*\r
+     * Clean up.\r
+     */\r
+    gdk_pixmap_unref(source);\r
+    gdk_pixmap_unref(mask);\r
+\r
+    return ret;\r
+}\r
+\r
+void modalfatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    exit(1);\r
+}\r
+\r
+void cmdline_error(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "%s: ", appname);\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    exit(1);\r
+}\r
+\r
+char *get_x_display(void *frontend)\r
+{\r
+    return gdk_get_display();\r
+}\r
+\r
+long get_windowid(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+    return (long)GDK_WINDOW_XWINDOW(inst->area->window);\r
+}\r
+\r
+static void help(FILE *fp) {\r
+    if(fprintf(fp,\r
+"pterm option summary:\n"\r
+"\n"\r
+"  --display DISPLAY         Specify X display to use (note '--')\n"\r
+"  -name PREFIX              Prefix when looking up resources (default: pterm)\n"\r
+"  -fn FONT                  Normal text font\n"\r
+"  -fb FONT                  Bold text font\n"\r
+"  -geometry GEOMETRY        Position and size of window (size in characters)\n"\r
+"  -sl LINES                 Number of lines of scrollback\n"\r
+"  -fg COLOUR, -bg COLOUR    Foreground/background colour\n"\r
+"  -bfg COLOUR, -bbg COLOUR  Foreground/background bold colour\n"\r
+"  -cfg COLOUR, -bfg COLOUR  Foreground/background cursor colour\n"\r
+"  -T TITLE                  Window title\n"\r
+"  -ut, +ut                  Do(default) or do not update utmp\n"\r
+"  -ls, +ls                  Do(default) or do not make shell a login shell\n"\r
+"  -sb, +sb                  Do(default) or do not display a scrollbar\n"\r
+"  -log PATH                 Log all output to a file\n"\r
+"  -nethack                  Map numeric keypad to hjklyubn direction keys\n"\r
+"  -xrm RESOURCE-STRING      Set an X resource\n"\r
+"  -e COMMAND [ARGS...]      Execute command (consumes all remaining args)\n"\r
+        ) < 0 || fflush(fp) < 0) {\r
+       perror("output error");\r
+       exit(1);\r
+    }\r
+}\r
+\r
+int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,\r
+               struct gui_data *inst, Config *cfg)\r
+{\r
+    int err = 0;\r
+    char *val;\r
+\r
+    /*\r
+     * Macros to make argument handling easier. Note that because\r
+     * they need to call `continue', they cannot be contained in\r
+     * the usual do {...} while (0) wrapper to make them\r
+     * syntactically single statements; hence it is not legal to\r
+     * use one of these macros as an unbraced statement between\r
+     * `if' and `else'.\r
+     */\r
+#define EXPECTS_ARG { \\r
+    if (--argc <= 0) { \\r
+       err = 1; \\r
+       fprintf(stderr, "%s: %s expects an argument\n", appname, p); \\r
+        continue; \\r
+    } else \\r
+       val = *++argv; \\r
+}\r
+#define SECOND_PASS_ONLY { if (!do_everything) continue; }\r
+\r
+    while (--argc > 0) {\r
+       char *p = *++argv;\r
+        int ret;\r
+\r
+       /*\r
+        * Shameless cheating. Debian requires all X terminal\r
+        * emulators to support `-T title'; but\r
+        * cmdline_process_param will eat -T (it means no-pty) and\r
+        * complain that pterm doesn't support it. So, in pterm\r
+        * only, we convert -T into -title.\r
+        */\r
+       if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&\r
+           !strcmp(p, "-T"))\r
+           p = "-title";\r
+\r
+        ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),\r
+                                    do_everything ? 1 : -1, cfg);\r
+\r
+       if (ret == -2) {\r
+           cmdline_error("option \"%s\" requires an argument", p);\r
+       } else if (ret == 2) {\r
+           --argc, ++argv;            /* skip next argument */\r
+            continue;\r
+       } else if (ret == 1) {\r
+            continue;\r
+        }\r
+\r
+       if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           strncpy(cfg->font.name, val, sizeof(cfg->font.name));\r
+           cfg->font.name[sizeof(cfg->font.name)-1] = '\0';\r
+\r
+       } else if (!strcmp(p, "-fb")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           strncpy(cfg->boldfont.name, val, sizeof(cfg->boldfont.name));\r
+           cfg->boldfont.name[sizeof(cfg->boldfont.name)-1] = '\0';\r
+\r
+       } else if (!strcmp(p, "-fw")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           strncpy(cfg->widefont.name, val, sizeof(cfg->widefont.name));\r
+           cfg->widefont.name[sizeof(cfg->widefont.name)-1] = '\0';\r
+\r
+       } else if (!strcmp(p, "-fwb")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           strncpy(cfg->wideboldfont.name, val, sizeof(cfg->wideboldfont.name));\r
+           cfg->wideboldfont.name[sizeof(cfg->wideboldfont.name)-1] = '\0';\r
+\r
+       } else if (!strcmp(p, "-cs")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           strncpy(cfg->line_codepage, val, sizeof(cfg->line_codepage));\r
+           cfg->line_codepage[sizeof(cfg->line_codepage)-1] = '\0';\r
+\r
+       } else if (!strcmp(p, "-geometry")) {\r
+           int flags, x, y;\r
+           unsigned int w, h;\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+\r
+           flags = XParseGeometry(val, &x, &y, &w, &h);\r
+           if (flags & WidthValue)\r
+               cfg->width = (int)w;\r
+           if (flags & HeightValue)\r
+               cfg->height = (int)h;\r
+\r
+            if (flags & (XValue | YValue)) {\r
+                inst->xpos = x;\r
+                inst->ypos = y;\r
+                inst->gotpos = TRUE;\r
+                inst->gravity = ((flags & XNegative ? 1 : 0) |\r
+                                 (flags & YNegative ? 2 : 0));\r
+            }\r
+\r
+       } else if (!strcmp(p, "-sl")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           cfg->savelines = atoi(val);\r
+\r
+       } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||\r
+                  !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||\r
+                  !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {\r
+           GdkColor col;\r
+\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           if (!gdk_color_parse(val, &col)) {\r
+               err = 1;\r
+               fprintf(stderr, "%s: unable to parse colour \"%s\"\n",\r
+                        appname, val);\r
+           } else {\r
+               int index;\r
+               index = (!strcmp(p, "-fg") ? 0 :\r
+                        !strcmp(p, "-bg") ? 2 :\r
+                        !strcmp(p, "-bfg") ? 1 :\r
+                        !strcmp(p, "-bbg") ? 3 :\r
+                        !strcmp(p, "-cfg") ? 4 :\r
+                        !strcmp(p, "-cbg") ? 5 : -1);\r
+               assert(index != -1);\r
+               cfg->colours[index][0] = col.red / 256;\r
+               cfg->colours[index][1] = col.green / 256;\r
+               cfg->colours[index][2] = col.blue / 256;\r
+           }\r
+\r
+       } else if (use_pty_argv && !strcmp(p, "-e")) {\r
+           /* This option swallows all further arguments. */\r
+           if (!do_everything)\r
+               break;\r
+\r
+           if (--argc > 0) {\r
+               int i;\r
+               pty_argv = snewn(argc+1, char *);\r
+               ++argv;\r
+               for (i = 0; i < argc; i++)\r
+                   pty_argv[i] = argv[i];\r
+               pty_argv[argc] = NULL;\r
+               break;                 /* finished command-line processing */\r
+           } else\r
+               err = 1, fprintf(stderr, "%s: -e expects an argument\n",\r
+                                 appname);\r
+\r
+       } else if (!strcmp(p, "-title")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           strncpy(cfg->wintitle, val, sizeof(cfg->wintitle));\r
+           cfg->wintitle[sizeof(cfg->wintitle)-1] = '\0';\r
+\r
+       } else if (!strcmp(p, "-log")) {\r
+           EXPECTS_ARG;\r
+           SECOND_PASS_ONLY;\r
+           strncpy(cfg->logfilename.path, val, sizeof(cfg->logfilename.path));\r
+           cfg->logfilename.path[sizeof(cfg->logfilename.path)-1] = '\0';\r
+           cfg->logtype = LGTYP_DEBUG;\r
+\r
+       } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {\r
+           SECOND_PASS_ONLY;\r
+           cfg->stamp_utmp = 0;\r
+\r
+       } else if (!strcmp(p, "-ut")) {\r
+           SECOND_PASS_ONLY;\r
+           cfg->stamp_utmp = 1;\r
+\r
+       } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {\r
+           SECOND_PASS_ONLY;\r
+           cfg->login_shell = 0;\r
+\r
+       } else if (!strcmp(p, "-ls")) {\r
+           SECOND_PASS_ONLY;\r
+           cfg->login_shell = 1;\r
+\r
+       } else if (!strcmp(p, "-nethack")) {\r
+           SECOND_PASS_ONLY;\r
+           cfg->nethack_keypad = 1;\r
+\r
+       } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {\r
+           SECOND_PASS_ONLY;\r
+           cfg->scrollbar = 0;\r
+\r
+       } else if (!strcmp(p, "-sb")) {\r
+           SECOND_PASS_ONLY;\r
+           cfg->scrollbar = 0;\r
+\r
+       } else if (!strcmp(p, "-name")) {\r
+           EXPECTS_ARG;\r
+           app_name = val;\r
+\r
+       } else if (!strcmp(p, "-xrm")) {\r
+           EXPECTS_ARG;\r
+           provide_xrm_string(val);\r
+\r
+       } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {\r
+           help(stdout);\r
+           exit(0);\r
+\r
+        } else if (!strcmp(p, "-pgpfp")) {\r
+            pgp_fingerprints();\r
+            exit(1);\r
+\r
+       } else if(p[0] != '-' && (!do_everything ||\r
+                                  process_nonoption_arg(p, cfg,\r
+                                                       allow_launch))) {\r
+            /* do nothing */\r
+\r
+       } else {\r
+           err = 1;\r
+           fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);\r
+       }\r
+    }\r
+\r
+    return err;\r
+}\r
+\r
+int uxsel_input_add(int fd, int rwx) {\r
+    int flags = 0;\r
+    if (rwx & 1) flags |= GDK_INPUT_READ;\r
+    if (rwx & 2) flags |= GDK_INPUT_WRITE;\r
+    if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;\r
+    assert(flags);\r
+    return gdk_input_add(fd, flags, fd_input_func, NULL);\r
+}\r
+\r
+void uxsel_input_remove(int id) {\r
+    gdk_input_remove(id);\r
+}\r
+\r
+void setup_fonts_ucs(struct gui_data *inst)\r
+{\r
+    if (inst->fonts[0])\r
+        unifont_destroy(inst->fonts[0]);\r
+    if (inst->fonts[1])\r
+        unifont_destroy(inst->fonts[1]);\r
+    if (inst->fonts[2])\r
+        unifont_destroy(inst->fonts[2]);\r
+    if (inst->fonts[3])\r
+        unifont_destroy(inst->fonts[3]);\r
+\r
+    inst->fonts[0] = unifont_create(inst->area, inst->cfg.font.name,\r
+                                   FALSE, FALSE,\r
+                                   inst->cfg.shadowboldoffset,\r
+                                   inst->cfg.shadowbold);\r
+    if (!inst->fonts[0]) {\r
+       fprintf(stderr, "%s: unable to load font \"%s\"\n", appname,\r
+               inst->cfg.font.name);\r
+       exit(1);\r
+    }\r
+\r
+    if (inst->cfg.shadowbold || !inst->cfg.boldfont.name[0]) {\r
+       inst->fonts[1] = NULL;\r
+    } else {\r
+       inst->fonts[1] = unifont_create(inst->area, inst->cfg.boldfont.name,\r
+                                       FALSE, TRUE,\r
+                                       inst->cfg.shadowboldoffset,\r
+                                       inst->cfg.shadowbold);\r
+       if (!inst->fonts[1]) {\r
+           fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,\r
+                   inst->cfg.boldfont.name);\r
+           exit(1);\r
+       }\r
+    }\r
+\r
+    if (inst->cfg.widefont.name[0]) {\r
+       inst->fonts[2] = unifont_create(inst->area, inst->cfg.widefont.name,\r
+                                       TRUE, FALSE,\r
+                                       inst->cfg.shadowboldoffset,\r
+                                       inst->cfg.shadowbold);\r
+       if (!inst->fonts[2]) {\r
+           fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,\r
+                   inst->cfg.widefont.name);\r
+           exit(1);\r
+       }\r
+    } else {\r
+       inst->fonts[2] = NULL;\r
+    }\r
+\r
+    if (inst->cfg.shadowbold || !inst->cfg.wideboldfont.name[0]) {\r
+       inst->fonts[3] = NULL;\r
+    } else {\r
+       inst->fonts[3] = unifont_create(inst->area,\r
+                                       inst->cfg.wideboldfont.name, TRUE,\r
+                                       TRUE, inst->cfg.shadowboldoffset,\r
+                                       inst->cfg.shadowbold);\r
+       if (!inst->fonts[3]) {\r
+           fprintf(stderr, "%s: unable to load wide bold font \"%s\"\n", appname,\r
+                   inst->cfg.boldfont.name);\r
+           exit(1);\r
+       }\r
+    }\r
+\r
+    inst->font_width = inst->fonts[0]->width;\r
+    inst->font_height = inst->fonts[0]->height;\r
+\r
+    inst->direct_to_font = init_ucs(&inst->ucsdata, inst->cfg.line_codepage,\r
+                                   inst->cfg.utf8_override,\r
+                                   inst->fonts[0]->public_charset,\r
+                                   inst->cfg.vtmode);\r
+}\r
+\r
+void set_geom_hints(struct gui_data *inst)\r
+{\r
+    GdkGeometry geom;\r
+    geom.min_width = inst->font_width + 2*inst->cfg.window_border;\r
+    geom.min_height = inst->font_height + 2*inst->cfg.window_border;\r
+    geom.max_width = geom.max_height = -1;\r
+    geom.base_width = 2*inst->cfg.window_border;\r
+    geom.base_height = 2*inst->cfg.window_border;\r
+    geom.width_inc = inst->font_width;\r
+    geom.height_inc = inst->font_height;\r
+    geom.min_aspect = geom.max_aspect = 0;\r
+    gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom,\r
+                                  GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |\r
+                                  GDK_HINT_RESIZE_INC);\r
+}\r
+\r
+void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    term_clrsb(inst->term);\r
+}\r
+\r
+void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    term_pwron(inst->term, TRUE);\r
+    if (inst->ldisc)\r
+       ldisc_send(inst->ldisc, NULL, 0, 0);\r
+}\r
+\r
+void copy_all_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    term_copyall(inst->term);\r
+}\r
+\r
+void special_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    int code = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),\r
+                                                  "user-data"));\r
+\r
+    if (inst->back)\r
+       inst->back->special(inst->backhandle, code);\r
+}\r
+\r
+void about_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    about_box(inst->window);\r
+}\r
+\r
+void event_log_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    showeventlog(inst->eventlogstuff, inst->window);\r
+}\r
+\r
+void change_settings_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    /* This maps colour indices in inst->cfg to those used in inst->cols. */\r
+    static const int ww[] = {\r
+       256, 257, 258, 259, 260, 261,\r
+       0, 8, 1, 9, 2, 10, 3, 11,\r
+       4, 12, 5, 13, 6, 14, 7, 15\r
+    };\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    char *title = dupcat(appname, " Reconfiguration", NULL);\r
+    Config cfg2, oldcfg;\r
+    int i, need_size;\r
+\r
+    assert(lenof(ww) == NCFGCOLOURS);\r
+\r
+    if (inst->reconfiguring)\r
+      return;\r
+    else\r
+      inst->reconfiguring = TRUE;\r
+\r
+    cfg2 = inst->cfg;                  /* structure copy */\r
+\r
+    if (do_config_box(title, &cfg2, 1,\r
+                     inst->back?inst->back->cfg_info(inst->backhandle):0)) {\r
+\r
+        oldcfg = inst->cfg;            /* structure copy */\r
+        inst->cfg = cfg2;              /* structure copy */\r
+\r
+        /* Pass new config data to the logging module */\r
+        log_reconfig(inst->logctx, &cfg2);\r
+        /*\r
+         * Flush the line discipline's edit buffer in the case\r
+         * where local editing has just been disabled.\r
+         */\r
+        if (inst->ldisc)\r
+           ldisc_send(inst->ldisc, NULL, 0, 0);\r
+        /* Pass new config data to the terminal */\r
+        term_reconfig(inst->term, &cfg2);\r
+        /* Pass new config data to the back end */\r
+        if (inst->back)\r
+           inst->back->reconfig(inst->backhandle, &cfg2);\r
+\r
+        /*\r
+         * Just setting inst->cfg is sufficient to cause colour\r
+         * setting changes to appear on the next ESC]R palette\r
+         * reset. But we should also check whether any colour\r
+         * settings have been changed, and revert the ones that\r
+         * have to the new default, on the assumption that the user\r
+         * is most likely to want an immediate update.\r
+         */\r
+        for (i = 0; i < NCFGCOLOURS; i++) {\r
+            if (oldcfg.colours[i][0] != cfg2.colours[i][0] ||\r
+                oldcfg.colours[i][1] != cfg2.colours[i][1] ||\r
+                oldcfg.colours[i][2] != cfg2.colours[i][2]) {\r
+                real_palette_set(inst, ww[i], cfg2.colours[i][0],\r
+                                 cfg2.colours[i][1],\r
+                                 cfg2.colours[i][2]);\r
+\r
+               /*\r
+                * If the default background has changed, we must\r
+                * repaint the space in between the window border\r
+                * and the text area.\r
+                */\r
+               if (i == 258) {\r
+                   set_window_background(inst);\r
+                   draw_backing_rect(inst);\r
+               }\r
+           }\r
+        }\r
+\r
+        /*\r
+         * If the scrollbar needs to be shown, hidden, or moved\r
+         * from one end to the other of the window, do so now.\r
+         */\r
+        if (oldcfg.scrollbar != cfg2.scrollbar) {\r
+            if (cfg2.scrollbar)\r
+                gtk_widget_show(inst->sbar);\r
+            else\r
+                gtk_widget_hide(inst->sbar);\r
+        }\r
+        if (oldcfg.scrollbar_on_left != cfg2.scrollbar_on_left) {\r
+            gtk_box_reorder_child(inst->hbox, inst->sbar,\r
+                                  cfg2.scrollbar_on_left ? 0 : 1);\r
+        }\r
+\r
+        /*\r
+         * Change the window title, if required.\r
+         */\r
+        if (strcmp(oldcfg.wintitle, cfg2.wintitle))\r
+            set_title(inst, cfg2.wintitle);\r
+       set_window_titles(inst);\r
+\r
+        /*\r
+         * Redo the whole tangled fonts and Unicode mess if\r
+         * necessary.\r
+         */\r
+        if (strcmp(oldcfg.font.name, cfg2.font.name) ||\r
+            strcmp(oldcfg.boldfont.name, cfg2.boldfont.name) ||\r
+            strcmp(oldcfg.widefont.name, cfg2.widefont.name) ||\r
+            strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) ||\r
+            strcmp(oldcfg.line_codepage, cfg2.line_codepage) ||\r
+           oldcfg.vtmode != cfg2.vtmode ||\r
+           oldcfg.shadowbold != cfg2.shadowbold) {\r
+            setup_fonts_ucs(inst);\r
+            need_size = 1;\r
+        } else\r
+            need_size = 0;\r
+\r
+        /*\r
+         * Resize the window.\r
+         */\r
+        if (oldcfg.width != cfg2.width || oldcfg.height != cfg2.height ||\r
+            oldcfg.window_border != cfg2.window_border || need_size) {\r
+            set_geom_hints(inst);\r
+            request_resize(inst, cfg2.width, cfg2.height);\r
+        } else {\r
+           /*\r
+            * The above will have caused a call to term_size() for\r
+            * us if it happened. If the user has fiddled with only\r
+            * the scrollback size, the above will not have\r
+            * happened and we will need an explicit term_size()\r
+            * here.\r
+            */\r
+           if (oldcfg.savelines != cfg2.savelines)\r
+               term_size(inst->term, inst->term->rows, inst->term->cols,\r
+                         cfg2.savelines);\r
+       }\r
+\r
+        term_invalidate(inst->term);\r
+\r
+       /*\r
+        * We do an explicit full redraw here to ensure the window\r
+        * border has been redrawn as well as the text area.\r
+        */\r
+       gtk_widget_queue_draw(inst->area);\r
+    }\r
+    sfree(title);\r
+    inst->reconfiguring = FALSE;\r
+}\r
+\r
+void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)\r
+{\r
+    /*\r
+     * Re-execing ourself is not an exact science under Unix. I do\r
+     * the best I can by using /proc/self/exe if available and by\r
+     * assuming argv[0] can be found on $PATH if not.\r
+     * \r
+     * Note that we also have to reconstruct the elements of the\r
+     * original argv which gtk swallowed, since the user wants the\r
+     * new session to appear on the same X display as the old one.\r
+     */\r
+    char **args;\r
+    va_list ap;\r
+    int i, n;\r
+    int pid;\r
+\r
+    /*\r
+     * Collect the arguments with which to re-exec ourself.\r
+     */\r
+    va_start(ap, fd_to_close);\r
+    n = 2;                            /* progname and terminating NULL */\r
+    n += inst->ngtkargs;\r
+    while (va_arg(ap, char *) != NULL)\r
+       n++;\r
+    va_end(ap);\r
+\r
+    args = snewn(n, char *);\r
+    args[0] = inst->progname;\r
+    args[n-1] = NULL;\r
+    for (i = 0; i < inst->ngtkargs; i++)\r
+       args[i+1] = inst->gtkargvstart[i];\r
+\r
+    i++;\r
+    va_start(ap, fd_to_close);\r
+    while ((args[i++] = va_arg(ap, char *)) != NULL);\r
+    va_end(ap);\r
+\r
+    assert(i == n);\r
+\r
+    /*\r
+     * Do the double fork.\r
+     */\r
+    pid = fork();\r
+    if (pid < 0) {\r
+       perror("fork");\r
+       return;\r
+    }\r
+\r
+    if (pid == 0) {\r
+       int pid2 = fork();\r
+       if (pid2 < 0) {\r
+           perror("fork");\r
+           _exit(1);\r
+       } else if (pid2 > 0) {\r
+           /*\r
+            * First child has successfully forked second child. My\r
+            * Work Here Is Done. Note the use of _exit rather than\r
+            * exit: the latter appears to cause destroy messages\r
+            * to be sent to the X server. I suspect gtk uses\r
+            * atexit.\r
+            */\r
+           _exit(0);\r
+       }\r
+\r
+       /*\r
+        * If we reach here, we are the second child, so we now\r
+        * actually perform the exec.\r
+        */\r
+       if (fd_to_close >= 0)\r
+           close(fd_to_close);\r
+\r
+       execv("/proc/self/exe", args);\r
+       execvp(inst->progname, args);\r
+       perror("exec");\r
+       _exit(127);\r
+\r
+    } else {\r
+       int status;\r
+       waitpid(pid, &status, 0);\r
+    }\r
+\r
+}\r
+\r
+void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)gdata;\r
+    /*\r
+     * For this feature we must marshal cfg and (possibly) pty_argv\r
+     * into a byte stream, create a pipe, and send this byte stream\r
+     * to the child through the pipe.\r
+     */\r
+    int i, ret, size;\r
+    char *data;\r
+    char option[80];\r
+    int pipefd[2];\r
+\r
+    if (pipe(pipefd) < 0) {\r
+       perror("pipe");\r
+       return;\r
+    }\r
+\r
+    size = sizeof(inst->cfg);\r
+    if (use_pty_argv && pty_argv) {\r
+       for (i = 0; pty_argv[i]; i++)\r
+           size += strlen(pty_argv[i]) + 1;\r
+    }\r
+\r
+    data = snewn(size, char);\r
+    memcpy(data, &inst->cfg, sizeof(inst->cfg));\r
+    if (use_pty_argv && pty_argv) {\r
+       int p = sizeof(inst->cfg);\r
+       for (i = 0; pty_argv[i]; i++) {\r
+           strcpy(data + p, pty_argv[i]);\r
+           p += strlen(pty_argv[i]) + 1;\r
+       }\r
+       assert(p == size);\r
+    }\r
+\r
+    sprintf(option, "---[%d,%d]", pipefd[0], size);\r
+    fcntl(pipefd[0], F_SETFD, 0);\r
+    fork_and_exec_self(inst, pipefd[1], option, NULL);\r
+    close(pipefd[0]);\r
+\r
+    i = ret = 0;\r
+    while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)\r
+       i += ret;\r
+    if (ret < 0)\r
+       perror("write to pipe");\r
+    close(pipefd[1]);\r
+    sfree(data);\r
+}\r
+\r
+int read_dupsession_data(struct gui_data *inst, Config *cfg, char *arg)\r
+{\r
+    int fd, i, ret, size;\r
+    char *data;\r
+\r
+    if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {\r
+       fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);\r
+       exit(1);\r
+    }\r
+\r
+    data = snewn(size, char);\r
+    i = ret = 0;\r
+    while (i < size && (ret = read(fd, data + i, size - i)) > 0)\r
+       i += ret;\r
+    if (ret < 0) {\r
+       perror("read from pipe");\r
+       exit(1);\r
+    } else if (i < size) {\r
+       fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",\r
+               appname);\r
+       exit(1);\r
+    }\r
+\r
+    memcpy(cfg, data, sizeof(Config));\r
+    if (use_pty_argv && size > sizeof(Config)) {\r
+       int n = 0;\r
+       i = sizeof(Config);\r
+       while (i < size) {\r
+           while (i < size && data[i]) i++;\r
+           if (i >= size) {\r
+               fprintf(stderr, "%s: malformed Duplicate Session data\n",\r
+                       appname);\r
+               exit(1);\r
+           }\r
+           i++;\r
+           n++;\r
+       }\r
+       pty_argv = snewn(n+1, char *);\r
+       pty_argv[n] = NULL;\r
+       n = 0;\r
+       i = sizeof(Config);\r
+       while (i < size) {\r
+           char *p = data + i;\r
+           while (i < size && data[i]) i++;\r
+           assert(i < size);\r
+           i++;\r
+           pty_argv[n++] = dupstr(p);\r
+       }\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+void new_session_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+\r
+    fork_and_exec_self(inst, -1, NULL);\r
+}\r
+\r
+void restart_session_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+\r
+    if (!inst->back) {\r
+       logevent(inst, "----- Session restarted -----");\r
+       term_pwron(inst->term, FALSE);\r
+       start_backend(inst);\r
+       inst->exited = FALSE;\r
+    }\r
+}\r
+\r
+void saved_session_menuitem(GtkMenuItem *item, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    char *str = (char *)gtk_object_get_data(GTK_OBJECT(item), "user-data");\r
+\r
+    fork_and_exec_self(inst, -1, "-load", str, NULL);\r
+}\r
+\r
+void saved_session_freedata(GtkMenuItem *item, gpointer data)\r
+{\r
+    char *str = (char *)gtk_object_get_data(GTK_OBJECT(item), "user-data");\r
+\r
+    sfree(str);\r
+}\r
+\r
+static void update_savedsess_menu(GtkMenuItem *menuitem, gpointer data)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)data;\r
+    struct sesslist sesslist;\r
+    int i;\r
+\r
+    gtk_container_foreach(GTK_CONTAINER(inst->sessionsmenu),\r
+                         (GtkCallback)gtk_widget_destroy, NULL);\r
+\r
+    get_sesslist(&sesslist, TRUE);\r
+    /* skip sesslist.sessions[0] == Default Settings */\r
+    for (i = 1; i < sesslist.nsessions; i++) {\r
+       GtkWidget *menuitem =\r
+           gtk_menu_item_new_with_label(sesslist.sessions[i]);\r
+       gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);\r
+       gtk_widget_show(menuitem);\r
+       gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",\r
+                           dupstr(sesslist.sessions[i]));\r
+       gtk_signal_connect(GTK_OBJECT(menuitem), "activate",\r
+                          GTK_SIGNAL_FUNC(saved_session_menuitem),\r
+                          inst);\r
+       gtk_signal_connect(GTK_OBJECT(menuitem), "destroy",\r
+                          GTK_SIGNAL_FUNC(saved_session_freedata),\r
+                          inst);\r
+    }\r
+    if (sesslist.nsessions <= 1) {\r
+       GtkWidget *menuitem =\r
+           gtk_menu_item_new_with_label("(No sessions)");\r
+       gtk_widget_set_sensitive(menuitem, FALSE);\r
+       gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem);\r
+       gtk_widget_show(menuitem);\r
+    }\r
+    get_sesslist(&sesslist, FALSE); /* free up */\r
+}\r
+\r
+void set_window_icon(GtkWidget *window, const char *const *const *icon,\r
+                    int n_icon)\r
+{\r
+    GdkPixmap *iconpm;\r
+    GdkBitmap *iconmask;\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    GList *iconlist;\r
+    int n;\r
+#endif\r
+\r
+    if (!n_icon)\r
+       return;\r
+\r
+    gtk_widget_realize(window);\r
+    iconpm = gdk_pixmap_create_from_xpm_d(window->window, &iconmask,\r
+                                         NULL, (gchar **)icon[0]);\r
+    gdk_window_set_icon(window->window, NULL, iconpm, iconmask);\r
+\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    iconlist = NULL;\r
+    for (n = 0; n < n_icon; n++) {\r
+       iconlist =\r
+           g_list_append(iconlist,\r
+                         gdk_pixbuf_new_from_xpm_data((const gchar **)\r
+                                                      icon[n]));\r
+    }\r
+    gdk_window_set_icon_list(window->window, iconlist);\r
+#endif\r
+}\r
+\r
+void update_specials_menu(void *frontend)\r
+{\r
+    struct gui_data *inst = (struct gui_data *)frontend;\r
+\r
+    const struct telnet_special *specials;\r
+\r
+    if (inst->back)\r
+       specials = inst->back->get_specials(inst->backhandle);\r
+    else\r
+       specials = NULL;\r
+\r
+    /* I believe this disposes of submenus too. */\r
+    gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),\r
+                         (GtkCallback)gtk_widget_destroy, NULL);\r
+    if (specials) {\r
+       int i;\r
+       GtkWidget *menu = inst->specialsmenu;\r
+       /* A lame "stack" for submenus that will do for now. */\r
+       GtkWidget *saved_menu = NULL;\r
+       int nesting = 1;\r
+       for (i = 0; nesting > 0; i++) {\r
+           GtkWidget *menuitem = NULL;\r
+           switch (specials[i].code) {\r
+             case TS_SUBMENU:\r
+               assert (nesting < 2);\r
+               saved_menu = menu; /* XXX lame stacking */\r
+               menu = gtk_menu_new();\r
+               menuitem = gtk_menu_item_new_with_label(specials[i].name);\r
+               gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);\r
+               gtk_container_add(GTK_CONTAINER(saved_menu), menuitem);\r
+               gtk_widget_show(menuitem);\r
+               menuitem = NULL;\r
+               nesting++;\r
+               break;\r
+             case TS_EXITMENU:\r
+               nesting--;\r
+               if (nesting) {\r
+                   menu = saved_menu; /* XXX lame stacking */\r
+                   saved_menu = NULL;\r
+               }\r
+               break;\r
+             case TS_SEP:\r
+               menuitem = gtk_menu_item_new();\r
+               break;\r
+             default:\r
+               menuitem = gtk_menu_item_new_with_label(specials[i].name);\r
+               gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",\r
+                                   GINT_TO_POINTER(specials[i].code));\r
+               gtk_signal_connect(GTK_OBJECT(menuitem), "activate",\r
+                                  GTK_SIGNAL_FUNC(special_menuitem), inst);\r
+               break;\r
+           }\r
+           if (menuitem) {\r
+               gtk_container_add(GTK_CONTAINER(menu), menuitem);\r
+               gtk_widget_show(menuitem);\r
+           }\r
+       }\r
+       gtk_widget_show(inst->specialsitem1);\r
+       gtk_widget_show(inst->specialsitem2);\r
+    } else {\r
+       gtk_widget_hide(inst->specialsitem1);\r
+       gtk_widget_hide(inst->specialsitem2);\r
+    }\r
+}\r
+\r
+static void start_backend(struct gui_data *inst)\r
+{\r
+    extern Backend *select_backend(Config *cfg);\r
+    char *realhost;\r
+    const char *error;\r
+\r
+    inst->back = select_backend(&inst->cfg);\r
+\r
+    error = inst->back->init((void *)inst, &inst->backhandle,\r
+                            &inst->cfg, inst->cfg.host, inst->cfg.port,\r
+                            &realhost, inst->cfg.tcp_nodelay,\r
+                            inst->cfg.tcp_keepalives);\r
+\r
+    if (error) {\r
+       char *msg = dupprintf("Unable to open connection to %s:\n%s",\r
+                             inst->cfg.host, error);\r
+       inst->exited = TRUE;\r
+       fatal_message_box(inst->window, msg);\r
+       sfree(msg);\r
+       exit(0);\r
+    }\r
+\r
+    if (inst->cfg.wintitle[0]) {\r
+       set_title(inst, inst->cfg.wintitle);\r
+       set_icon(inst, inst->cfg.wintitle);\r
+    } else {\r
+       char *title = make_default_wintitle(realhost);\r
+       set_title(inst, title);\r
+       set_icon(inst, title);\r
+       sfree(title);\r
+    }\r
+    sfree(realhost);\r
+\r
+    inst->back->provide_logctx(inst->backhandle, inst->logctx);\r
+\r
+    term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);\r
+\r
+    inst->ldisc =\r
+       ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle,\r
+                    inst);\r
+\r
+    gtk_widget_set_sensitive(inst->restartitem, FALSE);\r
+}\r
+\r
+int pt_main(int argc, char **argv)\r
+{\r
+    extern int cfgbox(Config *cfg);\r
+    struct gui_data *inst;\r
+\r
+    /*\r
+     * Create an instance structure and initialise to zeroes\r
+     */\r
+    inst = snew(struct gui_data);\r
+    memset(inst, 0, sizeof(*inst));\r
+    inst->alt_keycode = -1;            /* this one needs _not_ to be zero */\r
+    inst->busy_status = BUSY_NOT;\r
+\r
+    /* defer any child exit handling until we're ready to deal with\r
+     * it */\r
+    block_signal(SIGCHLD, 1);\r
+\r
+    inst->progname = argv[0];\r
+    /*\r
+     * Copy the original argv before letting gtk_init fiddle with\r
+     * it. It will be required later.\r
+     */\r
+    {\r
+       int i, oldargc;\r
+       inst->gtkargvstart = snewn(argc-1, char *);\r
+       for (i = 1; i < argc; i++)\r
+           inst->gtkargvstart[i-1] = dupstr(argv[i]);\r
+       oldargc = argc;\r
+       gtk_init(&argc, &argv);\r
+       inst->ngtkargs = oldargc - argc;\r
+    }\r
+\r
+    if (argc > 1 && !strncmp(argv[1], "---", 3)) {\r
+       read_dupsession_data(inst, &inst->cfg, argv[1]);\r
+       /* Splatter this argument so it doesn't clutter a ps listing */\r
+       memset(argv[1], 0, strlen(argv[1]));\r
+    } else {\r
+       /* By default, we bring up the config dialog, rather than launching\r
+        * a session. This gets set to TRUE if something happens to change\r
+        * that (e.g., a hostname is specified on the command-line). */\r
+       int allow_launch = FALSE;\r
+       if (do_cmdline(argc, argv, 0, &allow_launch, inst, &inst->cfg))\r
+           exit(1);                   /* pre-defaults pass to get -class */\r
+       do_defaults(NULL, &inst->cfg);\r
+       if (do_cmdline(argc, argv, 1, &allow_launch, inst, &inst->cfg))\r
+           exit(1);                   /* post-defaults, do everything */\r
+\r
+       cmdline_run_saved(&inst->cfg);\r
+\r
+       if (loaded_session)\r
+           allow_launch = TRUE;\r
+\r
+       if ((!allow_launch || !cfg_launchable(&inst->cfg)) &&\r
+           !cfgbox(&inst->cfg))\r
+           exit(0);                   /* config box hit Cancel */\r
+    }\r
+\r
+    if (!compound_text_atom)\r
+        compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);\r
+    if (!utf8_string_atom)\r
+        utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);\r
+\r
+    inst->area = gtk_drawing_area_new();\r
+\r
+    setup_fonts_ucs(inst);\r
+    init_cutbuffers();\r
+\r
+    inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);\r
+    if (inst->cfg.winclass[0])\r
+        gtk_window_set_wmclass(GTK_WINDOW(inst->window),\r
+                               inst->cfg.winclass, inst->cfg.winclass);\r
+\r
+    /*\r
+     * Set up the colour map.\r
+     */\r
+    palette_reset(inst);\r
+\r
+    inst->width = inst->cfg.width;\r
+    inst->height = inst->cfg.height;\r
+\r
+    gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area),\r
+                         inst->font_width * inst->cfg.width + 2*inst->cfg.window_border,\r
+                         inst->font_height * inst->cfg.height + 2*inst->cfg.window_border);\r
+    inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0));\r
+    inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);\r
+    inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));\r
+    /*\r
+     * We always create the scrollbar; it remains invisible if\r
+     * unwanted, so we can pop it up quickly if it suddenly becomes\r
+     * desirable.\r
+     */\r
+    if (inst->cfg.scrollbar_on_left)\r
+        gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);\r
+    gtk_box_pack_start(inst->hbox, inst->area, TRUE, TRUE, 0);\r
+    if (!inst->cfg.scrollbar_on_left)\r
+        gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);\r
+\r
+    gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox));\r
+\r
+    set_geom_hints(inst);\r
+\r
+    gtk_widget_show(inst->area);\r
+    if (inst->cfg.scrollbar)\r
+       gtk_widget_show(inst->sbar);\r
+    else\r
+       gtk_widget_hide(inst->sbar);\r
+    gtk_widget_show(GTK_WIDGET(inst->hbox));\r
+\r
+    if (inst->gotpos) {\r
+        int x = inst->xpos, y = inst->ypos;\r
+        GtkRequisition req;\r
+        gtk_widget_size_request(GTK_WIDGET(inst->window), &req);\r
+        if (inst->gravity & 1) x += gdk_screen_width() - req.width;\r
+        if (inst->gravity & 2) y += gdk_screen_height() - req.height;\r
+       gtk_window_set_position(GTK_WINDOW(inst->window), GTK_WIN_POS_NONE);\r
+       gtk_widget_set_uposition(GTK_WIDGET(inst->window), x, y);\r
+    }\r
+\r
+    gtk_signal_connect(GTK_OBJECT(inst->window), "destroy",\r
+                      GTK_SIGNAL_FUNC(destroy), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->window), "delete_event",\r
+                      GTK_SIGNAL_FUNC(delete_window), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->window), "key_press_event",\r
+                      GTK_SIGNAL_FUNC(key_event), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->window), "key_release_event",\r
+                      GTK_SIGNAL_FUNC(key_event), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->window), "focus_in_event",\r
+                      GTK_SIGNAL_FUNC(focus_event), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->window), "focus_out_event",\r
+                      GTK_SIGNAL_FUNC(focus_event), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "configure_event",\r
+                      GTK_SIGNAL_FUNC(configure_area), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "expose_event",\r
+                      GTK_SIGNAL_FUNC(expose_area), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "button_press_event",\r
+                      GTK_SIGNAL_FUNC(button_event), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "button_release_event",\r
+                      GTK_SIGNAL_FUNC(button_event), inst);\r
+#if GTK_CHECK_VERSION(2,0,0)\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "scroll_event",\r
+                      GTK_SIGNAL_FUNC(scroll_event), inst);\r
+#endif\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "motion_notify_event",\r
+                      GTK_SIGNAL_FUNC(motion_event), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received",\r
+                      GTK_SIGNAL_FUNC(selection_received), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get",\r
+                      GTK_SIGNAL_FUNC(selection_get), inst);\r
+    gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",\r
+                      GTK_SIGNAL_FUNC(selection_clear), inst);\r
+    if (inst->cfg.scrollbar)\r
+       gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",\r
+                          GTK_SIGNAL_FUNC(scrollbar_moved), inst);\r
+    gtk_widget_add_events(GTK_WIDGET(inst->area),\r
+                         GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |\r
+                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |\r
+                         GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK);\r
+\r
+    {\r
+       extern const char *const *const main_icon[];\r
+       extern const int n_main_icon;\r
+       set_window_icon(inst->window, main_icon, n_main_icon);\r
+    }\r
+\r
+    gtk_widget_show(inst->window);\r
+\r
+    set_window_background(inst);\r
+\r
+    /*\r
+     * Set up the Ctrl+rightclick context menu.\r
+     */\r
+    {\r
+       GtkWidget *menuitem;\r
+       char *s;\r
+       extern const int use_event_log, new_session, saved_sessions;\r
+\r
+       inst->menu = gtk_menu_new();\r
+\r
+#define MKMENUITEM(title, func) do                                      \\r
+        {                                                               \\r
+            menuitem = gtk_menu_item_new_with_label(title);             \\r
+            gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \\r
+            gtk_widget_show(menuitem);                                  \\r
+            gtk_signal_connect(GTK_OBJECT(menuitem), "activate",        \\r
+                               GTK_SIGNAL_FUNC(func), inst);            \\r
+        } while (0)\r
+\r
+#define MKSUBMENU(title) do                                             \\r
+        {                                                               \\r
+            menuitem = gtk_menu_item_new_with_label(title);             \\r
+            gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \\r
+            gtk_widget_show(menuitem);                                  \\r
+        } while (0)\r
+\r
+#define MKSEP() do                                                      \\r
+        {                                                               \\r
+            menuitem = gtk_menu_item_new();                             \\r
+            gtk_container_add(GTK_CONTAINER(inst->menu), menuitem);     \\r
+            gtk_widget_show(menuitem);                                  \\r
+        } while (0)\r
+\r
+       if (new_session)\r
+           MKMENUITEM("New Session...", new_session_menuitem);\r
+        MKMENUITEM("Restart Session", restart_session_menuitem);\r
+       inst->restartitem = menuitem;\r
+       gtk_widget_set_sensitive(inst->restartitem, FALSE);\r
+        MKMENUITEM("Duplicate Session", dup_session_menuitem);\r
+       if (saved_sessions) {\r
+           inst->sessionsmenu = gtk_menu_new();\r
+           /* sessionsmenu will be updated when it's invoked */\r
+           /* XXX is this the right way to do dynamic menus in Gtk? */\r
+           MKMENUITEM("Saved Sessions", update_savedsess_menu);\r
+           gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem),\r
+                                     inst->sessionsmenu);\r
+       }\r
+       MKSEP();\r
+        MKMENUITEM("Change Settings...", change_settings_menuitem);\r
+       MKSEP();\r
+       if (use_event_log)\r
+           MKMENUITEM("Event Log", event_log_menuitem);\r
+       MKSUBMENU("Special Commands");\r
+       inst->specialsmenu = gtk_menu_new();\r
+       gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), inst->specialsmenu);\r
+       inst->specialsitem1 = menuitem;\r
+       MKSEP();\r
+       inst->specialsitem2 = menuitem;\r
+       gtk_widget_hide(inst->specialsitem1);\r
+       gtk_widget_hide(inst->specialsitem2);\r
+       MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem);\r
+       MKMENUITEM("Reset Terminal", reset_terminal_menuitem);\r
+       MKMENUITEM("Copy All", copy_all_menuitem);\r
+       MKSEP();\r
+       s = dupcat("About ", appname, NULL);\r
+       MKMENUITEM(s, about_menuitem);\r
+       sfree(s);\r
+#undef MKMENUITEM\r
+#undef MKSUBMENU\r
+#undef MKSEP\r
+    }\r
+\r
+    inst->textcursor = make_mouse_ptr(inst, GDK_XTERM);\r
+    inst->rawcursor = make_mouse_ptr(inst, GDK_LEFT_PTR);\r
+    inst->waitcursor = make_mouse_ptr(inst, GDK_WATCH);\r
+    inst->blankcursor = make_mouse_ptr(inst, -1);\r
+    make_mouse_ptr(inst, -2);         /* clean up cursor font */\r
+    inst->currcursor = inst->textcursor;\r
+    show_mouseptr(inst, 1);\r
+\r
+    inst->eventlogstuff = eventlogstuff_new();\r
+\r
+    inst->term = term_init(&inst->cfg, &inst->ucsdata, inst);\r
+    inst->logctx = log_init(inst, &inst->cfg);\r
+    term_provide_logctx(inst->term, inst->logctx);\r
+\r
+    uxsel_init();\r
+\r
+    term_size(inst->term, inst->cfg.height, inst->cfg.width, inst->cfg.savelines);\r
+\r
+    start_backend(inst);\r
+\r
+    ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */\r
+\r
+    /* now we're reday to deal with the child exit handler being\r
+     * called */\r
+    block_signal(SIGCHLD, 0);\r
+\r
+    /*\r
+     * Block SIGPIPE: if we attempt Duplicate Session or similar\r
+     * and it falls over in some way, we certainly don't want\r
+     * SIGPIPE terminating the main pterm/PuTTY. Note that we do\r
+     * this _after_ (at least pterm) forks off its child process,\r
+     * since the child wants SIGPIPE handled in the usual way.\r
+     */\r
+    block_signal(SIGPIPE, 1);\r
+\r
+    inst->exited = FALSE;\r
+\r
+    gtk_main();\r
+\r
+    return 0;\r
+}\r
diff --git a/putty/UNIX/MAKEFILE.GTK b/putty/UNIX/MAKEFILE.GTK
new file mode 100644 (file)
index 0000000..4809191
--- /dev/null
@@ -0,0 +1,889 @@
+# Makefile for putty under X/GTK and Unix.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=-DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=-DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=-DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=-DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=-DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=-DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=-DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=-DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=-DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=-DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=-DMSVC4 (Windows only)\r
+#  - RCFL=-DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON.\r
+#\r
+#  - RCFL=-DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=-DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=-DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=-DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=-DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+\r
+# You can define this path to point at your tools if you need to\r
+# TOOLPATH = /opt/gcc/bin\r
+CC = $(TOOLPATH)cc\r
+# If necessary set the path to krb5-config here\r
+KRB5CONFIG=krb5-config\r
+# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'\r
+# (depending on what works on your system) if you want to enforce\r
+# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0 x11'\r
+# if you want to enforce 2.0. The default is to try 2.0 and fall back\r
+# to 1.2 if it isn't found.\r
+GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 x11 $$0 2>/dev/null || gtk-config $$0'\r
+\r
+-include Makefile.local\r
+\r
+unexport CFLAGS # work around a weird issue with krb5-config\r
+\r
+CFLAGS = -O2 -Wall -Werror -g -I.././ -I../charset/ -I../windows/ -I../unix/ \\r
+               -I../macosx/ $(shell $(GTK_CONFIG) --cflags) -D _FILE_OFFSET_BITS=64\r
+XLDFLAGS = $(LDFLAGS) $(shell $(GTK_CONFIG) --libs)\r
+ULDFLAGS = $(LDFLAGS)\r
+ifeq (,$(findstring NO_GSSAPI,$(COMPAT)))\r
+ifeq (,$(findstring STATIC_GSSAPI,$(COMPAT)))\r
+XLDFLAGS+= -ldl\r
+ULDFLAGS+= -ldl\r
+else\r
+CFLAGS+= -DNO_LIBDL $(shell $(KRB5CONFIG) --cflags gssapi)\r
+XLDFLAGS+= $(shell $(KRB5CONFIG) --libs gssapi)\r
+ULDFLAGS+= $(shell $(KRB5CONFIG) --libs gssapi)\r
+endif\r
+endif\r
+INSTALL=install\r
+INSTALL_PROGRAM=$(INSTALL)\r
+INSTALL_DATA=$(INSTALL)\r
+prefix=/usr/local\r
+exec_prefix=$(prefix)\r
+bindir=$(exec_prefix)/bin\r
+mandir=$(prefix)/man\r
+man1dir=$(mandir)/man1\r
+\r
+\r
+.SUFFIXES:\r
+\r
+\r
+all: plink pscp psftp pterm putty puttygen puttytel\r
+\r
+plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \\r
+               uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \\r
+               uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \\r
+               wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \\r
+               settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \\r
+               sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \\r
+               sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \\r
+               sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \\r
+               ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \\r
+               uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) \r
+\r
+pscp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_none.o cmdline.o cproxy.o int64.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \\r
+               uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \\r
+               uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS)\r
+\r
+psftp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_none.o cmdline.o cproxy.o int64.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \\r
+               uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \\r
+               uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS)\r
+\r
+pterm: be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o gtkcols.o \\r
+               gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o localenc.o \\r
+               logging.o macenc.o mimeenc.o minibidi.o misc.o nocproxy.o \\r
+               nogss.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \\r
+               terminal.o time.o timing.o toucs.o tree234.o utf8.o uxcfg.o \\r
+               uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \\r
+               uxstore.o uxucs.o version.o wcwidth.o xenc.o xkeysym.o \\r
+               xpmptcfg.o xpmpterm.o\r
+       $(CC) -o $@ be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \\r
+               gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \\r
+               localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \\r
+               nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o settings.o \\r
+               slookup.o terminal.o time.o timing.o toucs.o tree234.o \\r
+               utf8.o uxcfg.o uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o \\r
+               uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \\r
+               xkeysym.o xpmptcfg.o xpmpterm.o $(XLDFLAGS) \r
+\r
+putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \\r
+               gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \\r
+               localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o \\r
+               sbcsdat.o sercfg.o settings.o slookup.o ssh.o sshaes.o \\r
+               ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \\r
+               sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \\r
+               sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o \\r
+               terminal.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \\r
+               uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \\r
+               uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \\r
+               uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \\r
+               xenc.o xkeysym.o xpmpucfg.o xpmputty.o\r
+       $(CC) -o $@ be_all_s.o cmdline.o config.o cproxy.o dialog.o \\r
+               fromucs.o gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o \\r
+               ldisc.o ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \\r
+               minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \\r
+               rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \\r
+               ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o telnet.o terminal.o time.o timing.o toucs.o \\r
+               tree234.o utf8.o ux_x11.o uxagentc.o uxcfg.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o uxputty.o \\r
+               uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \\r
+               wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \\r
+               xpmputty.o $(XLDFLAGS) \r
+\r
+puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \\r
+               sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \\r
+               sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \\r
+               tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \\r
+               version.o\r
+       $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \\r
+               sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \\r
+               time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \\r
+               uxstore.o version.o $(ULDFLAGS) \r
+\r
+puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \\r
+               gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \\r
+               localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \\r
+               nocproxy.o nogss.o pinger.o proxy.o raw.o rlogin.o sbcs.o \\r
+               sbcsdat.o sercfg.o settings.o slookup.o telnet.o terminal.o \\r
+               time.o timing.o toucs.o tree234.o utf8.o uxcfg.o uxmisc.o \\r
+               uxnet.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \\r
+               uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \\r
+               xkeysym.o xpmpucfg.o xpmputty.o\r
+       $(CC) -o $@ be_nos_s.o cmdline.o config.o dialog.o fromucs.o \\r
+               gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \\r
+               ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \\r
+               minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o raw.o \\r
+               rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \\r
+               telnet.o terminal.o time.o timing.o toucs.o tree234.o utf8.o \\r
+               uxcfg.o uxmisc.o uxnet.o uxprint.o uxproxy.o uxputty.o \\r
+               uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \\r
+               wcwidth.o xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS)\r
+\r
+be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c\r
+be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c\r
+be_nos_s.o: ../be_nos_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c\r
+cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c\r
+cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c\r
+config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c\r
+cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c\r
+dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c\r
+fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c\r
+gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c\r
+gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c\r
+gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkfont.h ../putty.h \\r
+               ../storage.h ../dialog.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c\r
+gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c\r
+gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkfont.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c\r
+import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c\r
+int64.o: ../int64.c ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../int64.c\r
+ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c\r
+ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldiscucs.c\r
+localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c\r
+logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c\r
+macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c\r
+mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c\r
+minibidi.o: ../minibidi.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c\r
+misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c\r
+nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c\r
+nogss.o: ../nogss.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c\r
+notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c\r
+osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \\r
+               ../tree234.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxctrls.m\r
+osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \\r
+               ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxdlg.m\r
+osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxmain.m\r
+osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxsel.m\r
+osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m\r
+pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c\r
+pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c\r
+portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c\r
+proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c\r
+pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c\r
+psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c\r
+raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c\r
+rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c\r
+sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c\r
+sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c\r
+sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c\r
+settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c\r
+sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c\r
+sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c\r
+slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \\r
+               ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c\r
+ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../sshgssc.h ../sshgss.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../pgssapi.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c\r
+sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c\r
+ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c\r
+sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c\r
+sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbn.c\r
+sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c\r
+sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c\r
+sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c\r
+sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c\r
+sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c\r
+sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c\r
+sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../puttyps.h \\r
+               ../network.h ../pgssapi.h ../sshgss.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c\r
+sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c\r
+sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c\r
+sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c\r
+sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c\r
+sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c\r
+sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c\r
+sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c\r
+sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c\r
+sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c\r
+sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c\r
+telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c\r
+terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \\r
+               ../misc.h ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c\r
+testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testback.c\r
+time.o: ../time.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c\r
+timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c\r
+toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c\r
+tree234.o: ../tree234.c ../puttymem.h ../tree234.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c\r
+utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c\r
+ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../puttyps.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c\r
+uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \\r
+               ../puttymem.h ../puttyps.h ../network.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c\r
+uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c\r
+uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c\r
+uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c\r
+uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c\r
+uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c\r
+uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c\r
+uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c\r
+uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c\r
+uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c\r
+uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c\r
+uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c\r
+uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c\r
+uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c\r
+uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c\r
+uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c\r
+uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c\r
+uxsignal.o: ../unix/uxsignal.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c\r
+uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c\r
+uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \\r
+               ../misc.h ../puttyps.h ../network.h ../tree234.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c\r
+wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c\r
+wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c\r
+wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c\r
+wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c\r
+winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \\r
+               ../puttyps.h ../network.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c\r
+windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c\r
+windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \\r
+               ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \\r
+               ../puttymem.h ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c\r
+window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \\r
+               ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c\r
+wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \\r
+               ../sshgssc.h ../misc.h ../puttyps.h ../network.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c\r
+winhandl.o: ../windows/winhandl.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c\r
+winhelp.o: ../windows/winhelp.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c\r
+winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c\r
+winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c\r
+winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c\r
+winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c\r
+winnojmp.o: ../windows/winnojmp.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c\r
+winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c\r
+winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c\r
+winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c\r
+winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c\r
+winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c\r
+winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \\r
+               ../proxy.h ../puttyps.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c\r
+winser.o: ../windows/winser.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c\r
+winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c\r
+winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c\r
+wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c\r
+winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \\r
+               ../puttyps.h ../network.h ../tree234.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c\r
+winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c\r
+winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c\r
+x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c\r
+xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c\r
+xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c\r
+xpmptcfg.o: ../unix/xpmptcfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c\r
+xpmpterm.o: ../unix/xpmpterm.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c\r
+xpmpucfg.o: ../unix/xpmpucfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c\r
+xpmputty.o: ../unix/xpmputty.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c\r
+\r
+version.o: FORCE\r
+       if test -z "$(VER)" && (cd ..; md5sum -c manifest); then \\r
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat ../version.def` -c ../version.c; \\r
+       else \\r
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c; \\r
+       fi\r
+install:\r
+       mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir)\r
+       $(INSTALL_PROGRAM) -m 755 plink $(DESTDIR)$(bindir)/plink\r
+       $(INSTALL_PROGRAM) -m 755 pscp $(DESTDIR)$(bindir)/pscp\r
+       $(INSTALL_PROGRAM) -m 755 psftp $(DESTDIR)$(bindir)/psftp\r
+       $(INSTALL_PROGRAM) -m 755 pterm $(DESTDIR)$(bindir)/pterm\r
+       if test -n "$(UTMP_GROUP)"; then \\r
+         chgrp $(UTMP_GROUP) $(DESTDIR)$(bindir)/pterm && \\r
+           chmod 2755 $(DESTDIR)$(bindir)/pterm; \\r
+       elif test -n "$(UTMP_USER)"; then \\r
+         chown $(UTMP_USER) $(DESTDIR)$(bindir)/pterm && \\r
+           chmod 4755 $(DESTDIR)$(bindir)/pterm; \\r
+       fi\r
+       $(INSTALL_PROGRAM) -m 755 putty $(DESTDIR)$(bindir)/putty\r
+       $(INSTALL_PROGRAM) -m 755 puttygen $(DESTDIR)$(bindir)/puttygen\r
+       $(INSTALL_PROGRAM) -m 755 puttytel $(DESTDIR)$(bindir)/puttytel\r
+       $(INSTALL_DATA) -m 644 ../doc/plink.1 $(DESTDIR)$(man1dir)/plink.1\r
+       $(INSTALL_DATA) -m 644 ../doc/pscp.1 $(DESTDIR)$(man1dir)/pscp.1\r
+       $(INSTALL_DATA) -m 644 ../doc/psftp.1 $(DESTDIR)$(man1dir)/psftp.1\r
+       $(INSTALL_DATA) -m 644 ../doc/pterm.1 $(DESTDIR)$(man1dir)/pterm.1\r
+       $(INSTALL_DATA) -m 644 ../doc/putty.1 $(DESTDIR)$(man1dir)/putty.1\r
+       $(INSTALL_DATA) -m 644 ../doc/puttygen.1 $(DESTDIR)$(man1dir)/puttygen.1\r
+       $(INSTALL_DATA) -m 644 ../doc/puttytel.1 $(DESTDIR)$(man1dir)/puttytel.1\r
+\r
+install-strip:\r
+       $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s"\r
+\r
+clean:\r
+       rm -f *.o plink pscp psftp pterm putty puttygen puttytel\r
+\r
+FORCE:\r
diff --git a/putty/UNIX/MAKEFILE.IN b/putty/UNIX/MAKEFILE.IN
new file mode 100644 (file)
index 0000000..8893846
--- /dev/null
@@ -0,0 +1,872 @@
+# Makefile.in for putty under Unix with Autoconf.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=-DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=-DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=-DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=-DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=-DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=-DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=-DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=-DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=-DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=-DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=-DMSVC4 (Windows only)\r
+#  - RCFL=-DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON.\r
+#\r
+#  - RCFL=-DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=-DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=-DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=-DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=-DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+\r
+CC = @CC@\r
+\r
+CFLAGS = @CFLAGS@ @PUTTYCFLAGS@ @CPPFLAGS@ @DEFS@ @GTK_CFLAGS@ -I.././ \\r
+               -I../charset/ -I../windows/ -I../unix/ -I../macosx/\r
+XLDFLAGS = @LDFLAGS@ @LIBS@ @GTK_LIBS@\r
+ULDFLAGS = @LDFLAGS@ @LIBS@\r
+INSTALL=@INSTALL@\r
+INSTALL_PROGRAM=$(INSTALL)\r
+INSTALL_DATA=$(INSTALL)\r
+prefix=@prefix@\r
+exec_prefix=@exec_prefix@\r
+bindir=@bindir@\r
+datarootdir=@datarootdir@\r
+mandir=@mandir@\r
+man1dir=$(mandir)/man1\r
+\r
+\r
+.SUFFIXES:\r
+\r
+\r
+all: @all_targets@\r
+all-cli: plink pscp psftp puttygen\r
+all-gtk: pterm putty puttytel\r
+\r
+plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \\r
+               uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \\r
+               uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \\r
+               wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \\r
+               settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \\r
+               sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \\r
+               sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \\r
+               sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \\r
+               ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \\r
+               uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) \r
+\r
+pscp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_none.o cmdline.o cproxy.o int64.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \\r
+               uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \\r
+               uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS)\r
+\r
+psftp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_none.o cmdline.o cproxy.o int64.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \\r
+               uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \\r
+               uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS)\r
+\r
+pterm: be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o gtkcols.o \\r
+               gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o localenc.o \\r
+               logging.o macenc.o mimeenc.o minibidi.o misc.o nocproxy.o \\r
+               nogss.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \\r
+               terminal.o time.o timing.o toucs.o tree234.o utf8.o uxcfg.o \\r
+               uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \\r
+               uxstore.o uxucs.o version.o wcwidth.o xenc.o xkeysym.o \\r
+               xpmptcfg.o xpmpterm.o\r
+       $(CC) -o $@ be_none.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \\r
+               gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \\r
+               localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \\r
+               nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o settings.o \\r
+               slookup.o terminal.o time.o timing.o toucs.o tree234.o \\r
+               utf8.o uxcfg.o uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o \\r
+               uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \\r
+               xkeysym.o xpmptcfg.o xpmpterm.o $(XLDFLAGS) \r
+\r
+putty: be_all_s.o cmdline.o config.o cproxy.o dialog.o fromucs.o gtkcfg.o \\r
+               gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \\r
+               localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o sbcs.o \\r
+               sbcsdat.o sercfg.o settings.o slookup.o ssh.o sshaes.o \\r
+               ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o \\r
+               sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o sshrand.o \\r
+               sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o telnet.o \\r
+               terminal.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \\r
+               uxagentc.o uxcfg.o uxgss.o uxmisc.o uxnet.o uxnoise.o \\r
+               uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \\r
+               uxstore.o uxucs.o version.o wcwidth.o wildcard.o x11fwd.o \\r
+               xenc.o xkeysym.o xpmpucfg.o xpmputty.o\r
+       $(CC) -o $@ be_all_s.o cmdline.o config.o cproxy.o dialog.o \\r
+               fromucs.o gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o \\r
+               ldisc.o ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \\r
+               minibidi.o misc.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \\r
+               rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \\r
+               ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o telnet.o terminal.o time.o timing.o toucs.o \\r
+               tree234.o utf8.o ux_x11.o uxagentc.o uxcfg.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o uxputty.o \\r
+               uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \\r
+               wcwidth.o wildcard.o x11fwd.o xenc.o xkeysym.o xpmpucfg.o \\r
+               xpmputty.o $(XLDFLAGS) \r
+\r
+puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \\r
+               sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \\r
+               sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \\r
+               tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \\r
+               version.o\r
+       $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \\r
+               sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \\r
+               time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \\r
+               uxstore.o version.o $(ULDFLAGS) \r
+\r
+puttytel: be_nos_s.o cmdline.o config.o dialog.o fromucs.o gtkcfg.o \\r
+               gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o ldiscucs.o \\r
+               localenc.o logging.o macenc.o mimeenc.o minibidi.o misc.o \\r
+               nocproxy.o nogss.o pinger.o proxy.o raw.o rlogin.o sbcs.o \\r
+               sbcsdat.o sercfg.o settings.o slookup.o telnet.o terminal.o \\r
+               time.o timing.o toucs.o tree234.o utf8.o uxcfg.o uxmisc.o \\r
+               uxnet.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \\r
+               uxsignal.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \\r
+               xkeysym.o xpmpucfg.o xpmputty.o\r
+       $(CC) -o $@ be_nos_s.o cmdline.o config.o dialog.o fromucs.o \\r
+               gtkcfg.o gtkcols.o gtkdlg.o gtkfont.o gtkwin.o ldisc.o \\r
+               ldiscucs.o localenc.o logging.o macenc.o mimeenc.o \\r
+               minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o raw.o \\r
+               rlogin.o sbcs.o sbcsdat.o sercfg.o settings.o slookup.o \\r
+               telnet.o terminal.o time.o timing.o toucs.o tree234.o utf8.o \\r
+               uxcfg.o uxmisc.o uxnet.o uxprint.o uxproxy.o uxputty.o \\r
+               uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o version.o \\r
+               wcwidth.o xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS)\r
+\r
+be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c\r
+be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c\r
+be_nos_s.o: ../be_nos_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c\r
+cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c\r
+cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c\r
+config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c\r
+cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c\r
+dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c\r
+fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c\r
+gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c\r
+gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c\r
+gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkfont.h ../putty.h \\r
+               ../storage.h ../dialog.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c\r
+gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c\r
+gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkfont.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c\r
+import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c\r
+int64.o: ../int64.c ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../int64.c\r
+ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c\r
+ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldiscucs.c\r
+localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c\r
+logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c\r
+macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c\r
+mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c\r
+minibidi.o: ../minibidi.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c\r
+misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c\r
+nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c\r
+nogss.o: ../nogss.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c\r
+notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c\r
+osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \\r
+               ../tree234.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxctrls.m\r
+osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \\r
+               ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxdlg.m\r
+osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxmain.m\r
+osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxsel.m\r
+osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m\r
+pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c\r
+pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c\r
+portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c\r
+proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c\r
+pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c\r
+psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c\r
+raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c\r
+rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c\r
+sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c\r
+sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c\r
+sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c\r
+settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c\r
+sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c\r
+sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c\r
+slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \\r
+               ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c\r
+ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../sshgssc.h ../sshgss.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../pgssapi.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c\r
+sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c\r
+ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c\r
+sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c\r
+sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbn.c\r
+sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c\r
+sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c\r
+sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c\r
+sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c\r
+sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c\r
+sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c\r
+sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../puttyps.h \\r
+               ../network.h ../pgssapi.h ../sshgss.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c\r
+sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c\r
+sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c\r
+sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c\r
+sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c\r
+sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c\r
+sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c\r
+sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c\r
+sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c\r
+sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c\r
+sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c\r
+telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c\r
+terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \\r
+               ../misc.h ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c\r
+testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testback.c\r
+time.o: ../time.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c\r
+timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c\r
+toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c\r
+tree234.o: ../tree234.c ../puttymem.h ../tree234.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c\r
+utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c\r
+ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../puttyps.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c\r
+uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \\r
+               ../puttymem.h ../puttyps.h ../network.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c\r
+uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c\r
+uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c\r
+uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c\r
+uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c\r
+uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c\r
+uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c\r
+uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c\r
+uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c\r
+uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c\r
+uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c\r
+uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c\r
+uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c\r
+uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c\r
+uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c\r
+uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c\r
+uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c\r
+uxsignal.o: ../unix/uxsignal.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c\r
+uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c\r
+uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \\r
+               ../misc.h ../puttyps.h ../network.h ../tree234.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c\r
+wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c\r
+wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c\r
+wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c\r
+wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c\r
+winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \\r
+               ../puttyps.h ../network.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c\r
+windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c\r
+windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \\r
+               ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \\r
+               ../puttymem.h ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c\r
+window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \\r
+               ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c\r
+wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \\r
+               ../sshgssc.h ../misc.h ../puttyps.h ../network.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c\r
+winhandl.o: ../windows/winhandl.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c\r
+winhelp.o: ../windows/winhelp.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c\r
+winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c\r
+winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c\r
+winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c\r
+winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c\r
+winnojmp.o: ../windows/winnojmp.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c\r
+winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c\r
+winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c\r
+winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c\r
+winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c\r
+winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c\r
+winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \\r
+               ../proxy.h ../puttyps.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c\r
+winser.o: ../windows/winser.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c\r
+winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c\r
+winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c\r
+wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c\r
+winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \\r
+               ../puttyps.h ../network.h ../tree234.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c\r
+winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c\r
+winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c\r
+x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c\r
+xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c\r
+xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c\r
+xpmptcfg.o: ../unix/xpmptcfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c\r
+xpmpterm.o: ../unix/xpmpterm.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c\r
+xpmpucfg.o: ../unix/xpmpucfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c\r
+xpmputty.o: ../unix/xpmputty.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c\r
+\r
+version.o: FORCE\r
+       if test -z "$(VER)" && (cd ..; md5sum -c manifest); then \\r
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat ../version.def` -c ../version.c; \\r
+       else \\r
+               $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c; \\r
+       fi\r
+install:\r
+       mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir)\r
+       $(INSTALL_PROGRAM) -m 755 plink $(DESTDIR)$(bindir)/plink\r
+       $(INSTALL_PROGRAM) -m 755 pscp $(DESTDIR)$(bindir)/pscp\r
+       $(INSTALL_PROGRAM) -m 755 psftp $(DESTDIR)$(bindir)/psftp\r
+       $(INSTALL_PROGRAM) -m 755 pterm $(DESTDIR)$(bindir)/pterm\r
+       if test -n "$(UTMP_GROUP)"; then \\r
+         chgrp $(UTMP_GROUP) $(DESTDIR)$(bindir)/pterm && \\r
+           chmod 2755 $(DESTDIR)$(bindir)/pterm; \\r
+       elif test -n "$(UTMP_USER)"; then \\r
+         chown $(UTMP_USER) $(DESTDIR)$(bindir)/pterm && \\r
+           chmod 4755 $(DESTDIR)$(bindir)/pterm; \\r
+       fi\r
+       $(INSTALL_PROGRAM) -m 755 putty $(DESTDIR)$(bindir)/putty\r
+       $(INSTALL_PROGRAM) -m 755 puttygen $(DESTDIR)$(bindir)/puttygen\r
+       $(INSTALL_PROGRAM) -m 755 puttytel $(DESTDIR)$(bindir)/puttytel\r
+       $(INSTALL_DATA) -m 644 ../doc/plink.1 $(DESTDIR)$(man1dir)/plink.1\r
+       $(INSTALL_DATA) -m 644 ../doc/pscp.1 $(DESTDIR)$(man1dir)/pscp.1\r
+       $(INSTALL_DATA) -m 644 ../doc/psftp.1 $(DESTDIR)$(man1dir)/psftp.1\r
+       $(INSTALL_DATA) -m 644 ../doc/pterm.1 $(DESTDIR)$(man1dir)/pterm.1\r
+       $(INSTALL_DATA) -m 644 ../doc/putty.1 $(DESTDIR)$(man1dir)/putty.1\r
+       $(INSTALL_DATA) -m 644 ../doc/puttygen.1 $(DESTDIR)$(man1dir)/puttygen.1\r
+       $(INSTALL_DATA) -m 644 ../doc/puttytel.1 $(DESTDIR)$(man1dir)/puttytel.1\r
+\r
+install-strip:\r
+       $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s"\r
+\r
+clean:\r
+       rm -f *.o plink pscp psftp pterm putty puttygen puttytel\r
+\r
+distclean: clean\r
+       rm -f config.status config.cache config.log configure.lineno \\r
+               config.status.lineno Makefile\r
+\r
+FORCE:\r
diff --git a/putty/UNIX/MAKEFILE.UX b/putty/UNIX/MAKEFILE.UX
new file mode 100644 (file)
index 0000000..ac582b3
--- /dev/null
@@ -0,0 +1,776 @@
+# Makefile for putty under Unix.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=-DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=-DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=-DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=-DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=-DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=-DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=-DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=-DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=-DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=-DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=-DMSVC4 (Windows only)\r
+#  - RCFL=-DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON.\r
+#\r
+#  - RCFL=-DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=-DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=-DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=-DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=-DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+\r
+# You can define this path to point at your tools if you need to\r
+# TOOLPATH = /opt/gcc/bin\r
+CC = $(TOOLPATH)cc\r
+\r
+-include Makefile.local\r
+\r
+unexport CFLAGS # work around a weird issue with krb5-config\r
+\r
+CFLAGS = -O2 -Wall -Werror -g -I.././ -I../charset/ -I../windows/ -I../unix/ \\r
+               -I../macosx/ -D _FILE_OFFSET_BITS=64\r
+ULDFLAGS = $(LDFLAGS)\r
+INSTALL=install\r
+INSTALL_PROGRAM=$(INSTALL)\r
+INSTALL_DATA=$(INSTALL)\r
+prefix=/usr/local\r
+exec_prefix=$(prefix)\r
+bindir=$(exec_prefix)/bin\r
+mandir=$(prefix)/man\r
+man1dir=$(mandir)/man1\r
+\r
+\r
+.SUFFIXES:\r
+\r
+\r
+all: plink pscp psftp puttygen\r
+\r
+plink: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               telnet.o time.o timing.o tree234.o ux_x11.o uxagentc.o \\r
+               uxcons.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxplink.o \\r
+               uxproxy.o uxsel.o uxser.o uxsignal.o uxstore.o version.o \\r
+               wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \\r
+               settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \\r
+               sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \\r
+               sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \\r
+               sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \\r
+               ux_x11.o uxagentc.o uxcons.o uxgss.o uxmisc.o uxnet.o \\r
+               uxnoise.o uxplink.o uxproxy.o uxsel.o uxser.o uxsignal.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS) \r
+\r
+pscp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_none.o cmdline.o cproxy.o int64.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o pscp.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \\r
+               uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \\r
+               uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS)\r
+\r
+psftp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               time.o timing.o tree234.o uxagentc.o uxcons.o uxgss.o \\r
+               uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o uxsftp.o \\r
+               uxstore.o version.o wildcard.o x11fwd.o\r
+       $(CC) -o $@ be_none.o cmdline.o cproxy.o int64.o logging.o misc.o \\r
+               pgssapi.o pinger.o portfwd.o proxy.o psftp.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o time.o timing.o tree234.o uxagentc.o uxcons.o \\r
+               uxgss.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \\r
+               uxsftp.o uxstore.o version.o wildcard.o x11fwd.o $(ULDFLAGS)\r
+\r
+puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \\r
+               sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \\r
+               sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o time.o \\r
+               tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o \\r
+               version.o\r
+       $(CC) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o \\r
+               sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \\r
+               time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \\r
+               uxstore.o version.o $(ULDFLAGS) \r
+\r
+be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c\r
+be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c\r
+be_nos_s.o: ../be_nos_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c\r
+cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c\r
+cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c\r
+config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c\r
+cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c\r
+dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c\r
+fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c\r
+gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c\r
+gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c\r
+gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkfont.h ../putty.h \\r
+               ../storage.h ../dialog.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c\r
+gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c\r
+gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkfont.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c\r
+import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c\r
+int64.o: ../int64.c ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../int64.c\r
+ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c\r
+ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldiscucs.c\r
+localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c\r
+logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c\r
+macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c\r
+mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c\r
+minibidi.o: ../minibidi.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c\r
+misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c\r
+nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c\r
+nogss.o: ../nogss.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c\r
+notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c\r
+osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \\r
+               ../tree234.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxctrls.m\r
+osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \\r
+               ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxdlg.m\r
+osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxmain.m\r
+osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxsel.m\r
+osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m\r
+pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c\r
+pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c\r
+portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c\r
+proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c\r
+pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c\r
+psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c\r
+raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c\r
+rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c\r
+sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c\r
+sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c\r
+sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c\r
+settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c\r
+sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c\r
+sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c\r
+slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \\r
+               ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c\r
+ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../sshgssc.h ../sshgss.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../pgssapi.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c\r
+sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c\r
+ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c\r
+sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c\r
+sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbn.c\r
+sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c\r
+sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c\r
+sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c\r
+sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c\r
+sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c\r
+sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c\r
+sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../puttyps.h \\r
+               ../network.h ../pgssapi.h ../sshgss.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c\r
+sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c\r
+sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c\r
+sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c\r
+sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c\r
+sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c\r
+sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c\r
+sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c\r
+sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c\r
+sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c\r
+sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c\r
+telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c\r
+terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \\r
+               ../misc.h ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c\r
+testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testback.c\r
+time.o: ../time.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c\r
+timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c\r
+toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c\r
+tree234.o: ../tree234.c ../puttymem.h ../tree234.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c\r
+utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c\r
+ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../puttyps.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c\r
+uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \\r
+               ../puttymem.h ../puttyps.h ../network.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c\r
+uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c\r
+uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c\r
+uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c\r
+uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c\r
+uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c\r
+uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c\r
+uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c\r
+uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c\r
+uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c\r
+uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c\r
+uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c\r
+uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c\r
+uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c\r
+uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c\r
+uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c\r
+uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c\r
+uxsignal.o: ../unix/uxsignal.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c\r
+uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c\r
+uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \\r
+               ../misc.h ../puttyps.h ../network.h ../tree234.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c\r
+version.o: ../version.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../version.c\r
+wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c\r
+wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c\r
+wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c\r
+wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c\r
+winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \\r
+               ../puttyps.h ../network.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c\r
+windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c\r
+windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \\r
+               ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \\r
+               ../puttymem.h ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c\r
+window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \\r
+               ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c\r
+wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \\r
+               ../sshgssc.h ../misc.h ../puttyps.h ../network.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c\r
+winhandl.o: ../windows/winhandl.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c\r
+winhelp.o: ../windows/winhelp.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c\r
+winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c\r
+winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c\r
+winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c\r
+winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c\r
+winnojmp.o: ../windows/winnojmp.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c\r
+winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c\r
+winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c\r
+winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c\r
+winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c\r
+winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c\r
+winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \\r
+               ../proxy.h ../puttyps.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c\r
+winser.o: ../windows/winser.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c\r
+winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c\r
+winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c\r
+wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c\r
+winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \\r
+               ../puttyps.h ../network.h ../tree234.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c\r
+winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c\r
+winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c\r
+x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c\r
+xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c\r
+xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c\r
+xpmptcfg.o: ../unix/xpmptcfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c\r
+xpmpterm.o: ../unix/xpmpterm.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c\r
+xpmpucfg.o: ../unix/xpmpucfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c\r
+xpmputty.o: ../unix/xpmputty.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c\r
+\r
+\r
+clean:\r
+       rm -f *.o plink pscp psftp puttygen\r
+\r
+FORCE:\r
diff --git a/putty/UNIX/UNIX.H b/putty/UNIX/UNIX.H
new file mode 100644 (file)
index 0000000..198085f
--- /dev/null
@@ -0,0 +1,186 @@
+#ifndef PUTTY_UNIX_H\r
+#define PUTTY_UNIX_H\r
+\r
+#ifdef HAVE_CONFIG_H\r
+# include "uxconfig.h" /* Space to hide it from mkfiles.pl */\r
+#endif\r
+\r
+#include <stdio.h>                    /* for FILENAME_MAX */\r
+#include <stdint.h>                   /* C99 int types */\r
+#ifndef NO_LIBDL\r
+#include <dlfcn.h>                    /* Dynamic library loading */\r
+#endif /*  NO_LIBDL */\r
+#include "charset.h"\r
+\r
+struct Filename {\r
+    char path[FILENAME_MAX];\r
+};\r
+FILE *f_open(struct Filename, char const *, int);\r
+\r
+struct FontSpec {\r
+    char name[256];\r
+};\r
+\r
+typedef void *Context;                 /* FIXME: probably needs changing */\r
+\r
+typedef int OSSocket;\r
+#define OSSOCKET_DEFINED              /* stop network.h using its default */\r
+\r
+extern Backend pty_backend;\r
+\r
+typedef uint32_t uint32; /* C99: uint32_t defined in stdint.h */\r
+#define PUTTY_UINT32_DEFINED\r
+\r
+/*\r
+ * Under GTK, we send MA_CLICK _and_ MA_2CLK, or MA_CLICK _and_\r
+ * MA_3CLK, when a button is pressed for the second or third time.\r
+ */\r
+#define MULTICLICK_ONLY_EVENT 0\r
+\r
+/*\r
+ * Under GTK, there is no context help available.\r
+ */\r
+#define HELPCTX(x) P(NULL)\r
+#define FILTER_KEY_FILES NULL          /* FIXME */\r
+#define FILTER_DYNLIB_FILES NULL       /* FIXME */\r
+\r
+/*\r
+ * Under X, selection data must not be NUL-terminated.\r
+ */\r
+#define SELECTION_NUL_TERMINATED 0\r
+\r
+/*\r
+ * Under X, copying to the clipboard terminates lines with just LF.\r
+ */\r
+#define SEL_NL { 10 }\r
+\r
+/* Simple wraparound timer function */\r
+unsigned long getticks(void);         /* based on gettimeofday(2) */\r
+#define GETTICKCOUNT getticks\r
+#define TICKSPERSEC    1000           /* we choose to use milliseconds */\r
+#define CURSORBLINK     450           /* no standard way to set this */\r
+/* getticks() works using gettimeofday(), so it's vulnerable to system clock\r
+ * changes causing chaos. Therefore, we provide a compensation mechanism. */\r
+#define TIMING_SYNC\r
+#define TIMING_SYNC_ANOW\r
+extern long tickcount_offset;\r
+\r
+#define WCHAR wchar_t\r
+#define BYTE unsigned char\r
+\r
+/*\r
+ * Unix-specific global flag\r
+ *\r
+ * FLAG_STDERR_TTY indicates that standard error might be a terminal and\r
+ * might get its configuration munged, so anything trying to output plain\r
+ * text (i.e. with newlines in it) will need to put it back into cooked\r
+ * mode first.  Applications setting this flag should also call\r
+ * stderr_tty_init() before messing with any terminal modes, and can call\r
+ * premsg() before outputting text to stderr and postmsg() afterwards.\r
+ */\r
+#define FLAG_STDERR_TTY 0x1000\r
+\r
+/* Things pty.c needs from pterm.c */\r
+char *get_x_display(void *frontend);\r
+int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */\r
+long get_windowid(void *frontend);\r
+\r
+/* Things gtkdlg.c needs from pterm.c */\r
+void *get_window(void *frontend);      /* void * to avoid depending on gtk.h */\r
+\r
+/* Things pterm.c needs from gtkdlg.c */\r
+int do_config_box(const char *title, Config *cfg,\r
+                 int midsession, int protcfginfo);\r
+void fatal_message_box(void *window, char *msg);\r
+void about_box(void *window);\r
+void *eventlogstuff_new(void);\r
+void showeventlog(void *estuff, void *parentwin);\r
+void logevent_dlg(void *estuff, const char *string);\r
+int reallyclose(void *frontend);\r
+\r
+/* Things pterm.c needs from {ptermm,uxputty}.c */\r
+char *make_default_wintitle(char *hostname);\r
+int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch);\r
+\r
+/* pterm.c needs this special function in xkeysym.c */\r
+int keysym_to_unicode(int keysym);\r
+\r
+/* Things uxstore.c needs from pterm.c */\r
+char *x_get_default(const char *key);\r
+\r
+/* Things uxstore.c provides to pterm.c */\r
+void provide_xrm_string(char *string);\r
+\r
+/* Things provided by uxcons.c */\r
+struct termios;\r
+void stderr_tty_init(void);\r
+void premsg(struct termios *);\r
+void postmsg(struct termios *);\r
+\r
+/* The interface used by uxsel.c */\r
+void uxsel_init(void);\r
+typedef int (*uxsel_callback_fn)(int fd, int event);\r
+void uxsel_set(int fd, int rwx, uxsel_callback_fn callback);\r
+void uxsel_del(int fd);\r
+int select_result(int fd, int event);\r
+int first_fd(int *state, int *rwx);\r
+int next_fd(int *state, int *rwx);\r
+/* The following are expected to be provided _to_ uxsel.c by the frontend */\r
+int uxsel_input_add(int fd, int rwx);  /* returns an id */\r
+void uxsel_input_remove(int id);\r
+\r
+/* uxcfg.c */\r
+struct controlbox;\r
+void unix_setup_config_box(struct controlbox *b, int midsession, int protocol);\r
+\r
+/* gtkcfg.c */\r
+void gtk_setup_config_box(struct controlbox *b, int midsession, void *window);\r
+\r
+/*\r
+ * In the Unix Unicode layer, DEFAULT_CODEPAGE is a special value\r
+ * which causes mb_to_wc and wc_to_mb to call _libc_ rather than\r
+ * libcharset. That way, we can interface the various charsets\r
+ * supported by libcharset with the one supported by mbstowcs and\r
+ * wcstombs (which will be the character set in which stuff read\r
+ * from the command line or config files is assumed to be encoded).\r
+ */\r
+#define DEFAULT_CODEPAGE 0xFFFF\r
+#define CP_UTF8 CS_UTF8                       /* from libcharset */\r
+\r
+#define strnicmp strncasecmp\r
+#define stricmp strcasecmp\r
+\r
+/* BSD-semantics version of signal(), and another helpful function */\r
+void (*putty_signal(int sig, void (*func)(int)))(int);\r
+void block_signal(int sig, int block_it);\r
+\r
+/* uxmisc.c */\r
+int cloexec(int);\r
+\r
+/*\r
+ * Exports from unicode.c.\r
+ */\r
+struct unicode_data;\r
+int init_ucs(struct unicode_data *ucsdata, char *line_codepage,\r
+            int utf8_override, int font_charset, int vtmode);\r
+\r
+/*\r
+ * Spare function exported directly from uxnet.c.\r
+ */\r
+void *sk_getxdmdata(void *sock, int *lenp);\r
+\r
+/*\r
+ * General helpful Unix stuff: more helpful version of the FD_SET\r
+ * macro, which also handles maxfd.\r
+ */\r
+#define FD_SET_MAX(fd, max, set) do { \\r
+    FD_SET(fd, &set); \\r
+    if (max < fd + 1) max = fd + 1; \\r
+} while (0)\r
+\r
+/*\r
+ * Exports from winser.c.\r
+ */\r
+extern Backend serial_backend;\r
+\r
+#endif\r
diff --git a/putty/UNIX/UXAGENTC.C b/putty/UNIX/UXAGENTC.C
new file mode 100644 (file)
index 0000000..74cd9cd
--- /dev/null
@@ -0,0 +1,162 @@
+/*\r
+ * SSH agent client code.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <unistd.h>\r
+#include <sys/socket.h>\r
+#include <sys/un.h>\r
+#include <fcntl.h>\r
+\r
+#include "putty.h"\r
+#include "misc.h"\r
+#include "tree234.h"\r
+#include "puttymem.h"\r
+\r
+int agent_exists(void)\r
+{\r
+    if (getenv("SSH_AUTH_SOCK") != NULL)\r
+       return TRUE;\r
+    return FALSE;\r
+}\r
+\r
+static tree234 *agent_connections;\r
+struct agent_connection {\r
+    int fd;\r
+    char *retbuf;\r
+    char sizebuf[4];\r
+    int retsize, retlen;\r
+    void (*callback)(void *, void *, int);\r
+    void *callback_ctx;\r
+};\r
+static int agent_conncmp(void *av, void *bv)\r
+{\r
+    struct agent_connection *a = (struct agent_connection *) av;\r
+    struct agent_connection *b = (struct agent_connection *) bv;\r
+    if (a->fd < b->fd)\r
+       return -1;\r
+    if (a->fd > b->fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+static int agent_connfind(void *av, void *bv)\r
+{\r
+    int afd = *(int *) av;\r
+    struct agent_connection *b = (struct agent_connection *) bv;\r
+    if (afd < b->fd)\r
+       return -1;\r
+    if (afd > b->fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int agent_select_result(int fd, int event)\r
+{\r
+    int ret;\r
+    struct agent_connection *conn;\r
+\r
+    assert(event == 1);                       /* not selecting for anything but R */\r
+\r
+    conn = find234(agent_connections, &fd, agent_connfind);\r
+    if (!conn) {\r
+       uxsel_del(fd);\r
+       return 1;\r
+    }\r
+\r
+    ret = read(fd, conn->retbuf+conn->retlen, conn->retsize-conn->retlen);\r
+    if (ret <= 0) {\r
+       if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf);\r
+       conn->retbuf = NULL;\r
+       conn->retlen = 0;\r
+       goto done;\r
+    }\r
+    conn->retlen += ret;\r
+    if (conn->retsize == 4 && conn->retlen == 4) {\r
+       conn->retsize = GET_32BIT(conn->retbuf);\r
+       if (conn->retsize <= 0) {\r
+           conn->retbuf = NULL;\r
+           conn->retlen = 0;\r
+           goto done;\r
+       }\r
+       conn->retsize += 4;\r
+       assert(conn->retbuf == conn->sizebuf);\r
+       conn->retbuf = snewn(conn->retsize, char);\r
+       memcpy(conn->retbuf, conn->sizebuf, 4);\r
+    }\r
+\r
+    if (conn->retlen < conn->retsize)\r
+       return 0;                      /* more data to come */\r
+\r
+    done:\r
+    /*\r
+     * We have now completed the agent query. Do the callback, and\r
+     * clean up. (Of course we don't free retbuf, since ownership\r
+     * of that passes to the callback.)\r
+     */\r
+    conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);\r
+    uxsel_del(fd);\r
+    close(fd);\r
+    del234(agent_connections, conn);\r
+    sfree(conn);\r
+    return 0;\r
+}\r
+\r
+int agent_query(void *in, int inlen, void **out, int *outlen,\r
+               void (*callback)(void *, void *, int), void *callback_ctx)\r
+{\r
+    char *name;\r
+    int sock;\r
+    struct sockaddr_un addr;\r
+    int done;\r
+    struct agent_connection *conn;\r
+\r
+    name = getenv("SSH_AUTH_SOCK");\r
+    if (!name)\r
+       goto failure;\r
+\r
+    sock = socket(PF_UNIX, SOCK_STREAM, 0);\r
+    if (sock < 0) {\r
+       perror("socket(PF_UNIX)");\r
+       exit(1);\r
+    }\r
+\r
+    cloexec(sock);\r
+\r
+    addr.sun_family = AF_UNIX;\r
+    strncpy(addr.sun_path, name, sizeof(addr.sun_path));\r
+    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {\r
+       close(sock);\r
+       goto failure;\r
+    }\r
+\r
+    for (done = 0; done < inlen ;) {\r
+       int ret = write(sock, (char *)in + done, inlen - done);\r
+       if (ret <= 0) {\r
+           close(sock);\r
+           goto failure;\r
+       }\r
+       done += ret;\r
+    }\r
+\r
+    if (!agent_connections)\r
+       agent_connections = newtree234(agent_conncmp);\r
+\r
+    conn = snew(struct agent_connection);\r
+    conn->fd = sock;\r
+    conn->retbuf = conn->sizebuf;\r
+    conn->retsize = 4;\r
+    conn->retlen = 0;\r
+    conn->callback = callback;\r
+    conn->callback_ctx = callback_ctx;\r
+    add234(agent_connections, conn);\r
+\r
+    uxsel_set(sock, 1, agent_select_result);\r
+    return 0;\r
+\r
+    failure:\r
+    *out = NULL;\r
+    *outlen = 0;\r
+    return 1;\r
+}\r
diff --git a/putty/UNIX/UXCFG.C b/putty/UNIX/UXCFG.C
new file mode 100644 (file)
index 0000000..2e872cc
--- /dev/null
@@ -0,0 +1,80 @@
+/*\r
+ * uxcfg.c - the Unix-specific parts of the PuTTY configuration\r
+ * box.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "storage.h"\r
+\r
+void unix_setup_config_box(struct controlbox *b, int midsession, int protocol)\r
+{\r
+    struct controlset *s;\r
+    union control *c;\r
+\r
+    /*\r
+     * The Config structure contains two Unix-specific elements\r
+     * which are not configured in here: stamp_utmp and\r
+     * login_shell. This is because pterm does not put up a\r
+     * configuration box right at the start, which is the only time\r
+     * when these elements would be useful to configure.\r
+     */\r
+\r
+    /*\r
+     * On Unix, we don't have a drop-down list for the printer\r
+     * control.\r
+     */\r
+    s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");\r
+    assert(s->ncontrols == 1 && s->ctrls[0]->generic.type == CTRL_EDITBOX);\r
+    s->ctrls[0]->editbox.has_list = 0;\r
+\r
+    /*\r
+     * Unix supports a local-command proxy. This also means we must\r
+     * adjust the text on the `Telnet command' control.\r
+     */\r
+    if (!midsession) {\r
+       int i;\r
+        s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);\r
+       for (i = 0; i < s->ncontrols; i++) {\r
+           c = s->ctrls[i];\r
+           if (c->generic.type == CTRL_RADIO &&\r
+               c->generic.context.i == offsetof(Config, proxy_type)) {\r
+               assert(c->generic.handler == dlg_stdradiobutton_handler);\r
+               c->radio.nbuttons++;\r
+               c->radio.buttons =\r
+                   sresize(c->radio.buttons, c->radio.nbuttons, char *);\r
+               c->radio.buttons[c->radio.nbuttons-1] =\r
+                   dupstr("Local");\r
+               c->radio.buttondata =\r
+                   sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);\r
+               c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_CMD);\r
+               break;\r
+           }\r
+       }\r
+\r
+       for (i = 0; i < s->ncontrols; i++) {\r
+           c = s->ctrls[i];\r
+           if (c->generic.type == CTRL_EDITBOX &&\r
+               c->generic.context.i ==\r
+               offsetof(Config, proxy_telnet_command)) {\r
+               assert(c->generic.handler == dlg_stdeditbox_handler);\r
+               sfree(c->generic.label);\r
+               c->generic.label = dupstr("Telnet command, or local"\r
+                                         " proxy command");\r
+               break;\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Serial back end is available on Unix. However, we have to\r
+     * mask out a couple of the configuration options: mark and\r
+     * space parity are not conveniently supported, and neither is\r
+     * DSR/DTR flow control.\r
+     */\r
+    if (!midsession || (protocol == PROT_SERIAL))\r
+        ser_setup_config_box(b, midsession, 0x07, 0x07);\r
+}\r
diff --git a/putty/UNIX/UXCONS.C b/putty/UNIX/UXCONS.C
new file mode 100644 (file)
index 0000000..2e71df4
--- /dev/null
@@ -0,0 +1,440 @@
+/*\r
+ * uxcons.c: various interactive-prompt routines shared between the\r
+ * Unix console PuTTY tools\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <assert.h>\r
+#include <termios.h>\r
+#include <unistd.h>\r
+\r
+#include "putty.h"\r
+#include "storage.h"\r
+#include "ssh.h"\r
+\r
+int console_batch_mode = FALSE;\r
+\r
+static void *console_logctx = NULL;\r
+\r
+static struct termios orig_termios_stderr;\r
+static int stderr_is_a_tty;\r
+\r
+void stderr_tty_init()\r
+{\r
+    /* Ensure that if stderr is a tty, we can get it back to a sane state. */\r
+    if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) {\r
+       stderr_is_a_tty = TRUE;\r
+       tcgetattr(STDERR_FILENO, &orig_termios_stderr);\r
+    }\r
+}\r
+\r
+void premsg(struct termios *cf)\r
+{\r
+    if (stderr_is_a_tty) {\r
+       tcgetattr(STDERR_FILENO, cf);\r
+       tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr);\r
+    }\r
+}\r
+void postmsg(struct termios *cf)\r
+{\r
+    if (stderr_is_a_tty)\r
+       tcsetattr(STDERR_FILENO, TCSADRAIN, cf);\r
+}\r
+\r
+/*\r
+ * Clean up and exit.\r
+ */\r
+void cleanup_exit(int code)\r
+{\r
+    /*\r
+     * Clean up.\r
+     */\r
+    sk_cleanup();\r
+    random_save_seed();\r
+    exit(code);\r
+}\r
+\r
+void set_busy_status(void *frontend, int status)\r
+{\r
+}\r
+\r
+void update_specials_menu(void *frontend)\r
+{\r
+}\r
+\r
+void notify_remote_exit(void *frontend)\r
+{\r
+}\r
+\r
+void timer_change_notify(long next)\r
+{\r
+}\r
+\r
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
+                        char *keystr, char *fingerprint,\r
+                        void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    int ret;\r
+\r
+    static const char absentmsg_batch[] =\r
+       "The server's host key is not cached. You have no guarantee\n"\r
+       "that the server is the computer you think it is.\n"\r
+       "The server's %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "Connection abandoned.\n";\r
+    static const char absentmsg[] =\r
+       "The server's host key is not cached. You have no guarantee\n"\r
+       "that the server is the computer you think it is.\n"\r
+       "The server's %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you trust this host, enter \"y\" to add the key to\n"\r
+       "PuTTY's cache and carry on connecting.\n"\r
+       "If you want to carry on connecting just once, without\n"\r
+       "adding the key to the cache, enter \"n\".\n"\r
+       "If you do not trust this host, press Return to abandon the\n"\r
+       "connection.\n"\r
+       "Store key in cache? (y/n) ";\r
+\r
+    static const char wrongmsg_batch[] =\r
+       "WARNING - POTENTIAL SECURITY BREACH!\n"\r
+       "The server's host key does not match the one PuTTY has\n"\r
+       "cached. This means that either the server administrator\n"\r
+       "has changed the host key, or you have actually connected\n"\r
+       "to another computer pretending to be the server.\n"\r
+       "The new %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "Connection abandoned.\n";\r
+    static const char wrongmsg[] =\r
+       "WARNING - POTENTIAL SECURITY BREACH!\n"\r
+       "The server's host key does not match the one PuTTY has\n"\r
+       "cached. This means that either the server administrator\n"\r
+       "has changed the host key, or you have actually connected\n"\r
+       "to another computer pretending to be the server.\n"\r
+       "The new %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you were expecting this change and trust the new key,\n"\r
+       "enter \"y\" to update PuTTY's cache and continue connecting.\n"\r
+       "If you want to carry on connecting but without updating\n"\r
+       "the cache, enter \"n\".\n"\r
+       "If you want to abandon the connection completely, press\n"\r
+       "Return to cancel. Pressing Return is the ONLY guaranteed\n"\r
+       "safe choice.\n"\r
+       "Update cached key? (y/n, Return cancels connection) ";\r
+\r
+    static const char abandoned[] = "Connection abandoned.\n";\r
+\r
+    char line[32];\r
+    struct termios cf;\r
+\r
+    /*\r
+     * Verify the key.\r
+     */\r
+    ret = verify_host_key(host, port, keytype, keystr);\r
+\r
+    if (ret == 0)                     /* success - key matched OK */\r
+       return 1;\r
+\r
+    premsg(&cf);\r
+    if (ret == 2) {                   /* key was different */\r
+       if (console_batch_mode) {\r
+           fprintf(stderr, wrongmsg_batch, keytype, fingerprint);\r
+           return 0;\r
+       }\r
+       fprintf(stderr, wrongmsg, keytype, fingerprint);\r
+       fflush(stderr);\r
+    }\r
+    if (ret == 1) {                   /* key was absent */\r
+       if (console_batch_mode) {\r
+           fprintf(stderr, absentmsg_batch, keytype, fingerprint);\r
+           return 0;\r
+       }\r
+       fprintf(stderr, absentmsg, keytype, fingerprint);\r
+       fflush(stderr);\r
+    }\r
+\r
+    {\r
+       struct termios oldmode, newmode;\r
+       tcgetattr(0, &oldmode);\r
+       newmode = oldmode;\r
+       newmode.c_lflag |= ECHO | ISIG | ICANON;\r
+       tcsetattr(0, TCSANOW, &newmode);\r
+       line[0] = '\0';\r
+       if (read(0, line, sizeof(line) - 1) <= 0)\r
+           /* handled below */;\r
+       tcsetattr(0, TCSANOW, &oldmode);\r
+    }\r
+\r
+    if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {\r
+       if (line[0] == 'y' || line[0] == 'Y')\r
+           store_host_key(host, port, keytype, keystr);\r
+       postmsg(&cf);\r
+        return 1;\r
+    } else {\r
+       fprintf(stderr, abandoned);\r
+       postmsg(&cf);\r
+        return 0;\r
+    }\r
+}\r
+\r
+/*\r
+ * Ask whether the selected algorithm is acceptable (since it was\r
+ * below the configured 'warn' threshold).\r
+ */\r
+int askalg(void *frontend, const char *algtype, const char *algname,\r
+          void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char msg[] =\r
+       "The first %s supported by the server is\n"\r
+       "%s, which is below the configured warning threshold.\n"\r
+       "Continue with connection? (y/n) ";\r
+    static const char msg_batch[] =\r
+       "The first %s supported by the server is\n"\r
+       "%s, which is below the configured warning threshold.\n"\r
+       "Connection abandoned.\n";\r
+    static const char abandoned[] = "Connection abandoned.\n";\r
+\r
+    char line[32];\r
+    struct termios cf;\r
+\r
+    premsg(&cf);\r
+    if (console_batch_mode) {\r
+       fprintf(stderr, msg_batch, algtype, algname);\r
+       return 0;\r
+    }\r
+\r
+    fprintf(stderr, msg, algtype, algname);\r
+    fflush(stderr);\r
+\r
+    {\r
+       struct termios oldmode, newmode;\r
+       tcgetattr(0, &oldmode);\r
+       newmode = oldmode;\r
+       newmode.c_lflag |= ECHO | ISIG | ICANON;\r
+       tcsetattr(0, TCSANOW, &newmode);\r
+       line[0] = '\0';\r
+       if (read(0, line, sizeof(line) - 1) <= 0)\r
+           /* handled below */;\r
+       tcsetattr(0, TCSANOW, &oldmode);\r
+    }\r
+\r
+    if (line[0] == 'y' || line[0] == 'Y') {\r
+       postmsg(&cf);\r
+       return 1;\r
+    } else {\r
+       fprintf(stderr, abandoned);\r
+       postmsg(&cf);\r
+       return 0;\r
+    }\r
+}\r
+\r
+/*\r
+ * Ask whether to wipe a session log file before writing to it.\r
+ * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).\r
+ */\r
+int askappend(void *frontend, Filename filename,\r
+             void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char msgtemplate[] =\r
+       "The session log file \"%.*s\" already exists.\n"\r
+       "You can overwrite it with a new session log,\n"\r
+       "append your session log to the end of it,\n"\r
+       "or disable session logging for this session.\n"\r
+       "Enter \"y\" to wipe the file, \"n\" to append to it,\n"\r
+       "or just press Return to disable logging.\n"\r
+       "Wipe the log file? (y/n, Return cancels logging) ";\r
+\r
+    static const char msgtemplate_batch[] =\r
+       "The session log file \"%.*s\" already exists.\n"\r
+       "Logging will not be enabled.\n";\r
+\r
+    char line[32];\r
+    struct termios cf;\r
+\r
+    premsg(&cf);\r
+    if (console_batch_mode) {\r
+       fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);\r
+       fflush(stderr);\r
+       return 0;\r
+    }\r
+    fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);\r
+    fflush(stderr);\r
+\r
+    {\r
+       struct termios oldmode, newmode;\r
+       tcgetattr(0, &oldmode);\r
+       newmode = oldmode;\r
+       newmode.c_lflag |= ECHO | ISIG | ICANON;\r
+       tcsetattr(0, TCSANOW, &newmode);\r
+       line[0] = '\0';\r
+       if (read(0, line, sizeof(line) - 1) <= 0)\r
+           /* handled below */;\r
+       tcsetattr(0, TCSANOW, &oldmode);\r
+    }\r
+\r
+    postmsg(&cf);\r
+    if (line[0] == 'y' || line[0] == 'Y')\r
+       return 2;\r
+    else if (line[0] == 'n' || line[0] == 'N')\r
+       return 1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+/*\r
+ * Warn about the obsolescent key file format.\r
+ * \r
+ * Uniquely among these functions, this one does _not_ expect a\r
+ * frontend handle. This means that if PuTTY is ported to a\r
+ * platform which requires frontend handles, this function will be\r
+ * an anomaly. Fortunately, the problem it addresses will not have\r
+ * been present on that platform, so it can plausibly be\r
+ * implemented as an empty function.\r
+ */\r
+void old_keyfile_warning(void)\r
+{\r
+    static const char message[] =\r
+       "You are loading an SSH-2 private key which has an\n"\r
+       "old version of the file format. This means your key\n"\r
+       "file is not fully tamperproof. Future versions of\n"\r
+       "PuTTY may stop supporting this private key format,\n"\r
+       "so we recommend you convert your key to the new\n"\r
+       "format.\n"\r
+       "\n"\r
+       "Once the key is loaded into PuTTYgen, you can perform\n"\r
+       "this conversion simply by saving it again.\n";\r
+\r
+    struct termios cf;\r
+    premsg(&cf);\r
+    fputs(message, stderr);\r
+    postmsg(&cf);\r
+}\r
+\r
+void console_provide_logctx(void *logctx)\r
+{\r
+    console_logctx = logctx;\r
+}\r
+\r
+void logevent(void *frontend, const char *string)\r
+{\r
+    struct termios cf;\r
+    premsg(&cf);\r
+    if (console_logctx)\r
+       log_eventlog(console_logctx, string);\r
+    postmsg(&cf);\r
+}\r
+\r
+/*\r
+ * Special function to print text to the console for password\r
+ * prompts and the like. Uses /dev/tty or stderr, in that order of\r
+ * preference; also sanitises escape sequences out of the text, on\r
+ * the basis that it might have been sent by a hostile SSH server\r
+ * doing malicious keyboard-interactive.\r
+ */\r
+static void console_prompt_text(FILE **confp, const char *data, int len)\r
+{\r
+    int i;\r
+\r
+    if (!*confp) {\r
+       if ((*confp = fopen("/dev/tty", "w")) == NULL)\r
+           *confp = stderr;\r
+    }\r
+\r
+    for (i = 0; i < len; i++)\r
+       if ((data[i] & 0x60) || (data[i] == '\n'))\r
+           fputc(data[i], *confp);\r
+    fflush(*confp);\r
+}\r
+\r
+int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    size_t curr_prompt;\r
+    FILE *confp = NULL;\r
+\r
+    /*\r
+     * Zero all the results, in case we abort half-way through.\r
+     */\r
+    {\r
+       int i;\r
+       for (i = 0; i < p->n_prompts; i++)\r
+           memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);\r
+    }\r
+\r
+    if (p->n_prompts && console_batch_mode)\r
+       return 0;\r
+\r
+    /*\r
+     * Preamble.\r
+     */\r
+    /* We only print the `name' caption if we have to... */\r
+    if (p->name_reqd && p->name) {\r
+       size_t l = strlen(p->name);\r
+       console_prompt_text(&confp, p->name, l);\r
+       if (p->name[l-1] != '\n')\r
+           console_prompt_text(&confp, "\n", 1);\r
+    }\r
+    /* ...but we always print any `instruction'. */\r
+    if (p->instruction) {\r
+       size_t l = strlen(p->instruction);\r
+       console_prompt_text(&confp, p->instruction, l);\r
+       if (p->instruction[l-1] != '\n')\r
+           console_prompt_text(&confp, "\n", 1);\r
+    }\r
+\r
+    for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {\r
+\r
+       struct termios oldmode, newmode;\r
+       int i;\r
+       prompt_t *pr = p->prompts[curr_prompt];\r
+\r
+       tcgetattr(0, &oldmode);\r
+       newmode = oldmode;\r
+       newmode.c_lflag |= ISIG | ICANON;\r
+       if (!pr->echo)\r
+           newmode.c_lflag &= ~ECHO;\r
+       else\r
+           newmode.c_lflag |= ECHO;\r
+       tcsetattr(0, TCSANOW, &newmode);\r
+\r
+       console_prompt_text(&confp, pr->prompt, strlen(pr->prompt));\r
+\r
+       i = read(0, pr->result, pr->result_len - 1);\r
+\r
+       tcsetattr(0, TCSANOW, &oldmode);\r
+\r
+       if (i > 0 && pr->result[i-1] == '\n')\r
+           i--;\r
+       pr->result[i] = '\0';\r
+\r
+       if (!pr->echo)\r
+           console_prompt_text(&confp, "\n", 1);\r
+\r
+    }\r
+\r
+    if (confp && confp != stderr)\r
+       fclose(confp);\r
+\r
+    return 1; /* success */\r
+}\r
+\r
+void frontend_keypress(void *handle)\r
+{\r
+    /*\r
+     * This is nothing but a stub, in console code.\r
+     */\r
+    return;\r
+}\r
+\r
+int is_interactive(void)\r
+{\r
+    return isatty(0);\r
+}\r
+\r
+/*\r
+ * X11-forwarding-related things suitable for console.\r
+ */\r
+\r
+char *platform_get_x_display(void) {\r
+    return dupstr(getenv("DISPLAY"));\r
+}\r
diff --git a/putty/UNIX/UXGEN.C b/putty/UNIX/UXGEN.C
new file mode 100644 (file)
index 0000000..b1b4d19
--- /dev/null
@@ -0,0 +1,38 @@
+/*\r
+ * uxgen.c: Unix implementation of get_heavy_noise() from cmdgen.c.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <fcntl.h>\r
+#include <unistd.h>\r
+\r
+#include "putty.h"\r
+\r
+char *get_random_data(int len)\r
+{\r
+    char *buf = snewn(len, char);\r
+    int fd;\r
+    int ngot, ret;\r
+\r
+    fd = open("/dev/random", O_RDONLY);\r
+    if (fd < 0) {\r
+       sfree(buf);\r
+       perror("puttygen: unable to open /dev/random");\r
+       return NULL;\r
+    }\r
+\r
+    ngot = 0;\r
+    while (ngot < len) {\r
+       ret = read(fd, buf+ngot, len-ngot);\r
+       if (ret < 0) {\r
+           close(fd);\r
+           perror("puttygen: unable to read /dev/random");\r
+           return NULL;\r
+       }\r
+       ngot += ret;\r
+    }\r
+\r
+    close(fd);\r
+\r
+    return buf;\r
+}\r
diff --git a/putty/UNIX/UXGSS.C b/putty/UNIX/UXGSS.C
new file mode 100644 (file)
index 0000000..1bb803c
--- /dev/null
@@ -0,0 +1,168 @@
+#include "putty.h"\r
+#ifndef NO_GSSAPI\r
+#include "pgssapi.h"\r
+#include "sshgss.h"\r
+#include "sshgssc.h"\r
+\r
+/* Unix code to set up the GSSAPI library list. */\r
+\r
+#if !defined NO_LIBDL && !defined NO_GSSAPI\r
+\r
+const int ngsslibs = 4;\r
+const char *const gsslibnames[4] = {\r
+    "libgssapi (Heimdal)",\r
+    "libgssapi_krb5 (MIT Kerberos)",\r
+    "libgss (Sun)",\r
+    "User-specified GSSAPI library",\r
+};\r
+const struct keyvalwhere gsslibkeywords[] = {\r
+    { "libgssapi", 0, -1, -1 },\r
+    { "libgssapi_krb5", 1, -1, -1 },\r
+    { "libgss", 2, -1, -1 },\r
+    { "custom", 3, -1, -1 },\r
+};\r
+\r
+/*\r
+ * Run-time binding against a choice of GSSAPI implementations. We\r
+ * try loading several libraries, and produce an entry in\r
+ * ssh_gss_libraries[] for each one.\r
+ */\r
+\r
+static void gss_init(struct ssh_gss_library *lib, void *dlhandle,\r
+                    int id, const char *msg)\r
+{\r
+    lib->id = id;\r
+    lib->gsslogmsg = msg;\r
+    lib->handle = dlhandle;\r
+\r
+#define BIND_GSS_FN(name) \\r
+    lib->u.gssapi.name = (t_gss_##name) dlsym(dlhandle, "gss_" #name)\r
+\r
+    BIND_GSS_FN(delete_sec_context);\r
+    BIND_GSS_FN(display_status);\r
+    BIND_GSS_FN(get_mic);\r
+    BIND_GSS_FN(import_name);\r
+    BIND_GSS_FN(init_sec_context);\r
+    BIND_GSS_FN(release_buffer);\r
+    BIND_GSS_FN(release_cred);\r
+    BIND_GSS_FN(release_name);\r
+\r
+#undef BIND_GSS_FN\r
+\r
+    ssh_gssapi_bind_fns(lib);\r
+}\r
+\r
+/* Dynamically load gssapi libs. */\r
+struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)\r
+{\r
+    void *gsslib;\r
+    struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);\r
+\r
+    list->libraries = snewn(4, struct ssh_gss_library);\r
+    list->nlibraries = 0;\r
+\r
+    /* Heimdal's GSSAPI Library */\r
+    if ((gsslib = dlopen("libgssapi.so.2", RTLD_LAZY)) != NULL)\r
+       gss_init(&list->libraries[list->nlibraries++], gsslib,\r
+                0, "Using GSSAPI from libgssapi.so.2");\r
+\r
+    /* MIT Kerberos's GSSAPI Library */\r
+    if ((gsslib = dlopen("libgssapi_krb5.so.2", RTLD_LAZY)) != NULL)\r
+       gss_init(&list->libraries[list->nlibraries++], gsslib,\r
+                1, "Using GSSAPI from libgssapi_krb5.so.2");\r
+\r
+    /* Sun's GSSAPI Library */\r
+    if ((gsslib = dlopen("libgss.so.1", RTLD_LAZY)) != NULL)\r
+       gss_init(&list->libraries[list->nlibraries++], gsslib,\r
+                2, "Using GSSAPI from libgss.so.1");\r
+\r
+    /* User-specified GSSAPI library */\r
+    if (cfg->ssh_gss_custom.path[0] &&\r
+       (gsslib = dlopen(cfg->ssh_gss_custom.path, RTLD_LAZY)) != NULL)\r
+       gss_init(&list->libraries[list->nlibraries++], gsslib,\r
+                3, dupprintf("Using GSSAPI from user-specified"\r
+                             " library '%s'", cfg->ssh_gss_custom.path));\r
+\r
+    return list;\r
+}\r
+\r
+void ssh_gss_cleanup(struct ssh_gss_liblist *list)\r
+{\r
+    int i;\r
+\r
+    /*\r
+     * dlopen and dlclose are defined to employ reference counting\r
+     * in the case where the same library is repeatedly dlopened, so\r
+     * even in a multiple-sessions-per-process context it's safe to\r
+     * naively dlclose everything here without worrying about\r
+     * destroying it under the feet of another SSH instance still\r
+     * using it.\r
+     */\r
+    for (i = 0; i < list->nlibraries; i++) {\r
+       dlclose(list->libraries[i].handle);\r
+       if (list->libraries[i].id == 3) {\r
+           /* The 'custom' id involves a dynamically allocated message.\r
+            * Note that we must cast away the 'const' to free it. */\r
+           sfree((char *)list->libraries[i].gsslogmsg);\r
+       }\r
+    }\r
+    sfree(list->libraries);\r
+    sfree(list);\r
+}\r
+\r
+#elif !defined NO_GSSAPI\r
+\r
+const int ngsslibs = 1;\r
+const char *const gsslibnames[1] = {\r
+    "static",\r
+};\r
+const struct keyvalwhere gsslibkeywords[] = {\r
+    { "static", 0, -1, -1 },\r
+};\r
+\r
+/*\r
+ * Link-time binding against GSSAPI. Here we just construct a single\r
+ * library structure containing pointers to the functions we linked\r
+ * against.\r
+ */\r
+\r
+#include <gssapi/gssapi.h>\r
+\r
+/* Dynamically load gssapi libs. */\r
+struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)\r
+{\r
+    struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);\r
+\r
+    list->libraries = snew(struct ssh_gss_library);\r
+    list->nlibraries = 1;\r
+\r
+    list->libraries[0].gsslogmsg = "Using statically linked GSSAPI";\r
+\r
+#define BIND_GSS_FN(name) \\r
+    list->libraries[0].u.gssapi.name = (t_gss_##name) gss_##name\r
+\r
+    BIND_GSS_FN(delete_sec_context);\r
+    BIND_GSS_FN(display_status);\r
+    BIND_GSS_FN(get_mic);\r
+    BIND_GSS_FN(import_name);\r
+    BIND_GSS_FN(init_sec_context);\r
+    BIND_GSS_FN(release_buffer);\r
+    BIND_GSS_FN(release_cred);\r
+    BIND_GSS_FN(release_name);\r
+\r
+#undef BIND_GSS_FN\r
+\r
+    ssh_gssapi_bind_fns(&list->libraries[0]);\r
+\r
+    return list;\r
+}\r
+\r
+void ssh_gss_cleanup(struct ssh_gss_liblist *list)\r
+{\r
+    sfree(list->libraries);\r
+    sfree(list);\r
+}\r
+\r
+#endif /* NO_LIBDL */\r
+\r
+#endif /* NO_GSSAPI */\r
diff --git a/putty/UNIX/UXMISC.C b/putty/UNIX/UXMISC.C
new file mode 100644 (file)
index 0000000..4bda059
--- /dev/null
@@ -0,0 +1,152 @@
+/*\r
+ * PuTTY miscellaneous Unix stuff\r
+ */\r
+\r
+#include <fcntl.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <unistd.h>\r
+#include <sys/time.h>\r
+#include <sys/types.h>\r
+#include <pwd.h>\r
+\r
+#include "putty.h"\r
+\r
+long tickcount_offset = 0;\r
+\r
+unsigned long getticks(void)\r
+{\r
+    struct timeval tv;\r
+    gettimeofday(&tv, NULL);\r
+    /*\r
+     * We want to use milliseconds rather than microseconds,\r
+     * because we need a decent number of them to fit into a 32-bit\r
+     * word so it can be used for keepalives.\r
+     */\r
+    return tv.tv_sec * 1000 + tv.tv_usec / 1000 + tickcount_offset;\r
+}\r
+\r
+Filename filename_from_str(const char *str)\r
+{\r
+    Filename ret;\r
+    strncpy(ret.path, str, sizeof(ret.path));\r
+    ret.path[sizeof(ret.path)-1] = '\0';\r
+    return ret;\r
+}\r
+\r
+const char *filename_to_str(const Filename *fn)\r
+{\r
+    return fn->path;\r
+}\r
+\r
+int filename_equal(Filename f1, Filename f2)\r
+{\r
+    return !strcmp(f1.path, f2.path);\r
+}\r
+\r
+int filename_is_null(Filename fn)\r
+{\r
+    return !*fn.path;\r
+}\r
+\r
+#ifdef DEBUG\r
+static FILE *debug_fp = NULL;\r
+\r
+void dputs(char *buf)\r
+{\r
+    if (!debug_fp) {\r
+       debug_fp = fopen("debug.log", "w");\r
+    }\r
+\r
+    write(1, buf, strlen(buf));\r
+\r
+    fputs(buf, debug_fp);\r
+    fflush(debug_fp);\r
+}\r
+#endif\r
+\r
+char *get_username(void)\r
+{\r
+    struct passwd *p;\r
+    uid_t uid = getuid();\r
+    char *user, *ret = NULL;\r
+\r
+    /*\r
+     * First, find who we think we are using getlogin. If this\r
+     * agrees with our uid, we'll go along with it. This should\r
+     * allow sharing of uids between several login names whilst\r
+     * coping correctly with people who have su'ed.\r
+     */\r
+    user = getlogin();\r
+    setpwent();\r
+    if (user)\r
+       p = getpwnam(user);\r
+    else\r
+       p = NULL;\r
+    if (p && p->pw_uid == uid) {\r
+       /*\r
+        * The result of getlogin() really does correspond to\r
+        * our uid. Fine.\r
+        */\r
+       ret = user;\r
+    } else {\r
+       /*\r
+        * If that didn't work, for whatever reason, we'll do\r
+        * the simpler version: look up our uid in the password\r
+        * file and map it straight to a name.\r
+        */\r
+       p = getpwuid(uid);\r
+       if (!p)\r
+           return NULL;\r
+       ret = p->pw_name;\r
+    }\r
+    endpwent();\r
+\r
+    return dupstr(ret);\r
+}\r
+\r
+/*\r
+ * Display the fingerprints of the PGP Master Keys to the user.\r
+ * (This is here rather than in uxcons because it's appropriate even for\r
+ * Unix GUI apps.)\r
+ */\r
+void pgp_fingerprints(void)\r
+{\r
+    fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"\r
+         "be used to establish a trust path from this executable to another\n"\r
+         "one. See the manual for more information.\n"\r
+         "(Note: these fingerprints have nothing to do with SSH!)\n"\r
+         "\n"\r
+         "PuTTY Master Key (RSA), 1024-bit:\n"\r
+         "  " PGP_RSA_MASTER_KEY_FP "\n"\r
+         "PuTTY Master Key (DSA), 1024-bit:\n"\r
+         "  " PGP_DSA_MASTER_KEY_FP "\n", stdout);\r
+}\r
+\r
+/*\r
+ * Set FD_CLOEXEC on a file descriptor\r
+ */\r
+int cloexec(int fd) {\r
+    int fdflags;\r
+\r
+    fdflags = fcntl(fd, F_GETFD);\r
+    if (fdflags == -1) return -1;\r
+    return fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC);\r
+}\r
+\r
+FILE *f_open(struct Filename filename, char const *mode, int is_private)\r
+{\r
+    if (!is_private) {\r
+       return fopen(filename.path, mode);\r
+    } else {\r
+       int fd;\r
+       assert(mode[0] == 'w');        /* is_private is meaningless for read,\r
+                                         and tricky for append */\r
+       fd = open(filename.path, O_WRONLY | O_CREAT | O_TRUNC,\r
+                     0700);\r
+       if (fd < 0)\r
+           return NULL;\r
+       return fdopen(fd, mode);\r
+    }\r
+}\r
diff --git a/putty/UNIX/UXNET.C b/putty/UNIX/UXNET.C
new file mode 100644 (file)
index 0000000..0882638
--- /dev/null
@@ -0,0 +1,1435 @@
+/*\r
+ * Unix networking abstraction.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <errno.h>\r
+#include <fcntl.h>\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/socket.h>\r
+#include <sys/ioctl.h>\r
+#include <arpa/inet.h>\r
+#include <netinet/in.h>\r
+#include <netinet/tcp.h>\r
+#include <netdb.h>\r
+#include <sys/un.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "tree234.h"\r
+\r
+/* Solaris needs <sys/sockio.h> for SIOCATMARK. */\r
+#ifndef SIOCATMARK\r
+#include <sys/sockio.h>\r
+#endif\r
+\r
+#ifndef X11_UNIX_PATH\r
+# define X11_UNIX_PATH "/tmp/.X11-unix/X"\r
+#endif\r
+\r
+/* \r
+ * Access to sockaddr types without breaking C strict aliasing rules.\r
+ */\r
+union sockaddr_union {\r
+#ifdef NO_IPV6\r
+    struct sockaddr_in storage;\r
+#else\r
+    struct sockaddr_storage storage;\r
+    struct sockaddr_in6 sin6;\r
+#endif\r
+    struct sockaddr sa;\r
+    struct sockaddr_in sin;\r
+    struct sockaddr_un su;\r
+};\r
+\r
+/*\r
+ * We used to typedef struct Socket_tag *Socket.\r
+ *\r
+ * Since we have made the networking abstraction slightly more\r
+ * abstract, Socket no longer means a tcp socket (it could mean\r
+ * an ssl socket).  So now we must use Actual_Socket when we know\r
+ * we are talking about a tcp socket.\r
+ */\r
+typedef struct Socket_tag *Actual_Socket;\r
+\r
+/*\r
+ * Mutable state that goes with a SockAddr: stores information\r
+ * about where in the list of candidate IP(v*) addresses we've\r
+ * currently got to.\r
+ */\r
+typedef struct SockAddrStep_tag SockAddrStep;\r
+struct SockAddrStep_tag {\r
+#ifndef NO_IPV6\r
+    struct addrinfo *ai;              /* steps along addr->ais */\r
+#endif\r
+    int curraddr;\r
+};\r
+\r
+struct Socket_tag {\r
+    struct socket_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+    const char *error;\r
+    int s;\r
+    Plug plug;\r
+    void *private_ptr;\r
+    bufchain output_data;\r
+    int connected;                    /* irrelevant for listening sockets */\r
+    int writable;\r
+    int frozen; /* this causes readability notifications to be ignored */\r
+    int frozen_readable; /* this means we missed at least one readability\r
+                         * notification while we were frozen */\r
+    int localhost_only;                       /* for listening sockets */\r
+    char oobdata[1];\r
+    int sending_oob;\r
+    int oobpending;                   /* is there OOB data available to read? */\r
+    int oobinline;\r
+    int pending_error;                /* in case send() returns error */\r
+    int listener;\r
+    int nodelay, keepalive;            /* for connect()-type sockets */\r
+    int privport, port;                /* and again */\r
+    SockAddr addr;\r
+    SockAddrStep step;\r
+    /*\r
+     * We sometimes need pairs of Socket structures to be linked:\r
+     * if we are listening on the same IPv6 and v4 port, for\r
+     * example. So here we define `parent' and `child' pointers to\r
+     * track this link.\r
+     */\r
+    Actual_Socket parent, child;\r
+};\r
+\r
+struct SockAddr_tag {\r
+    int refcount;\r
+    const char *error;\r
+    enum { UNRESOLVED, UNIX, IP } superfamily;\r
+#ifndef NO_IPV6\r
+    struct addrinfo *ais;             /* Addresses IPv6 style. */\r
+#else\r
+    unsigned long *addresses;         /* Addresses IPv4 style. */\r
+    int naddresses;\r
+#endif\r
+    char hostname[512];                       /* Store an unresolved host name. */\r
+};\r
+\r
+/*\r
+ * Which address family this address belongs to. AF_INET for IPv4;\r
+ * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has\r
+ * not been done and a simple host name is held in this SockAddr\r
+ * structure.\r
+ */\r
+#ifndef NO_IPV6\r
+#define SOCKADDR_FAMILY(addr, step) \\r
+    ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \\r
+     (addr)->superfamily == UNIX ? AF_UNIX : \\r
+     (step).ai ? (step).ai->ai_family : AF_INET)\r
+#else\r
+#define SOCKADDR_FAMILY(addr, step) \\r
+    ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \\r
+     (addr)->superfamily == UNIX ? AF_UNIX : AF_INET)\r
+#endif\r
+\r
+/*\r
+ * Start a SockAddrStep structure to step through multiple\r
+ * addresses.\r
+ */\r
+#ifndef NO_IPV6\r
+#define START_STEP(addr, step) \\r
+    ((step).ai = (addr)->ais, (step).curraddr = 0)\r
+#else\r
+#define START_STEP(addr, step) \\r
+    ((step).curraddr = 0)\r
+#endif\r
+\r
+static tree234 *sktree;\r
+\r
+static void uxsel_tell(Actual_Socket s);\r
+\r
+static int cmpfortree(void *av, void *bv)\r
+{\r
+    Actual_Socket a = (Actual_Socket) av, b = (Actual_Socket) bv;\r
+    int as = a->s, bs = b->s;\r
+    if (as < bs)\r
+       return -1;\r
+    if (as > bs)\r
+       return +1;\r
+    if (a < b)\r
+       return -1;\r
+    if (a > b)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int cmpforsearch(void *av, void *bv)\r
+{\r
+    Actual_Socket b = (Actual_Socket) bv;\r
+    int as = *(int *)av, bs = b->s;\r
+    if (as < bs)\r
+       return -1;\r
+    if (as > bs)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+void sk_init(void)\r
+{\r
+    sktree = newtree234(cmpfortree);\r
+}\r
+\r
+void sk_cleanup(void)\r
+{\r
+    Actual_Socket s;\r
+    int i;\r
+\r
+    if (sktree) {\r
+       for (i = 0; (s = index234(sktree, i)) != NULL; i++) {\r
+           close(s->s);\r
+       }\r
+    }\r
+}\r
+\r
+SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family)\r
+{\r
+    SockAddr ret = snew(struct SockAddr_tag);\r
+#ifndef NO_IPV6\r
+    struct addrinfo hints;\r
+    int err;\r
+#else\r
+    unsigned long a;\r
+    struct hostent *h = NULL;\r
+    int n;\r
+#endif\r
+    char realhost[8192];\r
+\r
+    /* Clear the structure and default to IPv4. */\r
+    memset(ret, 0, sizeof(struct SockAddr_tag));\r
+    ret->superfamily = UNRESOLVED;\r
+    *realhost = '\0';\r
+    ret->error = NULL;\r
+    ret->refcount = 1;\r
+\r
+#ifndef NO_IPV6\r
+    hints.ai_flags = AI_CANONNAME;\r
+    hints.ai_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :\r
+                      address_family == ADDRTYPE_IPV6 ? AF_INET6 :\r
+                      AF_UNSPEC);\r
+    hints.ai_socktype = SOCK_STREAM;\r
+    hints.ai_protocol = 0;\r
+    hints.ai_addrlen = 0;\r
+    hints.ai_addr = NULL;\r
+    hints.ai_canonname = NULL;\r
+    hints.ai_next = NULL;\r
+    err = getaddrinfo(host, NULL, &hints, &ret->ais);\r
+    if (err != 0) {\r
+       ret->error = gai_strerror(err);\r
+       return ret;\r
+    }\r
+    ret->superfamily = IP;\r
+    *realhost = '\0';\r
+    if (ret->ais->ai_canonname != NULL)\r
+       strncat(realhost, ret->ais->ai_canonname, sizeof(realhost) - 1);\r
+    else\r
+       strncat(realhost, host, sizeof(realhost) - 1);\r
+#else\r
+    if ((a = inet_addr(host)) == (unsigned long)(in_addr_t)(-1)) {\r
+       /*\r
+        * Otherwise use the IPv4-only gethostbyname... (NOTE:\r
+        * we don't use gethostbyname as a fallback!)\r
+        */\r
+       if (ret->superfamily == UNRESOLVED) {\r
+           /*debug(("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host)); */\r
+           if ( (h = gethostbyname(host)) )\r
+               ret->superfamily = IP;\r
+       }\r
+       if (ret->superfamily == UNRESOLVED) {\r
+           ret->error = (h_errno == HOST_NOT_FOUND ||\r
+                         h_errno == NO_DATA ||\r
+                         h_errno == NO_ADDRESS ? "Host does not exist" :\r
+                         h_errno == TRY_AGAIN ?\r
+                         "Temporary name service failure" :\r
+                         "gethostbyname: unknown error");\r
+           return ret;\r
+       }\r
+       /* This way we are always sure the h->h_name is valid :) */\r
+       strncpy(realhost, h->h_name, sizeof(realhost));\r
+       for (n = 0; h->h_addr_list[n]; n++);\r
+       ret->addresses = snewn(n, unsigned long);\r
+       ret->naddresses = n;\r
+       for (n = 0; n < ret->naddresses; n++) {\r
+           memcpy(&a, h->h_addr_list[n], sizeof(a));\r
+           ret->addresses[n] = ntohl(a);\r
+       }\r
+    } else {\r
+       /*\r
+        * This must be a numeric IPv4 address because it caused a\r
+        * success return from inet_addr.\r
+        */\r
+       ret->superfamily = IP;\r
+       strncpy(realhost, host, sizeof(realhost));\r
+       ret->addresses = snew(unsigned long);\r
+       ret->naddresses = 1;\r
+       ret->addresses[0] = ntohl(a);\r
+    }\r
+#endif\r
+    realhost[lenof(realhost)-1] = '\0';\r
+    *canonicalname = snewn(1+strlen(realhost), char);\r
+    strcpy(*canonicalname, realhost);\r
+    return ret;\r
+}\r
+\r
+SockAddr sk_nonamelookup(const char *host)\r
+{\r
+    SockAddr ret = snew(struct SockAddr_tag);\r
+    ret->error = NULL;\r
+    ret->superfamily = UNRESOLVED;\r
+    strncpy(ret->hostname, host, lenof(ret->hostname));\r
+    ret->hostname[lenof(ret->hostname)-1] = '\0';\r
+#ifndef NO_IPV6\r
+    ret->ais = NULL;\r
+#else\r
+    ret->addresses = NULL;\r
+#endif\r
+    ret->refcount = 1;\r
+    return ret;\r
+}\r
+\r
+static int sk_nextaddr(SockAddr addr, SockAddrStep *step)\r
+{\r
+#ifndef NO_IPV6\r
+    if (step->ai && step->ai->ai_next) {\r
+       step->ai = step->ai->ai_next;\r
+       return TRUE;\r
+    } else\r
+       return FALSE;\r
+#else\r
+    if (step->curraddr+1 < addr->naddresses) {\r
+       step->curraddr++;\r
+       return TRUE;\r
+    } else {\r
+       return FALSE;\r
+    }\r
+#endif    \r
+}\r
+\r
+void sk_getaddr(SockAddr addr, char *buf, int buflen)\r
+{\r
+    /* XXX not clear what we should return for Unix-domain sockets; let's\r
+     * hope the question never arises */\r
+    assert(addr->superfamily != UNIX);\r
+    if (addr->superfamily == UNRESOLVED) {\r
+       strncpy(buf, addr->hostname, buflen);\r
+       buf[buflen-1] = '\0';\r
+    } else {\r
+#ifndef NO_IPV6\r
+       if (getnameinfo(addr->ais->ai_addr, addr->ais->ai_addrlen, buf, buflen,\r
+                       NULL, 0, NI_NUMERICHOST) != 0) {\r
+           buf[0] = '\0';\r
+           strncat(buf, "<unknown>", buflen - 1);\r
+       }\r
+#else\r
+       struct in_addr a;\r
+       SockAddrStep step;\r
+       START_STEP(addr, step);\r
+       assert(SOCKADDR_FAMILY(addr, step) == AF_INET);\r
+       a.s_addr = htonl(addr->addresses[0]);\r
+       strncpy(buf, inet_ntoa(a), buflen);\r
+       buf[buflen-1] = '\0';\r
+#endif\r
+    }\r
+}\r
+\r
+int sk_hostname_is_local(char *name)\r
+{\r
+    return !strcmp(name, "localhost") ||\r
+          !strcmp(name, "::1") ||\r
+          !strncmp(name, "127.", 4);\r
+}\r
+\r
+#define ipv4_is_loopback(addr) \\r
+    (((addr).s_addr & htonl(0xff000000)) == htonl(0x7f000000))\r
+\r
+static int sockaddr_is_loopback(struct sockaddr *sa)\r
+{\r
+    union sockaddr_union *u = (union sockaddr_union *)sa;\r
+    switch (u->sa.sa_family) {\r
+      case AF_INET:\r
+       return ipv4_is_loopback(u->sin.sin_addr);\r
+#ifndef NO_IPV6\r
+      case AF_INET6:\r
+       return IN6_IS_ADDR_LOOPBACK(&u->sin6.sin6_addr);\r
+#endif\r
+      case AF_UNIX:\r
+       return TRUE;\r
+      default:\r
+       return FALSE;\r
+    }\r
+}\r
+\r
+int sk_address_is_local(SockAddr addr)\r
+{\r
+    if (addr->superfamily == UNRESOLVED)\r
+       return 0;                      /* we don't know; assume not */\r
+    else if (addr->superfamily == UNIX)\r
+       return 1;\r
+    else {\r
+#ifndef NO_IPV6\r
+       return sockaddr_is_loopback(addr->ais->ai_addr);\r
+#else\r
+       struct in_addr a;\r
+       SockAddrStep step;\r
+       START_STEP(addr, step);\r
+       assert(SOCKADDR_FAMILY(addr, step) == AF_INET);\r
+       a.s_addr = htonl(addr->addresses[0]);\r
+       return ipv4_is_loopback(a);\r
+#endif\r
+    }\r
+}\r
+\r
+int sk_addrtype(SockAddr addr)\r
+{\r
+    SockAddrStep step;\r
+    int family;\r
+    START_STEP(addr, step);\r
+    family = SOCKADDR_FAMILY(addr, step);\r
+\r
+    return (family == AF_INET ? ADDRTYPE_IPV4 :\r
+#ifndef NO_IPV6\r
+           family == AF_INET6 ? ADDRTYPE_IPV6 :\r
+#endif\r
+           ADDRTYPE_NAME);\r
+}\r
+\r
+void sk_addrcopy(SockAddr addr, char *buf)\r
+{\r
+    SockAddrStep step;\r
+    int family;\r
+    START_STEP(addr, step);\r
+    family = SOCKADDR_FAMILY(addr, step);\r
+\r
+#ifndef NO_IPV6\r
+    if (family == AF_INET)\r
+       memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr,\r
+              sizeof(struct in_addr));\r
+    else if (family == AF_INET6)\r
+       memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr,\r
+              sizeof(struct in6_addr));\r
+    else\r
+       assert(FALSE);\r
+#else\r
+    struct in_addr a;\r
+\r
+    assert(family == AF_INET);\r
+    a.s_addr = htonl(addr->addresses[step.curraddr]);\r
+    memcpy(buf, (char*) &a.s_addr, 4);\r
+#endif\r
+}\r
+\r
+void sk_addr_free(SockAddr addr)\r
+{\r
+    if (--addr->refcount > 0)\r
+       return;\r
+#ifndef NO_IPV6\r
+    if (addr->ais != NULL)\r
+       freeaddrinfo(addr->ais);\r
+#else\r
+    sfree(addr->addresses);\r
+#endif\r
+    sfree(addr);\r
+}\r
+\r
+SockAddr sk_addr_dup(SockAddr addr)\r
+{\r
+    addr->refcount++;\r
+    return addr;\r
+}\r
+\r
+static Plug sk_tcp_plug(Socket sock, Plug p)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    Plug ret = s->plug;\r
+    if (p)\r
+       s->plug = p;\r
+    return ret;\r
+}\r
+\r
+static void sk_tcp_flush(Socket s)\r
+{\r
+    /*\r
+     * We send data to the socket as soon as we can anyway,\r
+     * so we don't need to do anything here.  :-)\r
+     */\r
+}\r
+\r
+static void sk_tcp_close(Socket s);\r
+static int sk_tcp_write(Socket s, const char *data, int len);\r
+static int sk_tcp_write_oob(Socket s, const char *data, int len);\r
+static void sk_tcp_set_private_ptr(Socket s, void *ptr);\r
+static void *sk_tcp_get_private_ptr(Socket s);\r
+static void sk_tcp_set_frozen(Socket s, int is_frozen);\r
+static const char *sk_tcp_socket_error(Socket s);\r
+\r
+static struct socket_function_table tcp_fn_table = {\r
+    sk_tcp_plug,\r
+    sk_tcp_close,\r
+    sk_tcp_write,\r
+    sk_tcp_write_oob,\r
+    sk_tcp_flush,\r
+    sk_tcp_set_private_ptr,\r
+    sk_tcp_get_private_ptr,\r
+    sk_tcp_set_frozen,\r
+    sk_tcp_socket_error\r
+};\r
+\r
+Socket sk_register(OSSocket sockfd, Plug plug)\r
+{\r
+    Actual_Socket ret;\r
+\r
+    /*\r
+     * Create Socket structure.\r
+     */\r
+    ret = snew(struct Socket_tag);\r
+    ret->fn = &tcp_fn_table;\r
+    ret->error = NULL;\r
+    ret->plug = plug;\r
+    bufchain_init(&ret->output_data);\r
+    ret->writable = 1;                /* to start with */\r
+    ret->sending_oob = 0;\r
+    ret->frozen = 1;\r
+    ret->frozen_readable = 0;\r
+    ret->localhost_only = 0;          /* unused, but best init anyway */\r
+    ret->pending_error = 0;\r
+    ret->oobpending = FALSE;\r
+    ret->listener = 0;\r
+    ret->parent = ret->child = NULL;\r
+    ret->addr = NULL;\r
+    ret->connected = 1;\r
+\r
+    ret->s = sockfd;\r
+\r
+    if (ret->s < 0) {\r
+       ret->error = strerror(errno);\r
+       return (Socket) ret;\r
+    }\r
+\r
+    ret->oobinline = 0;\r
+\r
+    uxsel_tell(ret);\r
+    add234(sktree, ret);\r
+\r
+    return (Socket) ret;\r
+}\r
+\r
+static int try_connect(Actual_Socket sock)\r
+{\r
+    int s;\r
+    union sockaddr_union u;\r
+    const union sockaddr_union *sa;\r
+    int err = 0;\r
+    short localport;\r
+    int fl, salen, family;\r
+\r
+    /*\r
+     * Remove the socket from the tree before we overwrite its\r
+     * internal socket id, because that forms part of the tree's\r
+     * sorting criterion. We'll add it back before exiting this\r
+     * function, whether we changed anything or not.\r
+     */\r
+    del234(sktree, sock);\r
+\r
+    if (sock->s >= 0)\r
+        close(sock->s);\r
+\r
+    plug_log(sock->plug, 0, sock->addr, sock->port, NULL, 0);\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    family = SOCKADDR_FAMILY(sock->addr, sock->step);\r
+    assert(family != AF_UNSPEC);\r
+    s = socket(family, SOCK_STREAM, 0);\r
+    sock->s = s;\r
+\r
+    if (s < 0) {\r
+       err = errno;\r
+       goto ret;\r
+    }\r
+\r
+    cloexec(s);\r
+\r
+    if (sock->oobinline) {\r
+       int b = TRUE;\r
+       setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b));\r
+    }\r
+\r
+    if (sock->nodelay) {\r
+       int b = TRUE;\r
+       setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b));\r
+    }\r
+\r
+    if (sock->keepalive) {\r
+       int b = TRUE;\r
+       setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b));\r
+    }\r
+\r
+    /*\r
+     * Bind to local address.\r
+     */\r
+    if (sock->privport)\r
+       localport = 1023;              /* count from 1023 downwards */\r
+    else\r
+       localport = 0;                 /* just use port 0 (ie kernel picks) */\r
+\r
+    /* BSD IP stacks need sockaddr_in zeroed before filling in */\r
+    memset(&u,'\0',sizeof(u));\r
+\r
+    /* We don't try to bind to a local address for UNIX domain sockets.  (Why\r
+     * do we bother doing the bind when localport == 0 anyway?) */\r
+    if (family != AF_UNIX) {\r
+       /* Loop round trying to bind */\r
+       while (1) {\r
+           int retcode;\r
+\r
+#ifndef NO_IPV6\r
+           if (family == AF_INET6) {\r
+               /* XXX use getaddrinfo to get a local address? */\r
+               u.sin6.sin6_family = AF_INET6;\r
+               u.sin6.sin6_addr = in6addr_any;\r
+               u.sin6.sin6_port = htons(localport);\r
+               retcode = bind(s, &u.sa, sizeof(u.sin6));\r
+           } else\r
+#endif\r
+           {\r
+               assert(family == AF_INET);\r
+               u.sin.sin_family = AF_INET;\r
+               u.sin.sin_addr.s_addr = htonl(INADDR_ANY);\r
+               u.sin.sin_port = htons(localport);\r
+               retcode = bind(s, &u.sa, sizeof(u.sin));\r
+           }\r
+           if (retcode >= 0) {\r
+               err = 0;\r
+               break;                 /* done */\r
+           } else {\r
+               err = errno;\r
+               if (err != EADDRINUSE) /* failed, for a bad reason */\r
+                 break;\r
+           }\r
+           \r
+           if (localport == 0)\r
+             break;                   /* we're only looping once */\r
+           localport--;\r
+           if (localport == 0)\r
+             break;                   /* we might have got to the end */\r
+       }\r
+       \r
+       if (err)\r
+           goto ret;\r
+    }\r
+\r
+    /*\r
+     * Connect to remote address.\r
+     */\r
+    switch(family) {\r
+#ifndef NO_IPV6\r
+      case AF_INET:\r
+       /* XXX would be better to have got getaddrinfo() to fill in the port. */\r
+       ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =\r
+           htons(sock->port);\r
+       sa = (const union sockaddr_union *)sock->step.ai->ai_addr;\r
+       salen = sock->step.ai->ai_addrlen;\r
+       break;\r
+      case AF_INET6:\r
+       ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =\r
+           htons(sock->port);\r
+       sa = (const union sockaddr_union *)sock->step.ai->ai_addr;\r
+       salen = sock->step.ai->ai_addrlen;\r
+       break;\r
+#else\r
+      case AF_INET:\r
+       u.sin.sin_family = AF_INET;\r
+       u.sin.sin_addr.s_addr = htonl(sock->addr->addresses[sock->step.curraddr]);\r
+       u.sin.sin_port = htons((short) sock->port);\r
+       sa = &u;\r
+       salen = sizeof u.sin;\r
+       break;\r
+#endif\r
+      case AF_UNIX:\r
+       assert(sock->port == 0);       /* to catch confused people */\r
+       assert(strlen(sock->addr->hostname) < sizeof u.su.sun_path);\r
+       u.su.sun_family = AF_UNIX;\r
+       strcpy(u.su.sun_path, sock->addr->hostname);\r
+       sa = &u;\r
+       salen = sizeof u.su;\r
+       break;\r
+\r
+      default:\r
+       assert(0 && "unknown address family");\r
+       exit(1); /* XXX: GCC doesn't understand assert() on some systems. */\r
+    }\r
+\r
+    fl = fcntl(s, F_GETFL);\r
+    if (fl != -1)\r
+       fcntl(s, F_SETFL, fl | O_NONBLOCK);\r
+\r
+    if ((connect(s, &(sa->sa), salen)) < 0) {\r
+       if ( errno != EINPROGRESS ) {\r
+           err = errno;\r
+           goto ret;\r
+       }\r
+    } else {\r
+       /*\r
+        * If we _don't_ get EWOULDBLOCK, the connect has completed\r
+        * and we should set the socket as connected and writable.\r
+        */\r
+       sock->connected = 1;\r
+       sock->writable = 1;\r
+    }\r
+\r
+    uxsel_tell(sock);\r
+\r
+    ret:\r
+\r
+    /*\r
+     * No matter what happened, put the socket back in the tree.\r
+     */\r
+    add234(sktree, sock);\r
+\r
+    if (err)\r
+       plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err);\r
+    return err;\r
+}\r
+\r
+Socket sk_new(SockAddr addr, int port, int privport, int oobinline,\r
+             int nodelay, int keepalive, Plug plug)\r
+{\r
+    Actual_Socket ret;\r
+    int err;\r
+\r
+    /*\r
+     * Create Socket structure.\r
+     */\r
+    ret = snew(struct Socket_tag);\r
+    ret->fn = &tcp_fn_table;\r
+    ret->error = NULL;\r
+    ret->plug = plug;\r
+    bufchain_init(&ret->output_data);\r
+    ret->connected = 0;                       /* to start with */\r
+    ret->writable = 0;                /* to start with */\r
+    ret->sending_oob = 0;\r
+    ret->frozen = 0;\r
+    ret->frozen_readable = 0;\r
+    ret->localhost_only = 0;          /* unused, but best init anyway */\r
+    ret->pending_error = 0;\r
+    ret->parent = ret->child = NULL;\r
+    ret->oobpending = FALSE;\r
+    ret->listener = 0;\r
+    ret->addr = addr;\r
+    START_STEP(ret->addr, ret->step);\r
+    ret->s = -1;\r
+    ret->oobinline = oobinline;\r
+    ret->nodelay = nodelay;\r
+    ret->keepalive = keepalive;\r
+    ret->privport = privport;\r
+    ret->port = port;\r
+\r
+    err = 0;\r
+    do {\r
+        err = try_connect(ret);\r
+    } while (err && sk_nextaddr(ret->addr, &ret->step));\r
+\r
+    if (err)\r
+        ret->error = strerror(err);\r
+\r
+    return (Socket) ret;\r
+}\r
+\r
+Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int orig_address_family)\r
+{\r
+    int s;\r
+#ifndef NO_IPV6\r
+    struct addrinfo hints, *ai;\r
+    char portstr[6];\r
+#endif\r
+    union sockaddr_union u;\r
+    union sockaddr_union *addr;\r
+    int addrlen;\r
+    Actual_Socket ret;\r
+    int retcode;\r
+    int address_family;\r
+    int on = 1;\r
+\r
+    /*\r
+     * Create Socket structure.\r
+     */\r
+    ret = snew(struct Socket_tag);\r
+    ret->fn = &tcp_fn_table;\r
+    ret->error = NULL;\r
+    ret->plug = plug;\r
+    bufchain_init(&ret->output_data);\r
+    ret->writable = 0;                /* to start with */\r
+    ret->sending_oob = 0;\r
+    ret->frozen = 0;\r
+    ret->frozen_readable = 0;\r
+    ret->localhost_only = local_host_only;\r
+    ret->pending_error = 0;\r
+    ret->parent = ret->child = NULL;\r
+    ret->oobpending = FALSE;\r
+    ret->listener = 1;\r
+    ret->addr = NULL;\r
+\r
+    /*\r
+     * Translate address_family from platform-independent constants\r
+     * into local reality.\r
+     */\r
+    address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET :\r
+#ifndef NO_IPV6\r
+                     orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 :\r
+#endif\r
+                     AF_UNSPEC);\r
+\r
+#ifndef NO_IPV6\r
+    /* Let's default to IPv6.\r
+     * If the stack doesn't support IPv6, we will fall back to IPv4. */\r
+    if (address_family == AF_UNSPEC) address_family = AF_INET6;\r
+#else\r
+    /* No other choice, default to IPv4 */\r
+    if (address_family == AF_UNSPEC)  address_family = AF_INET;\r
+#endif\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    s = socket(address_family, SOCK_STREAM, 0);\r
+\r
+#ifndef NO_IPV6\r
+    /* If the host doesn't support IPv6 try fallback to IPv4. */\r
+    if (s < 0 && address_family == AF_INET6) {\r
+       address_family = AF_INET;\r
+       s = socket(address_family, SOCK_STREAM, 0);\r
+    }\r
+#endif\r
+\r
+    if (s < 0) {\r
+       ret->error = strerror(errno);\r
+       return (Socket) ret;\r
+    }\r
+\r
+    cloexec(s);\r
+\r
+    ret->oobinline = 0;\r
+\r
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));\r
+\r
+    retcode = -1;\r
+    addr = NULL; addrlen = -1;         /* placate optimiser */\r
+\r
+    if (srcaddr != NULL) {\r
+#ifndef NO_IPV6\r
+        hints.ai_flags = AI_NUMERICHOST;\r
+        hints.ai_family = address_family;\r
+        hints.ai_socktype = SOCK_STREAM;\r
+        hints.ai_protocol = 0;\r
+        hints.ai_addrlen = 0;\r
+        hints.ai_addr = NULL;\r
+        hints.ai_canonname = NULL;\r
+        hints.ai_next = NULL;\r
+       assert(port >= 0 && port <= 99999);\r
+        sprintf(portstr, "%d", port);\r
+        retcode = getaddrinfo(srcaddr, portstr, &hints, &ai);\r
+       if (retcode == 0) {\r
+           addr = (union sockaddr_union *)ai->ai_addr;\r
+           addrlen = ai->ai_addrlen;\r
+       }\r
+#else\r
+        memset(&u,'\0',sizeof u);\r
+        u.sin.sin_family = AF_INET;\r
+        u.sin.sin_port = htons(port);\r
+        u.sin.sin_addr.s_addr = inet_addr(srcaddr);\r
+        if (u.sin.sin_addr.s_addr != (in_addr_t)(-1)) {\r
+            /* Override localhost_only with specified listen addr. */\r
+            ret->localhost_only = ipv4_is_loopback(u.sin.sin_addr);\r
+        }\r
+        addr = &u;\r
+        addrlen = sizeof(u.sin);\r
+        retcode = 0;\r
+#endif\r
+    }\r
+\r
+    if (retcode != 0) {\r
+        memset(&u,'\0',sizeof u);\r
+#ifndef NO_IPV6\r
+        if (address_family == AF_INET6) {\r
+            u.sin6.sin6_family = AF_INET6;\r
+            u.sin6.sin6_port = htons(port);\r
+            if (local_host_only)\r
+                u.sin6.sin6_addr = in6addr_loopback;\r
+            else\r
+                u.sin6.sin6_addr = in6addr_any;\r
+            addr = &u;\r
+            addrlen = sizeof(u.sin6);\r
+        } else\r
+#endif\r
+        {\r
+            u.sin.sin_family = AF_INET;\r
+            u.sin.sin_port = htons(port);\r
+           if (local_host_only)\r
+               u.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
+           else\r
+               u.sin.sin_addr.s_addr = htonl(INADDR_ANY);\r
+            addr = &u;\r
+            addrlen = sizeof(u.sin);\r
+        }\r
+    }\r
+\r
+    retcode = bind(s, &addr->sa, addrlen);\r
+    if (retcode < 0) {\r
+        close(s);\r
+       ret->error = strerror(errno);\r
+       return (Socket) ret;\r
+    }\r
+\r
+    if (listen(s, SOMAXCONN) < 0) {\r
+        close(s);\r
+       ret->error = strerror(errno);\r
+       return (Socket) ret;\r
+    }\r
+\r
+#ifndef NO_IPV6\r
+    /*\r
+     * If we were given ADDRTYPE_UNSPEC, we must also create an\r
+     * IPv4 listening socket and link it to this one.\r
+     */\r
+    if (address_family == AF_INET6 && orig_address_family == ADDRTYPE_UNSPEC) {\r
+        Actual_Socket other;\r
+\r
+        other = (Actual_Socket) sk_newlistener(srcaddr, port, plug,\r
+                                               local_host_only, ADDRTYPE_IPV4);\r
+\r
+        if (other) {\r
+            if (!other->error) {\r
+                other->parent = ret;\r
+                ret->child = other;\r
+            } else {\r
+                /* If we couldn't create a listening socket on IPv4 as well\r
+                 * as IPv6, we must return an error overall. */\r
+                close(s);\r
+                sfree(ret);\r
+                return (Socket) other;\r
+            }\r
+        }\r
+    }\r
+#endif\r
+\r
+    ret->s = s;\r
+\r
+    uxsel_tell(ret);\r
+    add234(sktree, ret);\r
+\r
+    return (Socket) ret;\r
+}\r
+\r
+static void sk_tcp_close(Socket sock)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+\r
+    if (s->child)\r
+        sk_tcp_close((Socket)s->child);\r
+\r
+    uxsel_del(s->s);\r
+    del234(sktree, s);\r
+    close(s->s);\r
+    if (s->addr)\r
+        sk_addr_free(s->addr);\r
+    sfree(s);\r
+}\r
+\r
+void *sk_getxdmdata(void *sock, int *lenp)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    union sockaddr_union u;\r
+    socklen_t addrlen;\r
+    char *buf;\r
+    static unsigned int unix_addr = 0xFFFFFFFF;\r
+\r
+    /*\r
+     * We must check that this socket really _is_ an Actual_Socket.\r
+     */\r
+    if (s->fn != &tcp_fn_table)\r
+       return NULL;                   /* failure */\r
+\r
+    addrlen = sizeof(u);\r
+    if (getsockname(s->s, &u.sa, &addrlen) < 0)\r
+       return NULL;\r
+    switch(u.sa.sa_family) {\r
+      case AF_INET:\r
+       *lenp = 6;\r
+       buf = snewn(*lenp, char);\r
+       PUT_32BIT_MSB_FIRST(buf, ntohl(u.sin.sin_addr.s_addr));\r
+       PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.sin.sin_port));\r
+       break;\r
+#ifndef NO_IPV6\r
+    case AF_INET6:\r
+       *lenp = 6;\r
+       buf = snewn(*lenp, char);\r
+       if (IN6_IS_ADDR_V4MAPPED(&u.sin6.sin6_addr)) {\r
+           memcpy(buf, u.sin6.sin6_addr.s6_addr + 12, 4);\r
+           PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.sin6.sin6_port));\r
+       } else\r
+           /* This is stupid, but it's what XLib does. */\r
+           memset(buf, 0, 6);\r
+       break;\r
+#endif\r
+      case AF_UNIX:\r
+       *lenp = 6;\r
+       buf = snewn(*lenp, char);\r
+       PUT_32BIT_MSB_FIRST(buf, unix_addr--);\r
+        PUT_16BIT_MSB_FIRST(buf+4, getpid());\r
+       break;\r
+\r
+       /* XXX IPV6 */\r
+\r
+      default:\r
+       return NULL;\r
+    }\r
+\r
+    return buf;\r
+}\r
+\r
+/*\r
+ * The function which tries to send on a socket once it's deemed\r
+ * writable.\r
+ */\r
+void try_send(Actual_Socket s)\r
+{\r
+    while (s->sending_oob || bufchain_size(&s->output_data) > 0) {\r
+       int nsent;\r
+       int err;\r
+       void *data;\r
+       int len, urgentflag;\r
+\r
+       if (s->sending_oob) {\r
+           urgentflag = MSG_OOB;\r
+           len = s->sending_oob;\r
+           data = &s->oobdata;\r
+       } else {\r
+           urgentflag = 0;\r
+           bufchain_prefix(&s->output_data, &data, &len);\r
+       }\r
+       nsent = send(s->s, data, len, urgentflag);\r
+       noise_ultralight(nsent);\r
+       if (nsent <= 0) {\r
+           err = (nsent < 0 ? errno : 0);\r
+           if (err == EWOULDBLOCK) {\r
+               /*\r
+                * Perfectly normal: we've sent all we can for the moment.\r
+                */\r
+               s->writable = FALSE;\r
+               return;\r
+           } else {\r
+               /*\r
+                * We unfortunately can't just call plug_closing(),\r
+                * because it's quite likely that we're currently\r
+                * _in_ a call from the code we'd be calling back\r
+                * to, so we'd have to make half the SSH code\r
+                * reentrant. Instead we flag a pending error on\r
+                * the socket, to be dealt with (by calling\r
+                * plug_closing()) at some suitable future moment.\r
+                */\r
+               s->pending_error = err;\r
+               return;\r
+           }\r
+       } else {\r
+           if (s->sending_oob) {\r
+               if (nsent < len) {\r
+                   memmove(s->oobdata, s->oobdata+nsent, len-nsent);\r
+                   s->sending_oob = len - nsent;\r
+               } else {\r
+                   s->sending_oob = 0;\r
+               }\r
+           } else {\r
+               bufchain_consume(&s->output_data, nsent);\r
+           }\r
+       }\r
+    }\r
+    uxsel_tell(s);\r
+}\r
+\r
+static int sk_tcp_write(Socket sock, const char *buf, int len)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+\r
+    /*\r
+     * Add the data to the buffer list on the socket.\r
+     */\r
+    bufchain_add(&s->output_data, buf, len);\r
+\r
+    /*\r
+     * Now try sending from the start of the buffer list.\r
+     */\r
+    if (s->writable)\r
+       try_send(s);\r
+\r
+    /*\r
+     * Update the select() status to correctly reflect whether or\r
+     * not we should be selecting for write.\r
+     */\r
+    uxsel_tell(s);\r
+\r
+    return bufchain_size(&s->output_data);\r
+}\r
+\r
+static int sk_tcp_write_oob(Socket sock, const char *buf, int len)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+\r
+    /*\r
+     * Replace the buffer list on the socket with the data.\r
+     */\r
+    bufchain_clear(&s->output_data);\r
+    assert(len <= sizeof(s->oobdata));\r
+    memcpy(s->oobdata, buf, len);\r
+    s->sending_oob = len;\r
+\r
+    /*\r
+     * Now try sending from the start of the buffer list.\r
+     */\r
+    if (s->writable)\r
+       try_send(s);\r
+\r
+    /*\r
+     * Update the select() status to correctly reflect whether or\r
+     * not we should be selecting for write.\r
+     */\r
+    uxsel_tell(s);\r
+\r
+    return s->sending_oob;\r
+}\r
+\r
+static int net_select_result(int fd, int event)\r
+{\r
+    int ret;\r
+    char buf[20480];                  /* nice big buffer for plenty of speed */\r
+    Actual_Socket s;\r
+    u_long atmark;\r
+\r
+    /* Find the Socket structure */\r
+    s = find234(sktree, &fd, cmpforsearch);\r
+    if (!s)\r
+       return 1;                      /* boggle */\r
+\r
+    noise_ultralight(event);\r
+\r
+    switch (event) {\r
+      case 4:                         /* exceptional */\r
+       if (!s->oobinline) {\r
+           /*\r
+            * On a non-oobinline socket, this indicates that we\r
+            * can immediately perform an OOB read and get back OOB\r
+            * data, which we will send to the back end with\r
+            * type==2 (urgent data).\r
+            */\r
+           ret = recv(s->s, buf, sizeof(buf), MSG_OOB);\r
+           noise_ultralight(ret);\r
+           if (ret <= 0) {\r
+                return plug_closing(s->plug,\r
+                                   ret == 0 ? "Internal networking trouble" :\r
+                                   strerror(errno), errno, 0);\r
+           } else {\r
+                /*\r
+                 * Receiving actual data on a socket means we can\r
+                 * stop falling back through the candidate\r
+                 * addresses to connect to.\r
+                 */\r
+                if (s->addr) {\r
+                    sk_addr_free(s->addr);\r
+                    s->addr = NULL;\r
+                }\r
+               return plug_receive(s->plug, 2, buf, ret);\r
+           }\r
+           break;\r
+       }\r
+\r
+       /*\r
+        * If we reach here, this is an oobinline socket, which\r
+        * means we should set s->oobpending and then deal with it\r
+        * when we get called for the readability event (which\r
+        * should also occur).\r
+        */\r
+       s->oobpending = TRUE;\r
+        break;\r
+      case 1:                         /* readable; also acceptance */\r
+       if (s->listener) {\r
+           /*\r
+            * On a listening socket, the readability event means a\r
+            * connection is ready to be accepted.\r
+            */\r
+           union sockaddr_union su;\r
+           socklen_t addrlen = sizeof(su);\r
+           int t;  /* socket of connection */\r
+            int fl;\r
+\r
+           memset(&su, 0, addrlen);\r
+           t = accept(s->s, &su.sa, &addrlen);\r
+           if (t < 0) {\r
+               break;\r
+           }\r
+\r
+            fl = fcntl(t, F_GETFL);\r
+            if (fl != -1)\r
+                fcntl(t, F_SETFL, fl | O_NONBLOCK);\r
+\r
+           if (s->localhost_only &&\r
+               !sockaddr_is_loopback(&su.sa)) {\r
+               close(t);              /* someone let nonlocal through?! */\r
+           } else if (plug_accepting(s->plug, t)) {\r
+               close(t);              /* denied or error */\r
+           }\r
+           break;\r
+       }\r
+\r
+       /*\r
+        * If we reach here, this is not a listening socket, so\r
+        * readability really means readability.\r
+        */\r
+\r
+       /* In the case the socket is still frozen, we don't even bother */\r
+       if (s->frozen) {\r
+           s->frozen_readable = 1;\r
+           break;\r
+       }\r
+\r
+       /*\r
+        * We have received data on the socket. For an oobinline\r
+        * socket, this might be data _before_ an urgent pointer,\r
+        * in which case we send it to the back end with type==1\r
+        * (data prior to urgent).\r
+        */\r
+       if (s->oobinline && s->oobpending) {\r
+           atmark = 1;\r
+           if (ioctl(s->s, SIOCATMARK, &atmark) == 0 && atmark)\r
+               s->oobpending = FALSE; /* clear this indicator */\r
+       } else\r
+           atmark = 1;\r
+\r
+       ret = recv(s->s, buf, s->oobpending ? 1 : sizeof(buf), 0);\r
+       noise_ultralight(ret);\r
+       if (ret < 0) {\r
+           if (errno == EWOULDBLOCK) {\r
+               break;\r
+           }\r
+       }\r
+       if (ret < 0) {\r
+            /*\r
+             * An error at this point _might_ be an error reported\r
+             * by a non-blocking connect(). So before we return a\r
+             * panic status to the user, let's just see whether\r
+             * that's the case.\r
+             */\r
+            int err = errno;\r
+           if (s->addr) {\r
+               plug_log(s->plug, 1, s->addr, s->port, strerror(err), err);\r
+               while (s->addr && sk_nextaddr(s->addr, &s->step)) {\r
+                   err = try_connect(s);\r
+               }\r
+           }\r
+            if (err != 0)\r
+                return plug_closing(s->plug, strerror(err), err, 0);\r
+       } else if (0 == ret) {\r
+           return plug_closing(s->plug, NULL, 0, 0);\r
+       } else {\r
+            /*\r
+             * Receiving actual data on a socket means we can\r
+             * stop falling back through the candidate\r
+             * addresses to connect to.\r
+             */\r
+            if (s->addr) {\r
+                sk_addr_free(s->addr);\r
+                s->addr = NULL;\r
+            }\r
+           return plug_receive(s->plug, atmark ? 0 : 1, buf, ret);\r
+       }\r
+       break;\r
+      case 2:                         /* writable */\r
+       if (!s->connected) {\r
+           /*\r
+            * select() reports a socket as _writable_ when an\r
+            * asynchronous connection is completed.\r
+            */\r
+           s->connected = s->writable = 1;\r
+           uxsel_tell(s);\r
+           break;\r
+       } else {\r
+           int bufsize_before, bufsize_after;\r
+           s->writable = 1;\r
+           bufsize_before = s->sending_oob + bufchain_size(&s->output_data);\r
+           try_send(s);\r
+           bufsize_after = s->sending_oob + bufchain_size(&s->output_data);\r
+           if (bufsize_after < bufsize_before)\r
+               plug_sent(s->plug, bufsize_after);\r
+       }\r
+       break;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Deal with socket errors detected in try_send().\r
+ */\r
+void net_pending_errors(void)\r
+{\r
+    int i;\r
+    Actual_Socket s;\r
+\r
+    /*\r
+     * This might be a fiddly business, because it's just possible\r
+     * that handling a pending error on one socket might cause\r
+     * others to be closed. (I can't think of any reason this might\r
+     * happen in current SSH implementation, but to maintain\r
+     * generality of this network layer I'll assume the worst.)\r
+     * \r
+     * So what we'll do is search the socket list for _one_ socket\r
+     * with a pending error, and then handle it, and then search\r
+     * the list again _from the beginning_. Repeat until we make a\r
+     * pass with no socket errors present. That way we are\r
+     * protected against the socket list changing under our feet.\r
+     */\r
+\r
+    do {\r
+       for (i = 0; (s = index234(sktree, i)) != NULL; i++) {\r
+           if (s->pending_error) {\r
+               /*\r
+                * An error has occurred on this socket. Pass it to the\r
+                * plug.\r
+                */\r
+               plug_closing(s->plug, strerror(s->pending_error),\r
+                            s->pending_error, 0);\r
+               break;\r
+           }\r
+       }\r
+    } while (s);\r
+}\r
+\r
+/*\r
+ * Each socket abstraction contains a `void *' private field in\r
+ * which the client can keep state.\r
+ */\r
+static void sk_tcp_set_private_ptr(Socket sock, void *ptr)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    s->private_ptr = ptr;\r
+}\r
+\r
+static void *sk_tcp_get_private_ptr(Socket sock)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    return s->private_ptr;\r
+}\r
+\r
+/*\r
+ * Special error values are returned from sk_namelookup and sk_new\r
+ * if there's a problem. These functions extract an error message,\r
+ * or return NULL if there's no problem.\r
+ */\r
+const char *sk_addr_error(SockAddr addr)\r
+{\r
+    return addr->error;\r
+}\r
+static const char *sk_tcp_socket_error(Socket sock)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    return s->error;\r
+}\r
+\r
+static void sk_tcp_set_frozen(Socket sock, int is_frozen)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    if (s->frozen == is_frozen)\r
+       return;\r
+    s->frozen = is_frozen;\r
+    if (!is_frozen && s->frozen_readable) {\r
+       char c;\r
+       recv(s->s, &c, 1, MSG_PEEK);\r
+    }\r
+    s->frozen_readable = 0;\r
+    uxsel_tell(s);\r
+}\r
+\r
+static void uxsel_tell(Actual_Socket s)\r
+{\r
+    int rwx = 0;\r
+    if (s->listener) {\r
+       rwx |= 1;                       /* read == accept */\r
+    } else {\r
+       if (!s->connected)\r
+           rwx |= 2;                   /* write == connect */\r
+       if (s->connected && !s->frozen)\r
+           rwx |= 1 | 4;               /* read, except */\r
+       if (bufchain_size(&s->output_data))\r
+           rwx |= 2;                   /* write */\r
+    }\r
+    uxsel_set(s->s, rwx, net_select_result);\r
+}\r
+\r
+int net_service_lookup(char *service)\r
+{\r
+    struct servent *se;\r
+    se = getservbyname(service, NULL);\r
+    if (se != NULL)\r
+       return ntohs(se->s_port);\r
+    else\r
+       return 0;\r
+}\r
+\r
+char *get_hostname(void)\r
+{\r
+    int len = 128;\r
+    char *hostname = NULL;\r
+    do {\r
+       len *= 2;\r
+       hostname = sresize(hostname, len, char);\r
+       if ((gethostname(hostname, len) < 0) &&\r
+           (errno != ENAMETOOLONG)) {\r
+           sfree(hostname);\r
+           hostname = NULL;\r
+           break;\r
+       }\r
+    } while (strlen(hostname) >= len-1);\r
+    return hostname;\r
+}\r
+\r
+SockAddr platform_get_x11_unix_address(const char *sockpath, int displaynum)\r
+{\r
+    SockAddr ret = snew(struct SockAddr_tag);\r
+    int n;\r
+\r
+    memset(ret, 0, sizeof *ret);\r
+    ret->superfamily = UNIX;\r
+    /*\r
+     * In special circumstances (notably Mac OS X Leopard), we'll\r
+     * have been passed an explicit Unix socket path.\r
+     */\r
+    if (sockpath) {\r
+       n = snprintf(ret->hostname, sizeof ret->hostname,\r
+                    "%s", sockpath);\r
+    } else {\r
+       n = snprintf(ret->hostname, sizeof ret->hostname,\r
+                    "%s%d", X11_UNIX_PATH, displaynum);\r
+    }\r
+\r
+    if (n < 0)\r
+       ret->error = "snprintf failed";\r
+    else if (n >= sizeof ret->hostname)\r
+       ret->error = "X11 UNIX name too long";\r
+\r
+#ifndef NO_IPV6\r
+    ret->ais = NULL;\r
+#else\r
+    ret->addresses = NULL;\r
+    ret->naddresses = 0;\r
+#endif\r
+    ret->refcount = 1;\r
+    return ret;\r
+}\r
diff --git a/putty/UNIX/UXNOISE.C b/putty/UNIX/UXNOISE.C
new file mode 100644 (file)
index 0000000..bbb1f73
--- /dev/null
@@ -0,0 +1,147 @@
+/*\r
+ * Noise generation for PuTTY's cryptographic random number\r
+ * generator.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <errno.h>\r
+\r
+#include <fcntl.h>\r
+#include <unistd.h>\r
+#include <sys/time.h>\r
+#include <sys/resource.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "storage.h"\r
+\r
+static int read_dev_urandom(char *buf, int len)\r
+{\r
+    int fd;\r
+    int ngot, ret;\r
+\r
+    fd = open("/dev/urandom", O_RDONLY);\r
+    if (fd < 0)\r
+       return 0;\r
+\r
+    ngot = 0;\r
+    while (ngot < len) {\r
+       ret = read(fd, buf+ngot, len-ngot);\r
+       if (ret < 0) {\r
+           close(fd);\r
+           return 0;\r
+       }\r
+       ngot += ret;\r
+    }\r
+\r
+    close(fd);\r
+\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * This function is called once, at PuTTY startup. It will do some\r
+ * slightly silly things such as fetching an entire process listing\r
+ * and scanning /tmp, load the saved random seed from disk, and\r
+ * also read 32 bytes out of /dev/urandom.\r
+ */\r
+\r
+void noise_get_heavy(void (*func) (void *, int))\r
+{\r
+    char buf[512];\r
+    FILE *fp;\r
+    int ret;\r
+    int got_dev_urandom = 0;\r
+\r
+    if (read_dev_urandom(buf, 32)) {\r
+       got_dev_urandom = 1;\r
+       func(buf, 32);\r
+    }\r
+\r
+    fp = popen("ps -axu 2>/dev/null", "r");\r
+    if (fp) {\r
+       while ( (ret = fread(buf, 1, sizeof(buf), fp)) > 0)\r
+           func(buf, ret);\r
+       pclose(fp);\r
+    } else if (!got_dev_urandom) {\r
+       fprintf(stderr, "popen: %s\n"\r
+               "Unable to access fallback entropy source\n", strerror(errno));\r
+       exit(1);\r
+    }\r
+\r
+    fp = popen("ls -al /tmp 2>/dev/null", "r");\r
+    if (fp) {\r
+       while ( (ret = fread(buf, 1, sizeof(buf), fp)) > 0)\r
+           func(buf, ret);\r
+       pclose(fp);\r
+    } else if (!got_dev_urandom) {\r
+       fprintf(stderr, "popen: %s\n"\r
+               "Unable to access fallback entropy source\n", strerror(errno));\r
+       exit(1);\r
+    }\r
+\r
+    read_random_seed(func);\r
+    random_save_seed();\r
+}\r
+\r
+void random_save_seed(void)\r
+{\r
+    int len;\r
+    void *data;\r
+\r
+    if (random_active) {\r
+       random_get_savedata(&data, &len);\r
+       write_random_seed(data, len);\r
+       sfree(data);\r
+    }\r
+}\r
+\r
+/*\r
+ * This function is called every time the random pool needs\r
+ * stirring, and will acquire the system time.\r
+ */\r
+void noise_get_light(void (*func) (void *, int))\r
+{\r
+    struct timeval tv;\r
+    gettimeofday(&tv, NULL);\r
+    func(&tv, sizeof(tv));\r
+}\r
+\r
+/*\r
+ * This function is called on a timer, and grabs as much changeable\r
+ * system data as it can quickly get its hands on.\r
+ */\r
+void noise_regular(void)\r
+{\r
+    int fd;\r
+    int ret;\r
+    char buf[512];\r
+    struct rusage rusage;\r
+\r
+    if ((fd = open("/proc/meminfo", O_RDONLY)) >= 0) {\r
+       while ( (ret = read(fd, buf, sizeof(buf))) > 0)\r
+           random_add_noise(buf, ret);\r
+       close(fd);\r
+    }\r
+    if ((fd = open("/proc/stat", O_RDONLY)) >= 0) {\r
+       while ( (ret = read(fd, buf, sizeof(buf))) > 0)\r
+           random_add_noise(buf, ret);\r
+       close(fd);\r
+    }\r
+    getrusage(RUSAGE_SELF, &rusage);\r
+    random_add_noise(&rusage, sizeof(rusage));\r
+}\r
+\r
+/*\r
+ * This function is called on every keypress or mouse move, and\r
+ * will add the current time to the noise pool. It gets the scan\r
+ * code or mouse position passed in, and adds that too.\r
+ */\r
+void noise_ultralight(unsigned long data)\r
+{\r
+    struct timeval tv;\r
+    gettimeofday(&tv, NULL);\r
+    random_add_noise(&tv, sizeof(tv));\r
+    random_add_noise(&data, sizeof(data));\r
+}\r
diff --git a/putty/UNIX/UXPLINK.C b/putty/UNIX/UXPLINK.C
new file mode 100644 (file)
index 0000000..90baa68
--- /dev/null
@@ -0,0 +1,1087 @@
+/*\r
+ * PLink - a command-line (stdin/stdout) variant of PuTTY.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <errno.h>\r
+#include <assert.h>\r
+#include <stdarg.h>\r
+#include <signal.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#include <termios.h>\r
+#include <pwd.h>\r
+#include <sys/ioctl.h>\r
+#include <sys/time.h>\r
+#ifndef HAVE_NO_SYS_SELECT_H\r
+#include <sys/select.h>\r
+#endif\r
+\r
+#define PUTTY_DO_GLOBALS              /* actually _define_ globals */\r
+#include "putty.h"\r
+#include "storage.h"\r
+#include "tree234.h"\r
+\r
+#define MAX_STDIN_BACKLOG 4096\r
+\r
+void *logctx;\r
+\r
+static struct termios orig_termios;\r
+\r
+void fatalbox(char *p, ...)\r
+{\r
+    struct termios cf;\r
+    va_list ap;\r
+    premsg(&cf);\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    postmsg(&cf);\r
+    if (logctx) {\r
+        log_free(logctx);\r
+        logctx = NULL;\r
+    }\r
+    cleanup_exit(1);\r
+}\r
+void modalfatalbox(char *p, ...)\r
+{\r
+    struct termios cf;\r
+    va_list ap;\r
+    premsg(&cf);\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    postmsg(&cf);\r
+    if (logctx) {\r
+        log_free(logctx);\r
+        logctx = NULL;\r
+    }\r
+    cleanup_exit(1);\r
+}\r
+void connection_fatal(void *frontend, char *p, ...)\r
+{\r
+    struct termios cf;\r
+    va_list ap;\r
+    premsg(&cf);\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    postmsg(&cf);\r
+    if (logctx) {\r
+        log_free(logctx);\r
+        logctx = NULL;\r
+    }\r
+    cleanup_exit(1);\r
+}\r
+void cmdline_error(char *p, ...)\r
+{\r
+    struct termios cf;\r
+    va_list ap;\r
+    premsg(&cf);\r
+    fprintf(stderr, "plink: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    postmsg(&cf);\r
+    exit(1);\r
+}\r
+\r
+static int local_tty = FALSE; /* do we have a local tty? */\r
+\r
+static Backend *back;\r
+static void *backhandle;\r
+static Config cfg;\r
+\r
+/*\r
+ * Default settings that are specific to pterm.\r
+ */\r
+char *platform_default_s(const char *name)\r
+{\r
+    if (!strcmp(name, "TermType"))\r
+       return dupstr(getenv("TERM"));\r
+     if (!strcmp(name, "UserName"))\r
+       return get_username();\r
+    if (!strcmp(name, "SerialLine"))\r
+       return dupstr("/dev/ttyS0");\r
+    return NULL;\r
+}\r
+\r
+int platform_default_i(const char *name, int def)\r
+{\r
+    if (!strcmp(name, "TermWidth") ||\r
+       !strcmp(name, "TermHeight")) {\r
+       struct winsize size;\r
+       if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0)\r
+           return (!strcmp(name, "TermWidth") ? size.ws_col : size.ws_row);\r
+    }\r
+    return def;\r
+}\r
+\r
+FontSpec platform_default_fontspec(const char *name)\r
+{\r
+    FontSpec ret;\r
+    *ret.name = '\0';\r
+    return ret;\r
+}\r
+\r
+Filename platform_default_filename(const char *name)\r
+{\r
+    Filename ret;\r
+    if (!strcmp(name, "LogFileName"))\r
+       strcpy(ret.path, "putty.log");\r
+    else\r
+       *ret.path = '\0';\r
+    return ret;\r
+}\r
+\r
+char *x_get_default(const char *key)\r
+{\r
+    return NULL;                      /* this is a stub */\r
+}\r
+int term_ldisc(Terminal *term, int mode)\r
+{\r
+    return FALSE;\r
+}\r
+void ldisc_update(void *frontend, int echo, int edit)\r
+{\r
+    /* Update stdin read mode to reflect changes in line discipline. */\r
+    struct termios mode;\r
+\r
+    if (!local_tty) return;\r
+\r
+    mode = orig_termios;\r
+\r
+    if (echo)\r
+       mode.c_lflag |= ECHO;\r
+    else\r
+       mode.c_lflag &= ~ECHO;\r
+\r
+    if (edit) {\r
+       mode.c_iflag |= ICRNL;\r
+       mode.c_lflag |= ISIG | ICANON;\r
+       mode.c_oflag |= OPOST;\r
+    } else {\r
+       mode.c_iflag &= ~ICRNL;\r
+       mode.c_lflag &= ~(ISIG | ICANON);\r
+       mode.c_oflag &= ~OPOST;\r
+       /* Solaris sets these to unhelpful values */\r
+       mode.c_cc[VMIN] = 1;\r
+       mode.c_cc[VTIME] = 0;\r
+       /* FIXME: perhaps what we do with IXON/IXOFF should be an\r
+        * argument to ldisc_update(), to allow implementation of SSH-2\r
+        * "xon-xoff" and Rlogin's equivalent? */\r
+       mode.c_iflag &= ~IXON;\r
+       mode.c_iflag &= ~IXOFF;\r
+    }\r
+    /* \r
+     * Mark parity errors and (more important) BREAK on input.  This\r
+     * is more complex than it need be because POSIX-2001 suggests\r
+     * that escaping of valid 0xff in the input stream is dependent on\r
+     * IGNPAR being clear even though marking of BREAK isn't.  NetBSD\r
+     * 2.0 goes one worse and makes it dependent on INPCK too.  We\r
+     * deal with this by forcing these flags into a useful state and\r
+     * then faking the state in which we found them in from_tty() if\r
+     * we get passed a parity or framing error.\r
+     */\r
+    mode.c_iflag = (mode.c_iflag | INPCK | PARMRK) & ~IGNPAR;\r
+\r
+    tcsetattr(STDIN_FILENO, TCSANOW, &mode);\r
+}\r
+\r
+/* Helper function to extract a special character from a termios. */\r
+static char *get_ttychar(struct termios *t, int index)\r
+{\r
+    cc_t c = t->c_cc[index];\r
+#if defined(_POSIX_VDISABLE)\r
+    if (c == _POSIX_VDISABLE)\r
+       return dupprintf("");\r
+#endif\r
+    return dupprintf("^<%d>", c);\r
+}\r
+\r
+char *get_ttymode(void *frontend, const char *mode)\r
+{\r
+    /*\r
+     * Propagate appropriate terminal modes from the local terminal,\r
+     * if any.\r
+     */\r
+    if (!local_tty) return NULL;\r
+\r
+#define GET_CHAR(ourname, uxname) \\r
+    do { \\r
+       if (strcmp(mode, ourname) == 0) \\r
+           return get_ttychar(&orig_termios, uxname); \\r
+    } while(0)\r
+#define GET_BOOL(ourname, uxname, uxmemb, transform) \\r
+    do { \\r
+       if (strcmp(mode, ourname) == 0) { \\r
+           int b = (orig_termios.uxmemb & uxname) != 0; \\r
+           transform; \\r
+           return dupprintf("%d", b); \\r
+       } \\r
+    } while (0)\r
+\r
+    /*\r
+     * Modes that want to be the same on all terminal devices involved.\r
+     */\r
+    /* All the special characters supported by SSH */\r
+#if defined(VINTR)\r
+    GET_CHAR("INTR", VINTR);\r
+#endif\r
+#if defined(VQUIT)\r
+    GET_CHAR("QUIT", VQUIT);\r
+#endif\r
+#if defined(VERASE)\r
+    GET_CHAR("ERASE", VERASE);\r
+#endif\r
+#if defined(VKILL)\r
+    GET_CHAR("KILL", VKILL);\r
+#endif\r
+#if defined(VEOF)\r
+    GET_CHAR("EOF", VEOF);\r
+#endif\r
+#if defined(VEOL)\r
+    GET_CHAR("EOL", VEOL);\r
+#endif\r
+#if defined(VEOL2)\r
+    GET_CHAR("EOL2", VEOL2);\r
+#endif\r
+#if defined(VSTART)\r
+    GET_CHAR("START", VSTART);\r
+#endif\r
+#if defined(VSTOP)\r
+    GET_CHAR("STOP", VSTOP);\r
+#endif\r
+#if defined(VSUSP)\r
+    GET_CHAR("SUSP", VSUSP);\r
+#endif\r
+#if defined(VDSUSP)\r
+    GET_CHAR("DSUSP", VDSUSP);\r
+#endif\r
+#if defined(VREPRINT)\r
+    GET_CHAR("REPRINT", VREPRINT);\r
+#endif\r
+#if defined(VWERASE)\r
+    GET_CHAR("WERASE", VWERASE);\r
+#endif\r
+#if defined(VLNEXT)\r
+    GET_CHAR("LNEXT", VLNEXT);\r
+#endif\r
+#if defined(VFLUSH)\r
+    GET_CHAR("FLUSH", VFLUSH);\r
+#endif\r
+#if defined(VSWTCH)\r
+    GET_CHAR("SWTCH", VSWTCH);\r
+#endif\r
+#if defined(VSTATUS)\r
+    GET_CHAR("STATUS", VSTATUS);\r
+#endif\r
+#if defined(VDISCARD)\r
+    GET_CHAR("DISCARD", VDISCARD);\r
+#endif\r
+    /* Modes that "configure" other major modes. These should probably be\r
+     * considered as user preferences. */\r
+    /* Configuration of ICANON */\r
+#if defined(ECHOK)\r
+    GET_BOOL("ECHOK", ECHOK, c_lflag, );\r
+#endif\r
+#if defined(ECHOKE)\r
+    GET_BOOL("ECHOKE", ECHOKE, c_lflag, );\r
+#endif\r
+#if defined(ECHOE)\r
+    GET_BOOL("ECHOE", ECHOE, c_lflag, );\r
+#endif\r
+#if defined(ECHONL)\r
+    GET_BOOL("ECHONL", ECHONL, c_lflag, );\r
+#endif\r
+#if defined(XCASE)\r
+    GET_BOOL("XCASE", XCASE, c_lflag, );\r
+#endif\r
+    /* Configuration of ECHO */\r
+#if defined(ECHOCTL)\r
+    GET_BOOL("ECHOCTL", ECHOCTL, c_lflag, );\r
+#endif\r
+    /* Configuration of IXON/IXOFF */\r
+#if defined(IXANY)\r
+    GET_BOOL("IXANY", IXANY, c_iflag, );\r
+#endif\r
+    /* Configuration of OPOST */\r
+#if defined(OLCUC)\r
+    GET_BOOL("OLCUC", OLCUC, c_oflag, );\r
+#endif\r
+#if defined(ONLCR)\r
+    GET_BOOL("ONLCR", ONLCR, c_oflag, );\r
+#endif\r
+#if defined(OCRNL)\r
+    GET_BOOL("OCRNL", OCRNL, c_oflag, );\r
+#endif\r
+#if defined(ONOCR)\r
+    GET_BOOL("ONOCR", ONOCR, c_oflag, );\r
+#endif\r
+#if defined(ONLRET)\r
+    GET_BOOL("ONLRET", ONLRET, c_oflag, );\r
+#endif\r
+\r
+    /*\r
+     * Modes that want to be set in only one place, and that we have\r
+     * squashed locally.\r
+     */\r
+#if defined(ISIG)\r
+    GET_BOOL("ISIG", ISIG, c_lflag, );\r
+#endif\r
+#if defined(ICANON)\r
+    GET_BOOL("ICANON", ICANON, c_lflag, );\r
+#endif\r
+#if defined(ECHO)\r
+    GET_BOOL("ECHO", ECHO, c_lflag, );\r
+#endif\r
+#if defined(IXON)\r
+    GET_BOOL("IXON", IXON, c_iflag, );\r
+#endif\r
+#if defined(IXOFF)\r
+    GET_BOOL("IXOFF", IXOFF, c_iflag, );\r
+#endif\r
+#if defined(OPOST)\r
+    GET_BOOL("OPOST", OPOST, c_oflag, );\r
+#endif\r
+\r
+    /*\r
+     * We do not propagate the following modes:\r
+     *  - Parity/serial settings, which are a local affair and don't\r
+     *    make sense propagated over SSH's 8-bit byte-stream.\r
+     *      IGNPAR PARMRK INPCK CS7 CS8 PARENB PARODD\r
+     *  - Things that want to be enabled in one place that we don't\r
+     *    squash locally.\r
+     *      IUCLC\r
+     *  - Status bits.\r
+     *      PENDIN\r
+     *  - Things I don't know what to do with. (FIXME)\r
+     *      ISTRIP IMAXBEL NOFLSH TOSTOP IEXTEN\r
+     *      INLCR IGNCR ICRNL\r
+     */\r
+\r
+#undef GET_CHAR\r
+#undef GET_BOOL\r
+\r
+    /* Fall through to here for unrecognised names, or ones that are\r
+     * unsupported on this platform */\r
+    return NULL;\r
+}\r
+\r
+void cleanup_termios(void)\r
+{\r
+    if (local_tty)\r
+       tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);\r
+}\r
+\r
+bufchain stdout_data, stderr_data;\r
+\r
+int try_output(int is_stderr)\r
+{\r
+    bufchain *chain = (is_stderr ? &stderr_data : &stdout_data);\r
+    int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO);\r
+    void *senddata;\r
+    int sendlen, ret, fl;\r
+\r
+    if (bufchain_size(chain) == 0)\r
+        return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);\r
+\r
+    fl = fcntl(fd, F_GETFL);\r
+    if (fl != -1 && !(fl & O_NONBLOCK))\r
+       fcntl(fd, F_SETFL, fl | O_NONBLOCK);\r
+    do {\r
+       bufchain_prefix(chain, &senddata, &sendlen);\r
+       ret = write(fd, senddata, sendlen);\r
+       if (ret > 0)\r
+           bufchain_consume(chain, ret);\r
+    } while (ret == sendlen && bufchain_size(chain) != 0);\r
+    if (fl != -1 && !(fl & O_NONBLOCK))\r
+       fcntl(fd, F_SETFL, fl);\r
+    if (ret < 0 && errno != EAGAIN) {\r
+       perror(is_stderr ? "stderr: write" : "stdout: write");\r
+       exit(1);\r
+    }\r
+    return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);\r
+}\r
+\r
+int from_backend(void *frontend_handle, int is_stderr,\r
+                const char *data, int len)\r
+{\r
+    if (is_stderr) {\r
+       bufchain_add(&stderr_data, data, len);\r
+       return try_output(TRUE);\r
+    } else {\r
+       bufchain_add(&stdout_data, data, len);\r
+       return try_output(FALSE);\r
+    }\r
+}\r
+\r
+int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
+{\r
+    /*\r
+     * No "untrusted" output should get here (the way the code is\r
+     * currently, it's all diverted by FLAG_STDERR).\r
+     */\r
+    assert(!"Unexpected call to from_backend_untrusted()");\r
+    return 0; /* not reached */\r
+}\r
+\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    int ret;\r
+    ret = cmdline_get_passwd_input(p, in, inlen);\r
+    if (ret == -1)\r
+       ret = console_get_userpass_input(p, in, inlen);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Handle data from a local tty in PARMRK format.\r
+ */\r
+static void from_tty(void *vbuf, unsigned len)\r
+{\r
+    char *p, *q, *end, *buf = vbuf;\r
+    static enum {NORMAL, FF, FF00} state = NORMAL;\r
+\r
+    p = buf; end = buf + len;\r
+    while (p < end) {\r
+       switch (state) {\r
+           case NORMAL:\r
+               if (*p == '\xff') {\r
+                   p++;\r
+                   state = FF;\r
+               } else {\r
+                   q = memchr(p, '\xff', end - p);\r
+                   if (q == NULL) q = end;\r
+                   back->send(backhandle, p, q - p);\r
+                   p = q;\r
+               }\r
+               break;\r
+           case FF:\r
+               if (*p == '\xff') {\r
+                   back->send(backhandle, p, 1);\r
+                   p++;\r
+                   state = NORMAL;\r
+               } else if (*p == '\0') {\r
+                   p++;\r
+                   state = FF00;\r
+               } else abort();\r
+               break;\r
+           case FF00:\r
+               if (*p == '\0') {\r
+                   back->special(backhandle, TS_BRK);\r
+               } else {\r
+                   /* \r
+                    * Pretend that PARMRK wasn't set.  This involves\r
+                    * faking what INPCK and IGNPAR would have done if\r
+                    * we hadn't overridden them.  Unfortunately, we\r
+                    * can't do this entirely correctly because INPCK\r
+                    * distinguishes between framing and parity\r
+                    * errors, but PARMRK format represents both in\r
+                    * the same way.  We assume that parity errors are\r
+                    * more common than framing errors, and hence\r
+                    * treat all input errors as being subject to\r
+                    * INPCK.\r
+                    */\r
+                   if (orig_termios.c_iflag & INPCK) {\r
+                       /* If IGNPAR is set, we throw away the character. */\r
+                       if (!(orig_termios.c_iflag & IGNPAR)) {\r
+                           /* PE/FE get passed on as NUL. */\r
+                           *p = 0;\r
+                           back->send(backhandle, p, 1);\r
+                       }\r
+                   } else {\r
+                       /* INPCK not set.  Assume we got a parity error. */\r
+                       back->send(backhandle, p, 1);\r
+                   }\r
+               }\r
+               p++;\r
+               state = NORMAL;\r
+       }\r
+    }\r
+}\r
+\r
+int signalpipe[2];\r
+\r
+void sigwinch(int signum)\r
+{\r
+    if (write(signalpipe[1], "x", 1) <= 0)\r
+       /* not much we can do about it */;\r
+}\r
+\r
+/*\r
+ * In Plink our selects are synchronous, so these functions are\r
+ * empty stubs.\r
+ */\r
+int uxsel_input_add(int fd, int rwx) { return 0; }\r
+void uxsel_input_remove(int id) { }\r
+\r
+/*\r
+ * Short description of parameters.\r
+ */\r
+static void usage(void)\r
+{\r
+    printf("PuTTY Link: command-line connection utility\n");\r
+    printf("%s\n", ver);\r
+    printf("Usage: plink [options] [user@]host [command]\n");\r
+    printf("       (\"host\" can also be a PuTTY saved session name)\n");\r
+    printf("Options:\n");\r
+    printf("  -V        print version information and exit\n");\r
+    printf("  -pgpfp    print PGP key fingerprints and exit\n");\r
+    printf("  -v        show verbose messages\n");\r
+    printf("  -load sessname  Load settings from saved session\n");\r
+    printf("  -ssh -telnet -rlogin -raw -serial\n");\r
+    printf("            force use of a particular protocol\n");\r
+    printf("  -P port   connect to specified port\n");\r
+    printf("  -l user   connect with specified username\n");\r
+    printf("  -batch    disable all interactive prompts\n");\r
+    printf("The following options only apply to SSH connections:\n");\r
+    printf("  -pw passw login with specified password\n");\r
+    printf("  -D [listen-IP:]listen-port\n");\r
+    printf("            Dynamic SOCKS-based port forwarding\n");\r
+    printf("  -L [listen-IP:]listen-port:host:port\n");\r
+    printf("            Forward local port to remote address\n");\r
+    printf("  -R [listen-IP:]listen-port:host:port\n");\r
+    printf("            Forward remote port to local address\n");\r
+    printf("  -X -x     enable / disable X11 forwarding\n");\r
+    printf("  -A -a     enable / disable agent forwarding\n");\r
+    printf("  -t -T     enable / disable pty allocation\n");\r
+    printf("  -1 -2     force use of particular protocol version\n");\r
+    printf("  -4 -6     force use of IPv4 or IPv6\n");\r
+    printf("  -C        enable compression\n");\r
+    printf("  -i key    private key file for authentication\n");\r
+    printf("  -noagent  disable use of Pageant\n");\r
+    printf("  -agent    enable use of Pageant\n");\r
+    printf("  -m file   read remote command(s) from file\n");\r
+    printf("  -s        remote command is an SSH subsystem (SSH-2 only)\n");\r
+    printf("  -N        don't start a shell/command (SSH-2 only)\n");\r
+    printf("  -nc host:port\n");\r
+    printf("            open tunnel in place of session (SSH-2 only)\n");\r
+    printf("  -sercfg configuration-string (e.g. 19200,8,n,1,X)\n");\r
+    printf("            Specify the serial configuration (serial only)\n");\r
+    exit(1);\r
+}\r
+\r
+static void version(void)\r
+{\r
+    printf("plink: %s\n", ver);\r
+    exit(1);\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    int sending;\r
+    int portnumber = -1;\r
+    int *fdlist;\r
+    int fd;\r
+    int i, fdcount, fdsize, fdstate;\r
+    int connopen;\r
+    int exitcode;\r
+    int errors;\r
+    int use_subsystem = 0;\r
+    int got_host = FALSE;\r
+    long now;\r
+\r
+    fdlist = NULL;\r
+    fdcount = fdsize = 0;\r
+    /*\r
+     * Initialise port and protocol to sensible defaults. (These\r
+     * will be overridden by more or less anything.)\r
+     */\r
+    default_protocol = PROT_SSH;\r
+    default_port = 22;\r
+\r
+    flags = FLAG_STDERR | FLAG_STDERR_TTY;\r
+\r
+    stderr_tty_init();\r
+    /*\r
+     * Process the command line.\r
+     */\r
+    do_defaults(NULL, &cfg);\r
+    loaded_session = FALSE;\r
+    default_protocol = cfg.protocol;\r
+    default_port = cfg.port;\r
+    errors = 0;\r
+    {\r
+       /*\r
+        * Override the default protocol if PLINK_PROTOCOL is set.\r
+        */\r
+       char *p = getenv("PLINK_PROTOCOL");\r
+       if (p) {\r
+           const Backend *b = backend_from_name(p);\r
+           if (b) {\r
+               default_protocol = cfg.protocol = b->protocol;\r
+               default_port = cfg.port = b->default_port;\r
+           }\r
+       }\r
+    }\r
+    while (--argc) {\r
+       char *p = *++argv;\r
+       if (*p == '-') {\r
+           int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),\r
+                                           1, &cfg);\r
+           if (ret == -2) {\r
+               fprintf(stderr,\r
+                       "plink: option \"%s\" requires an argument\n", p);\r
+               errors = 1;\r
+           } else if (ret == 2) {\r
+               --argc, ++argv;\r
+           } else if (ret == 1) {\r
+               continue;\r
+           } else if (!strcmp(p, "-batch")) {\r
+               console_batch_mode = 1;\r
+           } else if (!strcmp(p, "-s")) {\r
+                /* Save status to write to cfg later. */\r
+               use_subsystem = 1;\r
+           } else if (!strcmp(p, "-V")) {\r
+                version();\r
+            } else if (!strcmp(p, "-pgpfp")) {\r
+                pgp_fingerprints();\r
+                exit(1);\r
+           } else if (!strcmp(p, "-o")) {\r
+                if (argc <= 1) {\r
+                    fprintf(stderr,\r
+                            "plink: option \"-o\" requires an argument\n");\r
+                   errors = 1;\r
+               } else {\r
+                    --argc;\r
+                   provide_xrm_string(*++argv);\r
+               }\r
+           } else {\r
+               fprintf(stderr, "plink: unknown option \"%s\"\n", p);\r
+               errors = 1;\r
+           }\r
+       } else if (*p) {\r
+           if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
+               char *q = p;\r
+\r
+               /*\r
+                * If the hostname starts with "telnet:", set the\r
+                * protocol to Telnet and process the string as a\r
+                * Telnet URL.\r
+                */\r
+               if (!strncmp(q, "telnet:", 7)) {\r
+                   char c;\r
+\r
+                   q += 7;\r
+                   if (q[0] == '/' && q[1] == '/')\r
+                       q += 2;\r
+                   cfg.protocol = PROT_TELNET;\r
+                   p = q;\r
+                   while (*p && *p != ':' && *p != '/')\r
+                       p++;\r
+                   c = *p;\r
+                   if (*p)\r
+                       *p++ = '\0';\r
+                   if (c == ':')\r
+                       cfg.port = atoi(p);\r
+                   else\r
+                       cfg.port = -1;\r
+                   strncpy(cfg.host, q, sizeof(cfg.host) - 1);\r
+                   cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+                   got_host = TRUE;\r
+               } else {\r
+                   char *r, *user, *host;\r
+                   /*\r
+                    * Before we process the [user@]host string, we\r
+                    * first check for the presence of a protocol\r
+                    * prefix (a protocol name followed by ",").\r
+                    */\r
+                   r = strchr(p, ',');\r
+                   if (r) {\r
+                       const Backend *b;\r
+                       *r = '\0';\r
+                       b = backend_from_name(p);\r
+                       if (b) {\r
+                           default_protocol = cfg.protocol = b->protocol;\r
+                           portnumber = b->default_port;\r
+                       }\r
+                       p = r + 1;\r
+                   }\r
+\r
+                   /*\r
+                    * A nonzero length string followed by an @ is treated\r
+                    * as a username. (We discount an _initial_ @.) The\r
+                    * rest of the string (or the whole string if no @)\r
+                    * is treated as a session name and/or hostname.\r
+                    */\r
+                   r = strrchr(p, '@');\r
+                   if (r == p)\r
+                       p++, r = NULL; /* discount initial @ */\r
+                   if (r) {\r
+                       *r++ = '\0';\r
+                       user = p, host = r;\r
+                   } else {\r
+                       user = NULL, host = p;\r
+                   }\r
+\r
+                   /*\r
+                    * Now attempt to load a saved session with the\r
+                    * same name as the hostname.\r
+                    */\r
+                   {\r
+                       Config cfg2;\r
+                       do_defaults(host, &cfg2);\r
+                       if (loaded_session || !cfg_launchable(&cfg2)) {\r
+                           /* No settings for this host; use defaults */\r
+                           /* (or session was already loaded with -load) */\r
+                           strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
+                           cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+                           cfg.port = default_port;\r
+                           got_host = TRUE;\r
+                       } else {\r
+                           cfg = cfg2;\r
+                           loaded_session = TRUE;\r
+                       }\r
+                   }\r
+\r
+                   if (user) {\r
+                       /* Patch in specified username. */\r
+                       strncpy(cfg.username, user,\r
+                               sizeof(cfg.username) - 1);\r
+                       cfg.username[sizeof(cfg.username) - 1] = '\0';\r
+                   }\r
+\r
+               }\r
+           } else {\r
+               char *command;\r
+               int cmdlen, cmdsize;\r
+               cmdlen = cmdsize = 0;\r
+               command = NULL;\r
+\r
+               while (argc) {\r
+                   while (*p) {\r
+                       if (cmdlen >= cmdsize) {\r
+                           cmdsize = cmdlen + 512;\r
+                           command = sresize(command, cmdsize, char);\r
+                       }\r
+                       command[cmdlen++]=*p++;\r
+                   }\r
+                   if (cmdlen >= cmdsize) {\r
+                       cmdsize = cmdlen + 512;\r
+                       command = sresize(command, cmdsize, char);\r
+                   }\r
+                   command[cmdlen++]=' '; /* always add trailing space */\r
+                   if (--argc) p = *++argv;\r
+               }\r
+               if (cmdlen) command[--cmdlen]='\0';\r
+                                      /* change trailing blank to NUL */\r
+               cfg.remote_cmd_ptr = command;\r
+               cfg.remote_cmd_ptr2 = NULL;\r
+               cfg.nopty = TRUE;      /* command => no terminal */\r
+\r
+               break;                 /* done with cmdline */\r
+           }\r
+       }\r
+    }\r
+\r
+    if (errors)\r
+       return 1;\r
+\r
+    if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
+       usage();\r
+    }\r
+\r
+    /*\r
+     * Trim leading whitespace off the hostname if it's there.\r
+     */\r
+    {\r
+       int space = strspn(cfg.host, " \t");\r
+       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
+    }\r
+\r
+    /* See if host is of the form user@host */\r
+    if (cfg.host[0] != '\0') {\r
+       char *atsign = strrchr(cfg.host, '@');\r
+       /* Make sure we're not overflowing the user field */\r
+       if (atsign) {\r
+           if (atsign - cfg.host < sizeof cfg.username) {\r
+               strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
+               cfg.username[atsign - cfg.host] = '\0';\r
+           }\r
+           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Perform command-line overrides on session configuration.\r
+     */\r
+    cmdline_run_saved(&cfg);\r
+\r
+    /*\r
+     * Apply subsystem status.\r
+     */\r
+    if (use_subsystem)\r
+        cfg.ssh_subsys = TRUE;\r
+\r
+    /*\r
+     * Trim a colon suffix off the hostname if it's there.\r
+     */\r
+    cfg.host[strcspn(cfg.host, ":")] = '\0';\r
+\r
+    /*\r
+     * Remove any remaining whitespace from the hostname.\r
+     */\r
+    {\r
+       int p1 = 0, p2 = 0;\r
+       while (cfg.host[p2] != '\0') {\r
+           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
+               cfg.host[p1] = cfg.host[p2];\r
+               p1++;\r
+           }\r
+           p2++;\r
+       }\r
+       cfg.host[p1] = '\0';\r
+    }\r
+\r
+    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)\r
+       flags |= FLAG_INTERACTIVE;\r
+\r
+    /*\r
+     * Select protocol. This is farmed out into a table in a\r
+     * separate file to enable an ssh-free variant.\r
+     */\r
+    back = backend_from_proto(cfg.protocol);\r
+    if (back == NULL) {\r
+       fprintf(stderr,\r
+               "Internal fault: Unsupported protocol found\n");\r
+       return 1;\r
+    }\r
+\r
+    /*\r
+     * Select port.\r
+     */\r
+    if (portnumber != -1)\r
+       cfg.port = portnumber;\r
+\r
+    /*\r
+     * Set up the pipe we'll use to tell us about SIGWINCH.\r
+     */\r
+    if (pipe(signalpipe) < 0) {\r
+       perror("pipe");\r
+       exit(1);\r
+    }\r
+    putty_signal(SIGWINCH, sigwinch);\r
+\r
+    sk_init();\r
+    uxsel_init();\r
+\r
+    /*\r
+     * Unix Plink doesn't provide any way to add forwardings after the\r
+     * connection is set up, so if there are none now, we can safely set\r
+     * the "simple" flag.\r
+     */\r
+    if (cfg.protocol == PROT_SSH && !cfg.x11_forward &&        !cfg.agentfwd &&\r
+       cfg.portfwd[0] == '\0' && cfg.portfwd[1] == '\0')\r
+       cfg.ssh_simple = TRUE;\r
+    /*\r
+     * Start up the connection.\r
+     */\r
+    logctx = log_init(NULL, &cfg);\r
+    console_provide_logctx(logctx);\r
+    {\r
+       const char *error;\r
+       char *realhost;\r
+       /* nodelay is only useful if stdin is a terminal device */\r
+       int nodelay = cfg.tcp_nodelay && isatty(0);\r
+\r
+       error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,\r
+                          &realhost, nodelay, cfg.tcp_keepalives);\r
+       if (error) {\r
+           fprintf(stderr, "Unable to open connection:\n%s\n", error);\r
+           return 1;\r
+       }\r
+       back->provide_logctx(backhandle, logctx);\r
+       ldisc_create(&cfg, NULL, back, backhandle, NULL);\r
+       sfree(realhost);\r
+    }\r
+    connopen = 1;\r
+\r
+    /*\r
+     * Set up the initial console mode. We don't care if this call\r
+     * fails, because we know we aren't necessarily running in a\r
+     * console.\r
+     */\r
+    local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0);\r
+    atexit(cleanup_termios);\r
+    ldisc_update(NULL, 1, 1);\r
+    sending = FALSE;\r
+    now = GETTICKCOUNT();\r
+\r
+    while (1) {\r
+       fd_set rset, wset, xset;\r
+       int maxfd;\r
+       int rwx;\r
+       int ret;\r
+\r
+       FD_ZERO(&rset);\r
+       FD_ZERO(&wset);\r
+       FD_ZERO(&xset);\r
+       maxfd = 0;\r
+\r
+       FD_SET_MAX(signalpipe[0], maxfd, rset);\r
+\r
+       if (connopen && !sending &&\r
+           back->connected(backhandle) &&\r
+           back->sendok(backhandle) &&\r
+           back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {\r
+           /* If we're OK to send, then try to read from stdin. */\r
+           FD_SET_MAX(STDIN_FILENO, maxfd, rset);\r
+       }\r
+\r
+       if (bufchain_size(&stdout_data) > 0) {\r
+           /* If we have data for stdout, try to write to stdout. */\r
+           FD_SET_MAX(STDOUT_FILENO, maxfd, wset);\r
+       }\r
+\r
+       if (bufchain_size(&stderr_data) > 0) {\r
+           /* If we have data for stderr, try to write to stderr. */\r
+           FD_SET_MAX(STDERR_FILENO, maxfd, wset);\r
+       }\r
+\r
+       /* Count the currently active fds. */\r
+       i = 0;\r
+       for (fd = first_fd(&fdstate, &rwx); fd >= 0;\r
+            fd = next_fd(&fdstate, &rwx)) i++;\r
+\r
+       /* Expand the fdlist buffer if necessary. */\r
+       if (i > fdsize) {\r
+           fdsize = i + 16;\r
+           fdlist = sresize(fdlist, fdsize, int);\r
+       }\r
+\r
+       /*\r
+        * Add all currently open fds to the select sets, and store\r
+        * them in fdlist as well.\r
+        */\r
+       fdcount = 0;\r
+       for (fd = first_fd(&fdstate, &rwx); fd >= 0;\r
+            fd = next_fd(&fdstate, &rwx)) {\r
+           fdlist[fdcount++] = fd;\r
+           if (rwx & 1)\r
+               FD_SET_MAX(fd, maxfd, rset);\r
+           if (rwx & 2)\r
+               FD_SET_MAX(fd, maxfd, wset);\r
+           if (rwx & 4)\r
+               FD_SET_MAX(fd, maxfd, xset);\r
+       }\r
+\r
+       do {\r
+           long next, ticks;\r
+           struct timeval tv, *ptv;\r
+\r
+           if (run_timers(now, &next)) {\r
+               ticks = next - GETTICKCOUNT();\r
+               if (ticks < 0) ticks = 0;   /* just in case */\r
+               tv.tv_sec = ticks / 1000;\r
+               tv.tv_usec = ticks % 1000 * 1000;\r
+               ptv = &tv;\r
+           } else {\r
+               ptv = NULL;\r
+           }\r
+           ret = select(maxfd, &rset, &wset, &xset, ptv);\r
+           if (ret == 0)\r
+               now = next;\r
+           else {\r
+               long newnow = GETTICKCOUNT();\r
+               /*\r
+                * Check to see whether the system clock has\r
+                * changed massively during the select.\r
+                */\r
+               if (newnow - now < 0 || newnow - now > next - now) {\r
+                   /*\r
+                    * If so, look at the elapsed time in the\r
+                    * select and use it to compute a new\r
+                    * tickcount_offset.\r
+                    */\r
+                   long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000;\r
+                   /* So we'd like GETTICKCOUNT to have returned othernow,\r
+                    * but instead it return newnow. Hence ... */\r
+                   tickcount_offset += othernow - newnow;\r
+                   now = othernow;\r
+               } else {\r
+                   now = newnow;\r
+               }\r
+           }\r
+       } while (ret < 0 && errno == EINTR);\r
+\r
+       if (ret < 0) {\r
+           perror("select");\r
+           exit(1);\r
+       }\r
+\r
+       for (i = 0; i < fdcount; i++) {\r
+           fd = fdlist[i];\r
+            /*\r
+             * We must process exceptional notifications before\r
+             * ordinary readability ones, or we may go straight\r
+             * past the urgent marker.\r
+             */\r
+           if (FD_ISSET(fd, &xset))\r
+               select_result(fd, 4);\r
+           if (FD_ISSET(fd, &rset))\r
+               select_result(fd, 1);\r
+           if (FD_ISSET(fd, &wset))\r
+               select_result(fd, 2);\r
+       }\r
+\r
+       if (FD_ISSET(signalpipe[0], &rset)) {\r
+           char c[1];\r
+           struct winsize size;\r
+           if (read(signalpipe[0], c, 1) <= 0)\r
+               /* ignore error */;\r
+           /* ignore its value; it'll be `x' */\r
+           if (ioctl(0, TIOCGWINSZ, (void *)&size) >= 0)\r
+               back->size(backhandle, size.ws_col, size.ws_row);\r
+       }\r
+\r
+       if (FD_ISSET(STDIN_FILENO, &rset)) {\r
+           char buf[4096];\r
+           int ret;\r
+\r
+           if (connopen && back->connected(backhandle)) {\r
+               ret = read(STDIN_FILENO, buf, sizeof(buf));\r
+               if (ret < 0) {\r
+                   perror("stdin: read");\r
+                   exit(1);\r
+               } else if (ret == 0) {\r
+                   back->special(backhandle, TS_EOF);\r
+                   sending = FALSE;   /* send nothing further after this */\r
+               } else {\r
+                   if (local_tty)\r
+                       from_tty(buf, ret);\r
+                   else\r
+                       back->send(backhandle, buf, ret);\r
+               }\r
+           }\r
+       }\r
+\r
+       if (FD_ISSET(STDOUT_FILENO, &wset)) {\r
+           back->unthrottle(backhandle, try_output(FALSE));\r
+       }\r
+\r
+       if (FD_ISSET(STDERR_FILENO, &wset)) {\r
+           back->unthrottle(backhandle, try_output(TRUE));\r
+       }\r
+\r
+       if ((!connopen || !back->connected(backhandle)) &&\r
+           bufchain_size(&stdout_data) == 0 &&\r
+           bufchain_size(&stderr_data) == 0)\r
+           break;                     /* we closed the connection */\r
+    }\r
+    exitcode = back->exitcode(backhandle);\r
+    if (exitcode < 0) {\r
+       fprintf(stderr, "Remote process exit code unavailable\n");\r
+       exitcode = 1;                  /* this is an error condition */\r
+    }\r
+    cleanup_exit(exitcode);\r
+    return exitcode;                  /* shouldn't happen, but placates gcc */\r
+}\r
diff --git a/putty/UNIX/UXPRINT.C b/putty/UNIX/UXPRINT.C
new file mode 100644 (file)
index 0000000..fb3d16c
--- /dev/null
@@ -0,0 +1,58 @@
+/*\r
+ * Printing interface for PuTTY.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+struct printer_job_tag {\r
+    FILE *fp;\r
+};\r
+\r
+printer_job *printer_start_job(char *printer)\r
+{\r
+    printer_job *ret = snew(printer_job);\r
+    /*\r
+     * On Unix, we treat the printer string as the name of a\r
+     * command to pipe to - typically lpr, of course.\r
+     */\r
+    ret->fp = popen(printer, "w");\r
+    if (!ret->fp) {\r
+       sfree(ret);\r
+       ret = NULL;\r
+    }\r
+    return ret;\r
+}\r
+\r
+void printer_job_data(printer_job *pj, void *data, int len)\r
+{\r
+    if (!pj)\r
+       return;\r
+\r
+    if (fwrite(data, 1, len, pj->fp) < len)\r
+       /* ignore */;\r
+}\r
+\r
+void printer_finish_job(printer_job *pj)\r
+{\r
+    if (!pj)\r
+       return;\r
+\r
+    pclose(pj->fp);\r
+    sfree(pj);\r
+}\r
+\r
+/*\r
+ * There's no sensible way to enumerate printers under Unix, since\r
+ * practically any valid Unix command is a valid printer :-) So\r
+ * these are useless stub functions, and uxcfg.c will disable the\r
+ * drop-down list in the printer configurer.\r
+ */\r
+printer_enum *printer_start_enum(int *nprinters_ptr) {\r
+    *nprinters_ptr = 0;\r
+    return NULL;\r
+}\r
+char *printer_get_name(printer_enum *pe, int i) { return NULL;\r
+}\r
+void printer_finish_enum(printer_enum *pe) { }\r
diff --git a/putty/UNIX/UXPROXY.C b/putty/UNIX/UXPROXY.C
new file mode 100644 (file)
index 0000000..147df6e
--- /dev/null
@@ -0,0 +1,311 @@
+/*\r
+ * uxproxy.c: Unix implementation of platform_new_connection(),\r
+ * supporting an OpenSSH-like proxy command.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <assert.h>\r
+#include <errno.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "tree234.h"\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+typedef struct Socket_localproxy_tag * Local_Proxy_Socket;\r
+\r
+struct Socket_localproxy_tag {\r
+    const struct socket_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+\r
+    int to_cmd, from_cmd;             /* fds */\r
+\r
+    char *error;\r
+\r
+    Plug plug;\r
+\r
+    bufchain pending_output_data;\r
+    bufchain pending_input_data;\r
+\r
+    void *privptr;\r
+};\r
+\r
+static int localproxy_select_result(int fd, int event);\r
+\r
+/*\r
+ * Trees to look up the pipe fds in.\r
+ */\r
+static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;\r
+static int localproxy_fromfd_cmp(void *av, void *bv)\r
+{\r
+    Local_Proxy_Socket a = (Local_Proxy_Socket)av;\r
+    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
+    if (a->from_cmd < b->from_cmd)\r
+       return -1;\r
+    if (a->from_cmd > b->from_cmd)\r
+       return +1;\r
+    return 0;\r
+}\r
+static int localproxy_fromfd_find(void *av, void *bv)\r
+{\r
+    int a = *(int *)av;\r
+    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
+    if (a < b->from_cmd)\r
+       return -1;\r
+    if (a > b->from_cmd)\r
+       return +1;\r
+    return 0;\r
+}\r
+static int localproxy_tofd_cmp(void *av, void *bv)\r
+{\r
+    Local_Proxy_Socket a = (Local_Proxy_Socket)av;\r
+    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
+    if (a->to_cmd < b->to_cmd)\r
+       return -1;\r
+    if (a->to_cmd > b->to_cmd)\r
+       return +1;\r
+    return 0;\r
+}\r
+static int localproxy_tofd_find(void *av, void *bv)\r
+{\r
+    int a = *(int *)av;\r
+    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
+    if (a < b->to_cmd)\r
+       return -1;\r
+    if (a > b->to_cmd)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+/* basic proxy socket functions */\r
+\r
+static Plug sk_localproxy_plug (Socket s, Plug p)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    Plug ret = ps->plug;\r
+    if (p)\r
+       ps->plug = p;\r
+    return ret;\r
+}\r
+\r
+static void sk_localproxy_close (Socket s)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+\r
+    del234(localproxy_by_fromfd, ps);\r
+    del234(localproxy_by_tofd, ps);\r
+\r
+    uxsel_del(ps->to_cmd);\r
+    uxsel_del(ps->from_cmd);\r
+    close(ps->to_cmd);\r
+    close(ps->from_cmd);\r
+\r
+    sfree(ps);\r
+}\r
+\r
+static int localproxy_try_send(Local_Proxy_Socket ps)\r
+{\r
+    int sent = 0;\r
+\r
+    while (bufchain_size(&ps->pending_output_data) > 0) {\r
+       void *data;\r
+       int len, ret;\r
+\r
+       bufchain_prefix(&ps->pending_output_data, &data, &len);\r
+       ret = write(ps->to_cmd, data, len);\r
+       if (ret < 0 && errno != EWOULDBLOCK) {\r
+           /* We're inside the Unix frontend here, so we know\r
+            * that the frontend handle is unnecessary. */\r
+           logevent(NULL, strerror(errno));\r
+           fatalbox("%s", strerror(errno));\r
+       } else if (ret <= 0) {\r
+           break;\r
+       } else {\r
+           bufchain_consume(&ps->pending_output_data, ret);\r
+           sent += ret;\r
+       }\r
+    }\r
+\r
+    if (bufchain_size(&ps->pending_output_data) == 0)\r
+       uxsel_del(ps->to_cmd);\r
+    else\r
+       uxsel_set(ps->to_cmd, 2, localproxy_select_result);\r
+\r
+    return sent;\r
+}\r
+\r
+static int sk_localproxy_write (Socket s, const char *data, int len)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+\r
+    bufchain_add(&ps->pending_output_data, data, len);\r
+\r
+    localproxy_try_send(ps);\r
+\r
+    return bufchain_size(&ps->pending_output_data);\r
+}\r
+\r
+static int sk_localproxy_write_oob (Socket s, const char *data, int len)\r
+{\r
+    /*\r
+     * oob data is treated as inband; nasty, but nothing really\r
+     * better we can do\r
+     */\r
+    return sk_localproxy_write(s, data, len);\r
+}\r
+\r
+static void sk_localproxy_flush (Socket s)\r
+{\r
+    /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */\r
+    /* do nothing */\r
+}\r
+\r
+static void sk_localproxy_set_private_ptr (Socket s, void *ptr)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    ps->privptr = ptr;\r
+}\r
+\r
+static void * sk_localproxy_get_private_ptr (Socket s)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    return ps->privptr;\r
+}\r
+\r
+static void sk_localproxy_set_frozen (Socket s, int is_frozen)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+\r
+    if (is_frozen)\r
+       uxsel_del(ps->from_cmd);\r
+    else\r
+       uxsel_set(ps->from_cmd, 1, localproxy_select_result);\r
+}\r
+\r
+static const char * sk_localproxy_socket_error (Socket s)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    return ps->error;\r
+}\r
+\r
+static int localproxy_select_result(int fd, int event)\r
+{\r
+    Local_Proxy_Socket s;\r
+    char buf[20480];\r
+    int ret;\r
+\r
+    if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&\r
+       !(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )\r
+       return 1;                      /* boggle */\r
+\r
+    if (event == 1) {\r
+       assert(fd == s->from_cmd);\r
+       ret = read(fd, buf, sizeof(buf));\r
+       if (ret < 0) {\r
+           return plug_closing(s->plug, strerror(errno), errno, 0);\r
+       } else if (ret == 0) {\r
+           return plug_closing(s->plug, NULL, 0, 0);\r
+       } else {\r
+           return plug_receive(s->plug, 0, buf, ret);\r
+       }\r
+    } else if (event == 2) {\r
+       assert(fd == s->to_cmd);\r
+       if (localproxy_try_send(s))\r
+           plug_sent(s->plug, bufchain_size(&s->pending_output_data));\r
+       return 1;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+Socket platform_new_connection(SockAddr addr, char *hostname,\r
+                              int port, int privport,\r
+                              int oobinline, int nodelay, int keepalive,\r
+                              Plug plug, const Config *cfg)\r
+{\r
+    char *cmd;\r
+\r
+    static const struct socket_function_table socket_fn_table = {\r
+       sk_localproxy_plug,\r
+       sk_localproxy_close,\r
+       sk_localproxy_write,\r
+       sk_localproxy_write_oob,\r
+       sk_localproxy_flush,\r
+       sk_localproxy_set_private_ptr,\r
+       sk_localproxy_get_private_ptr,\r
+       sk_localproxy_set_frozen,\r
+       sk_localproxy_socket_error\r
+    };\r
+\r
+    Local_Proxy_Socket ret;\r
+    int to_cmd_pipe[2], from_cmd_pipe[2], pid;\r
+\r
+    if (cfg->proxy_type != PROXY_CMD)\r
+       return NULL;\r
+\r
+    cmd = format_telnet_command(addr, port, cfg);\r
+\r
+    ret = snew(struct Socket_localproxy_tag);\r
+    ret->fn = &socket_fn_table;\r
+    ret->plug = plug;\r
+    ret->error = NULL;\r
+\r
+    bufchain_init(&ret->pending_input_data);\r
+    bufchain_init(&ret->pending_output_data);\r
+\r
+    /*\r
+     * Create the pipes to the proxy command, and spawn the proxy\r
+     * command process.\r
+     */\r
+    if (pipe(to_cmd_pipe) < 0 ||\r
+       pipe(from_cmd_pipe) < 0) {\r
+       ret->error = dupprintf("pipe: %s", strerror(errno));\r
+       return (Socket)ret;\r
+    }\r
+    cloexec(to_cmd_pipe[1]);\r
+    cloexec(from_cmd_pipe[0]);\r
+\r
+    pid = fork();\r
+\r
+    if (pid < 0) {\r
+       ret->error = dupprintf("fork: %s", strerror(errno));\r
+       return (Socket)ret;\r
+    } else if (pid == 0) {\r
+       close(0);\r
+       close(1);\r
+       dup2(to_cmd_pipe[0], 0);\r
+       dup2(from_cmd_pipe[1], 1);\r
+       close(to_cmd_pipe[0]);\r
+       close(from_cmd_pipe[1]);\r
+       fcntl(0, F_SETFD, 0);\r
+       fcntl(1, F_SETFD, 0);\r
+       execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);\r
+       _exit(255);\r
+    }\r
+\r
+    sfree(cmd);\r
+\r
+    close(to_cmd_pipe[0]);\r
+    close(from_cmd_pipe[1]);\r
+\r
+    ret->to_cmd = to_cmd_pipe[1];\r
+    ret->from_cmd = from_cmd_pipe[0];\r
+\r
+    if (!localproxy_by_fromfd)\r
+       localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);\r
+    if (!localproxy_by_tofd)\r
+       localproxy_by_tofd = newtree234(localproxy_tofd_cmp);\r
+\r
+    add234(localproxy_by_fromfd, ret);\r
+    add234(localproxy_by_tofd, ret);\r
+\r
+    uxsel_set(ret->from_cmd, 1, localproxy_select_result);\r
+\r
+    /* We are responsible for this and don't need it any more */\r
+    sk_addr_free(addr);\r
+\r
+    return (Socket) ret;\r
+}\r
diff --git a/putty/UNIX/UXPTERM.C b/putty/UNIX/UXPTERM.C
new file mode 100644 (file)
index 0000000..73670e8
--- /dev/null
@@ -0,0 +1,57 @@
+/*\r
+ * pterm main program.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+\r
+const char *const appname = "pterm";\r
+const int use_event_log = 0;          /* pterm doesn't need it */\r
+const int new_session = 0, saved_sessions = 0;   /* or these */\r
+const int use_pty_argv = TRUE;\r
+\r
+Backend *select_backend(Config *cfg)\r
+{\r
+    return &pty_backend;\r
+}\r
+\r
+int cfgbox(Config *cfg)\r
+{\r
+    /*\r
+     * This is a no-op in pterm, except that we'll ensure the\r
+     * protocol is set to -1 to inhibit the useless Connection\r
+     * panel in the config box.\r
+     */\r
+    cfg->protocol = -1;\r
+    return 1;\r
+}\r
+\r
+void cleanup_exit(int code)\r
+{\r
+    exit(code);\r
+}\r
+\r
+int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)\r
+{\r
+    return 0;                          /* pterm doesn't have any. */\r
+}\r
+\r
+char *make_default_wintitle(char *hostname)\r
+{\r
+    return dupstr("pterm");\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    extern int pt_main(int argc, char **argv);\r
+    extern void pty_pre_init(void);    /* declared in pty.c */\r
+\r
+    cmdline_tooltype = TOOLTYPE_NONNETWORK;\r
+    default_protocol = -1;\r
+\r
+    pty_pre_init();\r
+\r
+    return pt_main(argc, argv);\r
+}\r
diff --git a/putty/UNIX/UXPTY.C b/putty/UNIX/UXPTY.C
new file mode 100644 (file)
index 0000000..b1e240e
--- /dev/null
@@ -0,0 +1,1096 @@
+/*\r
+ * Pseudo-tty backend for pterm.\r
+ */\r
+\r
+#define _GNU_SOURCE\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+#include <signal.h>\r
+#include <assert.h>\r
+#include <fcntl.h>\r
+#include <termios.h>\r
+#include <grp.h>\r
+#include <utmp.h>\r
+#include <pwd.h>\r
+#include <time.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <sys/wait.h>\r
+#include <sys/ioctl.h>\r
+#include <errno.h>\r
+\r
+#include "putty.h"\r
+#include "tree234.h"\r
+\r
+#ifndef OMIT_UTMP\r
+#include <utmpx.h>\r
+#endif\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+/* updwtmpx() needs the name of the wtmp file.  Try to find it. */\r
+#ifndef WTMPX_FILE\r
+#ifdef _PATH_WTMPX\r
+#define WTMPX_FILE _PATH_WTMPX\r
+#else\r
+#define WTMPX_FILE "/var/log/wtmpx"\r
+#endif\r
+#endif\r
+\r
+#ifndef LASTLOG_FILE\r
+#ifdef _PATH_LASTLOG\r
+#define LASTLOG_FILE _PATH_LASTLOG\r
+#else\r
+#define LASTLOG_FILE "/var/log/lastlog"\r
+#endif\r
+#endif\r
+\r
+/*\r
+ * Set up a default for vaguely sane systems. The idea is that if\r
+ * OMIT_UTMP is not defined, then at least one of the symbols which\r
+ * enable particular forms of utmp processing should be, if only so\r
+ * that a link error can warn you that you should have defined\r
+ * OMIT_UTMP if you didn't want any. Currently HAVE_PUTUTLINE is\r
+ * the only such symbol.\r
+ */\r
+#ifndef OMIT_UTMP\r
+#if !defined HAVE_PUTUTLINE\r
+#define HAVE_PUTUTLINE\r
+#endif\r
+#endif\r
+\r
+typedef struct pty_tag *Pty;\r
+\r
+/*\r
+ * The pty_signal_pipe, along with the SIGCHLD handler, must be\r
+ * process-global rather than session-specific.\r
+ */\r
+static int pty_signal_pipe[2] = { -1, -1 };   /* obviously bogus initial val */\r
+\r
+struct pty_tag {\r
+    Config cfg;\r
+    int master_fd, slave_fd;\r
+    void *frontend;\r
+    char name[FILENAME_MAX];\r
+    pid_t child_pid;\r
+    int term_width, term_height;\r
+    int child_dead, finished;\r
+    int exit_code;\r
+    bufchain output_data;\r
+};\r
+\r
+/*\r
+ * We store our pty backends in a tree sorted by master fd, so that\r
+ * when we get an uxsel notification we know which backend instance\r
+ * is the owner of the pty that caused it.\r
+ */\r
+static int pty_compare_by_fd(void *av, void *bv)\r
+{\r
+    Pty a = (Pty)av;\r
+    Pty b = (Pty)bv;\r
+\r
+    if (a->master_fd < b->master_fd)\r
+       return -1;\r
+    else if (a->master_fd > b->master_fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int pty_find_by_fd(void *av, void *bv)\r
+{\r
+    int a = *(int *)av;\r
+    Pty b = (Pty)bv;\r
+\r
+    if (a < b->master_fd)\r
+       return -1;\r
+    else if (a > b->master_fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static tree234 *ptys_by_fd = NULL;\r
+\r
+/*\r
+ * We also have a tree sorted by child pid, so that when we wait()\r
+ * in response to the signal we know which backend instance is the\r
+ * owner of the process that caused the signal.\r
+ */\r
+static int pty_compare_by_pid(void *av, void *bv)\r
+{\r
+    Pty a = (Pty)av;\r
+    Pty b = (Pty)bv;\r
+\r
+    if (a->child_pid < b->child_pid)\r
+       return -1;\r
+    else if (a->child_pid > b->child_pid)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int pty_find_by_pid(void *av, void *bv)\r
+{\r
+    pid_t a = *(pid_t *)av;\r
+    Pty b = (Pty)bv;\r
+\r
+    if (a < b->child_pid)\r
+       return -1;\r
+    else if (a > b->child_pid)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static tree234 *ptys_by_pid = NULL;\r
+\r
+/*\r
+ * If we are using pty_pre_init(), it will need to have already\r
+ * allocated a pty structure, which we must then return from\r
+ * pty_init() rather than allocating a new one. Here we store that\r
+ * structure between allocation and use.\r
+ * \r
+ * Note that although most of this module is entirely capable of\r
+ * handling multiple ptys in a single process, pty_pre_init() is\r
+ * fundamentally _dependent_ on there being at most one pty per\r
+ * process, so the normal static-data constraints don't apply.\r
+ * \r
+ * Likewise, since utmp is only used via pty_pre_init, it too must\r
+ * be single-instance, so we can declare utmp-related variables\r
+ * here.\r
+ */\r
+static Pty single_pty = NULL;\r
+\r
+#ifndef OMIT_UTMP\r
+static pid_t pty_utmp_helper_pid;\r
+static int pty_utmp_helper_pipe;\r
+static int pty_stamped_utmp;\r
+static struct utmpx utmp_entry;\r
+#endif\r
+\r
+/*\r
+ * pty_argv is a grievous hack to allow a proper argv to be passed\r
+ * through from the Unix command line. Again, it doesn't really\r
+ * make sense outside a one-pty-per-process setup.\r
+ */\r
+char **pty_argv;\r
+\r
+static void pty_close(Pty pty);\r
+static void pty_try_write(Pty pty);\r
+\r
+#ifndef OMIT_UTMP\r
+static void setup_utmp(char *ttyname, char *location)\r
+{\r
+#ifdef HAVE_LASTLOG\r
+    struct lastlog lastlog_entry;\r
+    FILE *lastlog;\r
+#endif\r
+    struct passwd *pw;\r
+    struct timeval tv;\r
+\r
+    pw = getpwuid(getuid());\r
+    memset(&utmp_entry, 0, sizeof(utmp_entry));\r
+    utmp_entry.ut_type = USER_PROCESS;\r
+    utmp_entry.ut_pid = getpid();\r
+    strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line));\r
+    strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id));\r
+    strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user));\r
+    strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host));\r
+    /*\r
+     * Apparently there are some architectures where (struct\r
+     * utmpx).ut_tv is not essentially struct timeval (e.g. Linux\r
+     * amd64). Hence the temporary.\r
+     */\r
+    gettimeofday(&tv, NULL);\r
+    utmp_entry.ut_tv.tv_sec = tv.tv_sec;\r
+    utmp_entry.ut_tv.tv_usec = tv.tv_usec;\r
+\r
+    setutxent();\r
+    pututxline(&utmp_entry);\r
+    endutxent();\r
+\r
+    updwtmpx(WTMPX_FILE, &utmp_entry);\r
+\r
+#ifdef HAVE_LASTLOG\r
+    memset(&lastlog_entry, 0, sizeof(lastlog_entry));\r
+    strncpy(lastlog_entry.ll_line, ttyname+5, lenof(lastlog_entry.ll_line));\r
+    strncpy(lastlog_entry.ll_host, location, lenof(lastlog_entry.ll_host));\r
+    time(&lastlog_entry.ll_time);\r
+    if ((lastlog = fopen(LASTLOG_FILE, "r+")) != NULL) {\r
+       fseek(lastlog, sizeof(lastlog_entry) * getuid(), SEEK_SET);\r
+       fwrite(&lastlog_entry, 1, sizeof(lastlog_entry), lastlog);\r
+       fclose(lastlog);\r
+    }\r
+#endif\r
+\r
+    pty_stamped_utmp = 1;\r
+\r
+}\r
+\r
+static void cleanup_utmp(void)\r
+{\r
+    struct timeval tv;\r
+\r
+    if (!pty_stamped_utmp)\r
+       return;\r
+\r
+    utmp_entry.ut_type = DEAD_PROCESS;\r
+    memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user));\r
+    gettimeofday(&tv, NULL);\r
+    utmp_entry.ut_tv.tv_sec = tv.tv_sec;\r
+    utmp_entry.ut_tv.tv_usec = tv.tv_usec;\r
+\r
+    updwtmpx(WTMPX_FILE, &utmp_entry);\r
+\r
+    memset(utmp_entry.ut_line, 0, lenof(utmp_entry.ut_line));\r
+    utmp_entry.ut_tv.tv_sec = 0;\r
+    utmp_entry.ut_tv.tv_usec = 0;\r
+\r
+    setutxent();\r
+    pututxline(&utmp_entry);\r
+    endutxent();\r
+\r
+    pty_stamped_utmp = 0;             /* ensure we never double-cleanup */\r
+}\r
+#endif\r
+\r
+static void sigchld_handler(int signum)\r
+{\r
+    if (write(pty_signal_pipe[1], "x", 1) <= 0)\r
+       /* not much we can do about it */;\r
+}\r
+\r
+#ifndef OMIT_UTMP\r
+static void fatal_sig_handler(int signum)\r
+{\r
+    putty_signal(signum, SIG_DFL);\r
+    cleanup_utmp();\r
+    setuid(getuid());\r
+    raise(signum);\r
+}\r
+#endif\r
+\r
+static int pty_open_slave(Pty pty)\r
+{\r
+    if (pty->slave_fd < 0) {\r
+       pty->slave_fd = open(pty->name, O_RDWR);\r
+        cloexec(pty->slave_fd);\r
+    }\r
+\r
+    return pty->slave_fd;\r
+}\r
+\r
+static void pty_open_master(Pty pty)\r
+{\r
+#ifdef BSD_PTYS\r
+    const char chars1[] = "pqrstuvwxyz";\r
+    const char chars2[] = "0123456789abcdef";\r
+    const char *p1, *p2;\r
+    char master_name[20];\r
+    struct group *gp;\r
+\r
+    for (p1 = chars1; *p1; p1++)\r
+       for (p2 = chars2; *p2; p2++) {\r
+           sprintf(master_name, "/dev/pty%c%c", *p1, *p2);\r
+           pty->master_fd = open(master_name, O_RDWR);\r
+           if (pty->master_fd >= 0) {\r
+               if (geteuid() == 0 ||\r
+                   access(master_name, R_OK | W_OK) == 0) {\r
+                   /*\r
+                    * We must also check at this point that we are\r
+                    * able to open the slave side of the pty. We\r
+                    * wouldn't want to allocate the wrong master,\r
+                    * get all the way down to forking, and _then_\r
+                    * find we're unable to open the slave.\r
+                    */\r
+                   strcpy(pty->name, master_name);\r
+                   pty->name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */\r
+\r
+                    cloexec(pty->master_fd);\r
+\r
+                   if (pty_open_slave(pty) >= 0 &&\r
+                       access(pty->name, R_OK | W_OK) == 0)\r
+                       goto got_one;\r
+                   if (pty->slave_fd > 0)\r
+                       close(pty->slave_fd);\r
+                   pty->slave_fd = -1;\r
+               }\r
+               close(pty->master_fd);\r
+           }\r
+       }\r
+\r
+    /* If we get here, we couldn't get a tty at all. */\r
+    fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");\r
+    exit(1);\r
+\r
+    got_one:\r
+\r
+    /* We need to chown/chmod the /dev/ttyXX device. */\r
+    gp = getgrnam("tty");\r
+    chown(pty->name, getuid(), gp ? gp->gr_gid : -1);\r
+    chmod(pty->name, 0600);\r
+#else\r
+    pty->master_fd = open("/dev/ptmx", O_RDWR);\r
+\r
+    if (pty->master_fd < 0) {\r
+       perror("/dev/ptmx: open");\r
+       exit(1);\r
+    }\r
+\r
+    if (grantpt(pty->master_fd) < 0) {\r
+       perror("grantpt");\r
+       exit(1);\r
+    }\r
+    \r
+    if (unlockpt(pty->master_fd) < 0) {\r
+       perror("unlockpt");\r
+       exit(1);\r
+    }\r
+\r
+    cloexec(pty->master_fd);\r
+\r
+    pty->name[FILENAME_MAX-1] = '\0';\r
+    strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1);\r
+#endif\r
+\r
+    {\r
+        /*\r
+         * Set the pty master into non-blocking mode.\r
+         */\r
+        int fl;\r
+       fl = fcntl(pty->master_fd, F_GETFL);\r
+       if (fl != -1 && !(fl & O_NONBLOCK))\r
+           fcntl(pty->master_fd, F_SETFL, fl | O_NONBLOCK);\r
+    }\r
+\r
+    if (!ptys_by_fd)\r
+       ptys_by_fd = newtree234(pty_compare_by_fd);\r
+    add234(ptys_by_fd, pty);\r
+}\r
+\r
+/*\r
+ * Pre-initialisation. This is here to get around the fact that GTK\r
+ * doesn't like being run in setuid/setgid programs (probably\r
+ * sensibly). So before we initialise GTK - and therefore before we\r
+ * even process the command line - we check to see if we're running\r
+ * set[ug]id. If so, we open our pty master _now_, chown it as\r
+ * necessary, and drop privileges. We can always close it again\r
+ * later. If we're potentially going to be doing utmp as well, we\r
+ * also fork off a utmp helper process and communicate with it by\r
+ * means of a pipe; the utmp helper will keep privileges in order\r
+ * to clean up utmp when we exit (i.e. when its end of our pipe\r
+ * closes).\r
+ */\r
+void pty_pre_init(void)\r
+{\r
+    Pty pty;\r
+\r
+#ifndef OMIT_UTMP\r
+    pid_t pid;\r
+    int pipefd[2];\r
+#endif\r
+\r
+    pty = single_pty = snew(struct pty_tag);\r
+    bufchain_init(&pty->output_data);\r
+\r
+    /* set the child signal handler straight away; it needs to be set\r
+     * before we ever fork. */\r
+    putty_signal(SIGCHLD, sigchld_handler);\r
+    pty->master_fd = pty->slave_fd = -1;\r
+#ifndef OMIT_UTMP\r
+    pty_stamped_utmp = FALSE;\r
+#endif\r
+\r
+    if (geteuid() != getuid() || getegid() != getgid()) {\r
+       pty_open_master(pty);\r
+    }\r
+\r
+#ifndef OMIT_UTMP\r
+    /*\r
+     * Fork off the utmp helper.\r
+     */\r
+    if (pipe(pipefd) < 0) {\r
+       perror("pterm: pipe");\r
+       exit(1);\r
+    }\r
+    cloexec(pipefd[0]);\r
+    cloexec(pipefd[1]);\r
+    pid = fork();\r
+    if (pid < 0) {\r
+       perror("pterm: fork");\r
+       exit(1);\r
+    } else if (pid == 0) {\r
+       char display[128], buffer[128];\r
+       int dlen, ret;\r
+\r
+       close(pipefd[1]);\r
+       /*\r
+        * Now sit here until we receive a display name from the\r
+        * other end of the pipe, and then stamp utmp. Unstamp utmp\r
+        * again, and exit, when the pipe closes.\r
+        */\r
+\r
+       dlen = 0;\r
+       while (1) {\r
+           \r
+           ret = read(pipefd[0], buffer, lenof(buffer));\r
+           if (ret <= 0) {\r
+               cleanup_utmp();\r
+               _exit(0);\r
+           } else if (!pty_stamped_utmp) {\r
+               if (dlen < lenof(display))\r
+                   memcpy(display+dlen, buffer,\r
+                          min(ret, lenof(display)-dlen));\r
+               if (buffer[ret-1] == '\0') {\r
+                   /*\r
+                    * Now we have a display name. NUL-terminate\r
+                    * it, and stamp utmp.\r
+                    */\r
+                   display[lenof(display)-1] = '\0';\r
+                   /*\r
+                    * Trap as many fatal signals as we can in the\r
+                    * hope of having the best possible chance to\r
+                    * clean up utmp before termination. We are\r
+                    * unfortunately unprotected against SIGKILL,\r
+                    * but that's life.\r
+                    */\r
+                   putty_signal(SIGHUP, fatal_sig_handler);\r
+                   putty_signal(SIGINT, fatal_sig_handler);\r
+                   putty_signal(SIGQUIT, fatal_sig_handler);\r
+                   putty_signal(SIGILL, fatal_sig_handler);\r
+                   putty_signal(SIGABRT, fatal_sig_handler);\r
+                   putty_signal(SIGFPE, fatal_sig_handler);\r
+                   putty_signal(SIGPIPE, fatal_sig_handler);\r
+                   putty_signal(SIGALRM, fatal_sig_handler);\r
+                   putty_signal(SIGTERM, fatal_sig_handler);\r
+                   putty_signal(SIGSEGV, fatal_sig_handler);\r
+                   putty_signal(SIGUSR1, fatal_sig_handler);\r
+                   putty_signal(SIGUSR2, fatal_sig_handler);\r
+#ifdef SIGBUS\r
+                   putty_signal(SIGBUS, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGPOLL\r
+                   putty_signal(SIGPOLL, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGPROF\r
+                   putty_signal(SIGPROF, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGSYS\r
+                   putty_signal(SIGSYS, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGTRAP\r
+                   putty_signal(SIGTRAP, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGVTALRM\r
+                   putty_signal(SIGVTALRM, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGXCPU\r
+                   putty_signal(SIGXCPU, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGXFSZ\r
+                   putty_signal(SIGXFSZ, fatal_sig_handler);\r
+#endif\r
+#ifdef SIGIO\r
+                   putty_signal(SIGIO, fatal_sig_handler);\r
+#endif\r
+                   setup_utmp(pty->name, display);\r
+               }\r
+           }\r
+       }\r
+    } else {\r
+       close(pipefd[0]);\r
+       pty_utmp_helper_pid = pid;\r
+       pty_utmp_helper_pipe = pipefd[1];\r
+    }\r
+#endif\r
+\r
+    /* Drop privs. */\r
+    {\r
+#ifndef HAVE_NO_SETRESUID\r
+       int gid = getgid(), uid = getuid();\r
+       int setresgid(gid_t, gid_t, gid_t);\r
+       int setresuid(uid_t, uid_t, uid_t);\r
+       setresgid(gid, gid, gid);\r
+       setresuid(uid, uid, uid);\r
+#else\r
+       setgid(getgid());\r
+       setuid(getuid());\r
+#endif\r
+    }\r
+}\r
+\r
+int pty_real_select_result(Pty pty, int event, int status)\r
+{\r
+    char buf[4096];\r
+    int ret;\r
+    int finished = FALSE;\r
+\r
+    if (event < 0) {\r
+       /*\r
+        * We've been called because our child process did\r
+        * something. `status' tells us what.\r
+        */\r
+       if ((WIFEXITED(status) || WIFSIGNALED(status))) {\r
+           /*\r
+            * The primary child process died. We could keep\r
+            * the terminal open for remaining subprocesses to\r
+            * output to, but conventional wisdom seems to feel\r
+            * that that's the Wrong Thing for an xterm-alike,\r
+            * so we bail out now (though we don't necessarily\r
+            * _close_ the window, depending on the state of\r
+            * Close On Exit). This would be easy enough to\r
+            * change or make configurable if necessary.\r
+            */\r
+           pty->exit_code = status;\r
+           pty->child_dead = TRUE;\r
+           del234(ptys_by_pid, pty);\r
+           finished = TRUE;\r
+       }\r
+    } else {\r
+       if (event == 1) {\r
+\r
+           ret = read(pty->master_fd, buf, sizeof(buf));\r
+\r
+           /*\r
+            * Clean termination condition is that either ret == 0, or ret\r
+            * < 0 and errno == EIO. Not sure why the latter, but it seems\r
+            * to happen. Boo.\r
+            */\r
+           if (ret == 0 || (ret < 0 && errno == EIO)) {\r
+               /*\r
+                * We assume a clean exit if the pty has closed but the\r
+                * actual child process hasn't. The only way I can\r
+                * imagine this happening is if it detaches itself from\r
+                * the pty and goes daemonic - in which case the\r
+                * expected usage model would precisely _not_ be for\r
+                * the pterm window to hang around!\r
+                */\r
+               finished = TRUE;\r
+               if (!pty->child_dead)\r
+                   pty->exit_code = 0;\r
+           } else if (ret < 0) {\r
+               perror("read pty master");\r
+               exit(1);\r
+           } else if (ret > 0) {\r
+               from_backend(pty->frontend, 0, buf, ret);\r
+           }\r
+       } else if (event == 2) {\r
+            /*\r
+             * Attempt to send data down the pty.\r
+             */\r
+            pty_try_write(pty);\r
+        }\r
+    }\r
+\r
+    if (finished && !pty->finished) {\r
+       uxsel_del(pty->master_fd);\r
+       pty_close(pty);\r
+       pty->master_fd = -1;\r
+\r
+       pty->finished = TRUE;\r
+\r
+       /*\r
+        * This is a slight layering-violation sort of hack: only\r
+        * if we're not closing on exit (COE is set to Never, or to\r
+        * Only On Clean and it wasn't a clean exit) do we output a\r
+        * `terminated' message.\r
+        */\r
+       if (pty->cfg.close_on_exit == FORCE_OFF ||\r
+           (pty->cfg.close_on_exit == AUTO && pty->exit_code != 0)) {\r
+           char message[512];\r
+           if (WIFEXITED(pty->exit_code))\r
+               sprintf(message, "\r\n[pterm: process terminated with exit"\r
+                       " code %d]\r\n", WEXITSTATUS(pty->exit_code));\r
+           else if (WIFSIGNALED(pty->exit_code))\r
+#ifdef HAVE_NO_STRSIGNAL\r
+               sprintf(message, "\r\n[pterm: process terminated on signal"\r
+                       " %d]\r\n", WTERMSIG(pty->exit_code));\r
+#else\r
+               sprintf(message, "\r\n[pterm: process terminated on signal"\r
+                       " %d (%.400s)]\r\n", WTERMSIG(pty->exit_code),\r
+                       strsignal(WTERMSIG(pty->exit_code)));\r
+#endif\r
+           from_backend(pty->frontend, 0, message, strlen(message));\r
+       }\r
+\r
+       notify_remote_exit(pty->frontend);\r
+    }\r
+\r
+    return !finished;\r
+}\r
+\r
+int pty_select_result(int fd, int event)\r
+{\r
+    int ret = TRUE;\r
+    Pty pty;\r
+\r
+    if (fd == pty_signal_pipe[0]) {\r
+       pid_t pid;\r
+       int status;\r
+       char c[1];\r
+\r
+       if (read(pty_signal_pipe[0], c, 1) <= 0)\r
+           /* ignore error */;\r
+       /* ignore its value; it'll be `x' */\r
+\r
+       do {\r
+           pid = waitpid(-1, &status, WNOHANG);\r
+\r
+           pty = find234(ptys_by_pid, &pid, pty_find_by_pid);\r
+\r
+           if (pty)\r
+               ret = ret && pty_real_select_result(pty, -1, status);\r
+       } while (pid > 0);\r
+    } else {\r
+       pty = find234(ptys_by_fd, &fd, pty_find_by_fd);\r
+\r
+       if (pty)\r
+           ret = ret && pty_real_select_result(pty, event, 0);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+static void pty_uxsel_setup(Pty pty)\r
+{\r
+    int rwx;\r
+\r
+    rwx = 1;                           /* always want to read from pty */\r
+    if (bufchain_size(&pty->output_data))\r
+        rwx |= 2;                      /* might also want to write to it */\r
+    uxsel_set(pty->master_fd, rwx, pty_select_result);\r
+\r
+    /*\r
+     * In principle this only needs calling once for all pty\r
+     * backend instances, but it's simplest just to call it every\r
+     * time; uxsel won't mind.\r
+     */\r
+    uxsel_set(pty_signal_pipe[0], 1, pty_select_result);\r
+}\r
+\r
+/*\r
+ * Called to set up the pty.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,\r
+                           char *host, int port, char **realhost, int nodelay,\r
+                           int keepalive)\r
+{\r
+    int slavefd;\r
+    pid_t pid, pgrp;\r
+#ifndef NOT_X_WINDOWS                 /* for Mac OS X native compilation */\r
+    long windowid;\r
+#endif\r
+    Pty pty;\r
+\r
+    if (single_pty) {\r
+       pty = single_pty;\r
+    } else {\r
+       pty = snew(struct pty_tag);\r
+       pty->master_fd = pty->slave_fd = -1;\r
+#ifndef OMIT_UTMP\r
+       pty_stamped_utmp = FALSE;\r
+#endif\r
+    }\r
+\r
+    pty->frontend = frontend;\r
+    *backend_handle = NULL;           /* we can't sensibly use this, sadly */\r
+\r
+    pty->cfg = *cfg;                  /* structure copy */\r
+    pty->term_width = cfg->width;\r
+    pty->term_height = cfg->height;\r
+\r
+    if (pty->master_fd < 0)\r
+       pty_open_master(pty);\r
+\r
+    /*\r
+     * Set the backspace character to be whichever of ^H and ^? is\r
+     * specified by bksp_is_delete.\r
+     */\r
+    {\r
+       struct termios attrs;\r
+       tcgetattr(pty->master_fd, &attrs);\r
+       attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010';\r
+       tcsetattr(pty->master_fd, TCSANOW, &attrs);\r
+    }\r
+\r
+#ifndef OMIT_UTMP\r
+    /*\r
+     * Stamp utmp (that is, tell the utmp helper process to do so),\r
+     * or not.\r
+     */\r
+    if (!cfg->stamp_utmp) {\r
+       close(pty_utmp_helper_pipe);   /* just let the child process die */\r
+       pty_utmp_helper_pipe = -1;\r
+    } else {\r
+       char *location = get_x_display(pty->frontend);\r
+       int len = strlen(location)+1, pos = 0;   /* +1 to include NUL */\r
+       while (pos < len) {\r
+           int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);\r
+           if (ret < 0) {\r
+               perror("pterm: writing to utmp helper process");\r
+               close(pty_utmp_helper_pipe);   /* arrgh, just give up */\r
+               pty_utmp_helper_pipe = -1;\r
+               break;\r
+           }\r
+           pos += ret;\r
+       }\r
+    }\r
+#endif\r
+\r
+#ifndef NOT_X_WINDOWS                 /* for Mac OS X native compilation */\r
+    windowid = get_windowid(pty->frontend);\r
+#endif\r
+\r
+    /*\r
+     * Fork and execute the command.\r
+     */\r
+    pid = fork();\r
+    if (pid < 0) {\r
+       perror("fork");\r
+       exit(1);\r
+    }\r
+\r
+    if (pid == 0) {\r
+       /*\r
+        * We are the child.\r
+        */\r
+\r
+       slavefd = pty_open_slave(pty);\r
+       if (slavefd < 0) {\r
+           perror("slave pty: open");\r
+           _exit(1);\r
+       }\r
+\r
+       close(pty->master_fd);\r
+       fcntl(slavefd, F_SETFD, 0);    /* don't close on exec */\r
+       dup2(slavefd, 0);\r
+       dup2(slavefd, 1);\r
+       dup2(slavefd, 2);\r
+       close(slavefd);\r
+       setsid();\r
+#ifdef TIOCSCTTY\r
+       ioctl(0, TIOCSCTTY, 1);\r
+#endif\r
+       pgrp = getpid();\r
+       tcsetpgrp(0, pgrp);\r
+       setpgid(pgrp, pgrp);\r
+       close(open(pty->name, O_WRONLY, 0));\r
+       setpgid(pgrp, pgrp);\r
+       {\r
+           char *term_env_var = dupprintf("TERM=%s", cfg->termtype);\r
+           putenv(term_env_var);\r
+           /* We mustn't free term_env_var, as putenv links it into the\r
+            * environment in place.\r
+            */\r
+       }\r
+#ifndef NOT_X_WINDOWS                 /* for Mac OS X native compilation */\r
+       {\r
+           char *windowid_env_var = dupprintf("WINDOWID=%ld", windowid);\r
+           putenv(windowid_env_var);\r
+           /* We mustn't free windowid_env_var, as putenv links it into the\r
+            * environment in place.\r
+            */\r
+       }\r
+#endif\r
+       {\r
+           char *e = cfg->environmt;\r
+           char *var, *varend, *val, *varval;\r
+           while (*e) {\r
+               var = e;\r
+               while (*e && *e != '\t') e++;\r
+               varend = e;\r
+               if (*e == '\t') e++;\r
+               val = e;\r
+               while (*e) e++;\r
+               e++;\r
+\r
+               varval = dupprintf("%.*s=%s", varend-var, var, val);\r
+               putenv(varval);\r
+               /*\r
+                * We must not free varval, since putenv links it\r
+                * into the environment _in place_. Weird, but\r
+                * there we go. Memory usage will be rationalised\r
+                * as soon as we exec anyway.\r
+                */\r
+           }\r
+       }\r
+\r
+       /*\r
+        * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by\r
+        * our parent, particularly by things like sh -c 'pterm &' and\r
+        * some window or session managers. SIGCHLD, meanwhile, was\r
+        * blocked during pt_main() startup. Reverse all this for our\r
+        * child process.\r
+        */\r
+       putty_signal(SIGINT, SIG_DFL);\r
+       putty_signal(SIGQUIT, SIG_DFL);\r
+       putty_signal(SIGPIPE, SIG_DFL);\r
+       block_signal(SIGCHLD, 0);\r
+       if (pty_argv)\r
+           execvp(pty_argv[0], pty_argv);\r
+       else {\r
+           char *shell = getenv("SHELL");\r
+           char *shellname;\r
+           if (cfg->login_shell) {\r
+               char *p = strrchr(shell, '/');\r
+               shellname = snewn(2+strlen(shell), char);\r
+               p = p ? p+1 : shell;\r
+               sprintf(shellname, "-%s", p);\r
+           } else\r
+               shellname = shell;\r
+           execl(getenv("SHELL"), shellname, (void *)NULL);\r
+       }\r
+\r
+       /*\r
+        * If we're here, exec has gone badly foom.\r
+        */\r
+       perror("exec");\r
+       _exit(127);\r
+    } else {\r
+       pty->child_pid = pid;\r
+       pty->child_dead = FALSE;\r
+       pty->finished = FALSE;\r
+       if (pty->slave_fd > 0)\r
+           close(pty->slave_fd);\r
+       if (!ptys_by_pid)\r
+           ptys_by_pid = newtree234(pty_compare_by_pid);\r
+       add234(ptys_by_pid, pty);\r
+    }\r
+\r
+    if (pty_signal_pipe[0] < 0) {\r
+       if (pipe(pty_signal_pipe) < 0) {\r
+           perror("pipe");\r
+           exit(1);\r
+       }\r
+       cloexec(pty_signal_pipe[0]);\r
+       cloexec(pty_signal_pipe[1]);\r
+    }\r
+    pty_uxsel_setup(pty);\r
+\r
+    *backend_handle = pty;\r
+\r
+    *realhost = dupprintf("\0");\r
+\r
+    return NULL;\r
+}\r
+\r
+static void pty_reconfig(void *handle, Config *cfg)\r
+{\r
+    Pty pty = (Pty)handle;\r
+    /*\r
+     * We don't have much need to reconfigure this backend, but\r
+     * unfortunately we do need to pick up the setting of Close On\r
+     * Exit so we know whether to give a `terminated' message.\r
+     */\r
+    pty->cfg = *cfg;                  /* structure copy */\r
+}\r
+\r
+/*\r
+ * Stub routine (never called in pterm).\r
+ */\r
+static void pty_free(void *handle)\r
+{\r
+    Pty pty = (Pty)handle;\r
+\r
+    /* Either of these may fail `not found'. That's fine with us. */\r
+    del234(ptys_by_pid, pty);\r
+    del234(ptys_by_fd, pty);\r
+\r
+    sfree(pty);\r
+}\r
+\r
+static void pty_try_write(Pty pty)\r
+{\r
+    void *data;\r
+    int len, ret;\r
+\r
+    assert(pty->master_fd >= 0);\r
+\r
+    while (bufchain_size(&pty->output_data) > 0) {\r
+        bufchain_prefix(&pty->output_data, &data, &len);\r
+       ret = write(pty->master_fd, data, len);\r
+\r
+        if (ret < 0 && (errno == EWOULDBLOCK)) {\r
+            /*\r
+             * We've sent all we can for the moment.\r
+             */\r
+            break;\r
+        }\r
+       if (ret < 0) {\r
+           perror("write pty master");\r
+           exit(1);\r
+       }\r
+       bufchain_consume(&pty->output_data, ret);\r
+    }\r
+\r
+    pty_uxsel_setup(pty);\r
+}\r
+\r
+/*\r
+ * Called to send data down the pty.\r
+ */\r
+static int pty_send(void *handle, char *buf, int len)\r
+{\r
+    Pty pty = (Pty)handle;\r
+\r
+    if (pty->master_fd < 0)\r
+       return 0;                      /* ignore all writes if fd closed */\r
+\r
+    bufchain_add(&pty->output_data, buf, len);\r
+    pty_try_write(pty);\r
+\r
+    return bufchain_size(&pty->output_data);\r
+}\r
+\r
+static void pty_close(Pty pty)\r
+{\r
+    if (pty->master_fd >= 0) {\r
+       close(pty->master_fd);\r
+       pty->master_fd = -1;\r
+    }\r
+#ifndef OMIT_UTMP\r
+    if (pty_utmp_helper_pipe >= 0) {\r
+       close(pty_utmp_helper_pipe);   /* this causes utmp to be cleaned up */\r
+       pty_utmp_helper_pipe = -1;\r
+    }\r
+#endif\r
+}\r
+\r
+/*\r
+ * Called to query the current socket sendability status.\r
+ */\r
+static int pty_sendbuffer(void *handle)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void pty_size(void *handle, int width, int height)\r
+{\r
+    Pty pty = (Pty)handle;\r
+    struct winsize size;\r
+\r
+    pty->term_width = width;\r
+    pty->term_height = height;\r
+\r
+    size.ws_row = (unsigned short)pty->term_height;\r
+    size.ws_col = (unsigned short)pty->term_width;\r
+    size.ws_xpixel = (unsigned short) pty->term_width *\r
+       font_dimension(pty->frontend, 0);\r
+    size.ws_ypixel = (unsigned short) pty->term_height *\r
+       font_dimension(pty->frontend, 1);\r
+    ioctl(pty->master_fd, TIOCSWINSZ, (void *)&size);\r
+    return;\r
+}\r
+\r
+/*\r
+ * Send special codes.\r
+ */\r
+static void pty_special(void *handle, Telnet_Special code)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *pty_get_specials(void *handle)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    /*\r
+     * Hmm. When I get round to having this actually usable, it\r
+     * might be quite nice to have the ability to deliver a few\r
+     * well chosen signals to the child process - SIGINT, SIGTERM,\r
+     * SIGKILL at least.\r
+     */\r
+    return NULL;\r
+}\r
+\r
+static int pty_connected(void *handle)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    return TRUE;\r
+}\r
+\r
+static int pty_sendok(void *handle)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    return 1;\r
+}\r
+\r
+static void pty_unthrottle(void *handle, int backlog)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    /* do nothing */\r
+}\r
+\r
+static int pty_ldisc(void *handle, int option)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    return 0;                         /* neither editing nor echoing */\r
+}\r
+\r
+static void pty_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    /* This is a stub. */\r
+}\r
+\r
+static void pty_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    /* This is a stub. */\r
+}\r
+\r
+static int pty_exitcode(void *handle)\r
+{\r
+    Pty pty = (Pty)handle;\r
+    if (!pty->finished)\r
+       return -1;                     /* not dead yet */\r
+    else\r
+       return pty->exit_code;\r
+}\r
+\r
+static int pty_cfg_info(void *handle)\r
+{\r
+    /* Pty pty = (Pty)handle; */\r
+    return 0;\r
+}\r
+\r
+Backend pty_backend = {\r
+    pty_init,\r
+    pty_free,\r
+    pty_reconfig,\r
+    pty_send,\r
+    pty_sendbuffer,\r
+    pty_size,\r
+    pty_special,\r
+    pty_get_specials,\r
+    pty_connected,\r
+    pty_exitcode,\r
+    pty_sendok,\r
+    pty_ldisc,\r
+    pty_provide_ldisc,\r
+    pty_provide_logctx,\r
+    pty_unthrottle,\r
+    pty_cfg_info,\r
+    "pty",\r
+    -1,\r
+    0\r
+};\r
diff --git a/putty/UNIX/UXPUTTY.C b/putty/UNIX/UXPUTTY.C
new file mode 100644 (file)
index 0000000..83eae33
--- /dev/null
@@ -0,0 +1,141 @@
+/*\r
+ * Unix PuTTY main program.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <unistd.h>\r
+#include <gdk/gdk.h>\r
+\r
+#include "putty.h"\r
+#include "storage.h"\r
+\r
+/*\r
+ * Stubs to avoid uxpty.c needing to be linked in.\r
+ */\r
+const int use_pty_argv = FALSE;\r
+char **pty_argv;                      /* never used */\r
+\r
+/*\r
+ * Clean up and exit.\r
+ */\r
+void cleanup_exit(int code)\r
+{\r
+    /*\r
+     * Clean up.\r
+     */\r
+    sk_cleanup();\r
+    random_save_seed();\r
+    exit(code);\r
+}\r
+\r
+Backend *select_backend(Config *cfg)\r
+{\r
+    Backend *back = backend_from_proto(cfg->protocol);\r
+    assert(back != NULL);\r
+    return back;\r
+}\r
+\r
+int cfgbox(Config *cfg)\r
+{\r
+    char *title = dupcat(appname, " Configuration", NULL);\r
+    int ret = do_config_box(title, cfg, 0, 0);\r
+    sfree(title);\r
+    return ret;\r
+}\r
+\r
+static int got_host = 0;\r
+\r
+const int use_event_log = 1, new_session = 1, saved_sessions = 1;\r
+\r
+int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)\r
+{\r
+    char *p, *q = arg;\r
+\r
+    if (got_host) {\r
+        /*\r
+         * If we already have a host name, treat this argument as a\r
+         * port number. NB we have to treat this as a saved -P\r
+         * argument, so that it will be deferred until it's a good\r
+         * moment to run it.\r
+         */\r
+        int ret = cmdline_process_param("-P", arg, 1, cfg);\r
+        assert(ret == 2);\r
+    } else if (!strncmp(q, "telnet:", 7)) {\r
+        /*\r
+         * If the hostname starts with "telnet:",\r
+         * set the protocol to Telnet and process\r
+         * the string as a Telnet URL.\r
+         */\r
+        char c;\r
+\r
+        q += 7;\r
+        if (q[0] == '/' && q[1] == '/')\r
+            q += 2;\r
+        cfg->protocol = PROT_TELNET;\r
+        p = q;\r
+        while (*p && *p != ':' && *p != '/')\r
+            p++;\r
+        c = *p;\r
+        if (*p)\r
+            *p++ = '\0';\r
+        if (c == ':')\r
+            cfg->port = atoi(p);\r
+        else\r
+            cfg->port = -1;\r
+        strncpy(cfg->host, q, sizeof(cfg->host) - 1);\r
+        cfg->host[sizeof(cfg->host) - 1] = '\0';\r
+        got_host = 1;\r
+    } else {\r
+        /*\r
+         * Otherwise, treat this argument as a host name.\r
+         */\r
+        p = arg;\r
+        while (*p && !isspace((unsigned char)*p))\r
+            p++;\r
+        if (*p)\r
+            *p++ = '\0';\r
+        strncpy(cfg->host, q, sizeof(cfg->host) - 1);\r
+        cfg->host[sizeof(cfg->host) - 1] = '\0';\r
+        got_host = 1;\r
+    }\r
+    if (got_host)\r
+       *allow_launch = TRUE;\r
+    return 1;\r
+}\r
+\r
+char *make_default_wintitle(char *hostname)\r
+{\r
+    return dupcat(hostname, " - ", appname, NULL);\r
+}\r
+\r
+/*\r
+ * X11-forwarding-related things suitable for Gtk app.\r
+ */\r
+\r
+char *platform_get_x_display(void) {\r
+    const char *display;\r
+    /* Try to take account of --display and what have you. */\r
+    if (!(display = gdk_get_display()))\r
+       /* fall back to traditional method */\r
+       display = getenv("DISPLAY");\r
+    return dupstr(display);\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    extern int pt_main(int argc, char **argv);\r
+    sk_init();\r
+    flags = FLAG_VERBOSE | FLAG_INTERACTIVE;\r
+    default_protocol = be_default_protocol;\r
+    /* Find the appropriate default port. */\r
+    {\r
+       Backend *b = backend_from_proto(default_protocol);\r
+       default_port = 0; /* illegal */\r
+       if (b)\r
+           default_port = b->default_port;\r
+    }\r
+    return pt_main(argc, argv);\r
+}\r
diff --git a/putty/UNIX/UXSEL.C b/putty/UNIX/UXSEL.C
new file mode 100644 (file)
index 0000000..41d5b4d
--- /dev/null
@@ -0,0 +1,123 @@
+/*\r
+ * uxsel.c\r
+ * \r
+ * This module is a sort of all-purpose interchange for file\r
+ * descriptors. At one end it talks to uxnet.c and pty.c and\r
+ * anything else which might have one or more fds that need\r
+ * select()-type things doing to them during an extended program\r
+ * run; at the other end it talks to pterm.c or uxplink.c or\r
+ * anything else which might have its own means of actually doing\r
+ * those select()-type things.\r
+ */\r
+\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+#include "tree234.h"\r
+\r
+struct fd {\r
+    int fd;\r
+    int rwx;                          /* 4=except 2=write 1=read */\r
+    uxsel_callback_fn callback;\r
+    int id;                           /* for uxsel_input_remove */\r
+};\r
+\r
+static tree234 *fds;\r
+\r
+static int uxsel_fd_cmp(void *av, void *bv)\r
+{\r
+    struct fd *a = (struct fd *)av;\r
+    struct fd *b = (struct fd *)bv;\r
+    if (a->fd < b->fd)\r
+       return -1;\r
+    if (a->fd > b->fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+static int uxsel_fd_findcmp(void *av, void *bv)\r
+{\r
+    int *a = (int *)av;\r
+    struct fd *b = (struct fd *)bv;\r
+    if (*a < b->fd)\r
+       return -1;\r
+    if (*a > b->fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+void uxsel_init(void)\r
+{\r
+    fds = newtree234(uxsel_fd_cmp);\r
+}\r
+\r
+/*\r
+ * Here is the interface to fd-supplying modules. They supply an\r
+ * fd, a set of read/write/execute states, and a callback function\r
+ * for when the fd satisfies one of those states. Repeated calls to\r
+ * uxsel_set on the same fd are perfectly legal and serve to change\r
+ * the rwx state (typically you only want to select an fd for\r
+ * writing when you actually have pending data you want to write to\r
+ * it!).\r
+ */\r
+\r
+void uxsel_set(int fd, int rwx, uxsel_callback_fn callback)\r
+{\r
+    struct fd *newfd;\r
+\r
+    uxsel_del(fd);\r
+\r
+    if (rwx) {\r
+       newfd = snew(struct fd);\r
+       newfd->fd = fd;\r
+       newfd->rwx = rwx;\r
+       newfd->callback = callback;\r
+       newfd->id = uxsel_input_add(fd, rwx);\r
+       add234(fds, newfd);\r
+    }\r
+}\r
+\r
+void uxsel_del(int fd)\r
+{\r
+    struct fd *oldfd = find234(fds, &fd, uxsel_fd_findcmp);\r
+    if (oldfd) {\r
+       uxsel_input_remove(oldfd->id);\r
+       del234(fds, oldfd);\r
+       sfree(oldfd);\r
+    }\r
+}\r
+\r
+/*\r
+ * And here is the interface to select-functionality-supplying\r
+ * modules. \r
+ */\r
+\r
+int next_fd(int *state, int *rwx)\r
+{\r
+    struct fd *fd;\r
+    fd = index234(fds, (*state)++);\r
+    if (fd) {\r
+       *rwx = fd->rwx;\r
+       return fd->fd;\r
+    } else\r
+       return -1;\r
+}\r
+\r
+int first_fd(int *state, int *rwx)\r
+{\r
+    *state = 0;\r
+    return next_fd(state, rwx);\r
+}\r
+\r
+int select_result(int fd, int event)\r
+{\r
+    struct fd *fdstruct = find234(fds, &fd, uxsel_fd_findcmp);\r
+    /*\r
+     * Apparently this can sometimes be NULL. Can't see how, but I\r
+     * assume it means I need to ignore the event since it's on an\r
+     * fd I've stopped being interested in. Sigh.\r
+     */\r
+    if (fdstruct)\r
+        return fdstruct->callback(fd, event);\r
+    else\r
+        return 1;\r
+}\r
diff --git a/putty/UNIX/UXSER.C b/putty/UNIX/UXSER.C
new file mode 100644 (file)
index 0000000..de47877
--- /dev/null
@@ -0,0 +1,590 @@
+/*\r
+ * Serial back end (Unix-specific).\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <limits.h>\r
+\r
+#include <errno.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#include <termios.h>\r
+\r
+#include "putty.h"\r
+#include "tree234.h"\r
+\r
+#define SERIAL_MAX_BACKLOG 4096\r
+\r
+typedef struct serial_backend_data {\r
+    void *frontend;\r
+    int fd;\r
+    int finished;\r
+    int inbufsize;\r
+    bufchain output_data;\r
+} *Serial;\r
+\r
+/*\r
+ * We store our serial backends in a tree sorted by fd, so that\r
+ * when we get an uxsel notification we know which backend instance\r
+ * is the owner of the serial port that caused it.\r
+ */\r
+static int serial_compare_by_fd(void *av, void *bv)\r
+{\r
+    Serial a = (Serial)av;\r
+    Serial b = (Serial)bv;\r
+\r
+    if (a->fd < b->fd)\r
+       return -1;\r
+    else if (a->fd > b->fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int serial_find_by_fd(void *av, void *bv)\r
+{\r
+    int a = *(int *)av;\r
+    Serial b = (Serial)bv;\r
+\r
+    if (a < b->fd)\r
+       return -1;\r
+    else if (a > b->fd)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static tree234 *serial_by_fd = NULL;\r
+\r
+static int serial_select_result(int fd, int event);\r
+static void serial_uxsel_setup(Serial serial);\r
+static void serial_try_write(Serial serial);\r
+\r
+static const char *serial_configure(Serial serial, Config *cfg)\r
+{\r
+    struct termios options;\r
+    int bflag, bval;\r
+    const char *str;\r
+    char *msg;\r
+\r
+    if (serial->fd < 0)\r
+       return "Unable to reconfigure already-closed serial connection";\r
+\r
+    tcgetattr(serial->fd, &options);\r
+\r
+    /*\r
+     * Find the appropriate baud rate flag.\r
+     */\r
+#define SETBAUD(x) (bflag = B ## x, bval = x)\r
+#define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)\r
+    SETBAUD(50);\r
+#ifdef B75\r
+    CHECKBAUD(75);\r
+#endif\r
+#ifdef B110\r
+    CHECKBAUD(110);\r
+#endif\r
+#ifdef B134\r
+    CHECKBAUD(134);\r
+#endif\r
+#ifdef B150\r
+    CHECKBAUD(150);\r
+#endif\r
+#ifdef B200\r
+    CHECKBAUD(200);\r
+#endif\r
+#ifdef B300\r
+    CHECKBAUD(300);\r
+#endif\r
+#ifdef B600\r
+    CHECKBAUD(600);\r
+#endif\r
+#ifdef B1200\r
+    CHECKBAUD(1200);\r
+#endif\r
+#ifdef B1800\r
+    CHECKBAUD(1800);\r
+#endif\r
+#ifdef B2400\r
+    CHECKBAUD(2400);\r
+#endif\r
+#ifdef B4800\r
+    CHECKBAUD(4800);\r
+#endif\r
+#ifdef B9600\r
+    CHECKBAUD(9600);\r
+#endif\r
+#ifdef B19200\r
+    CHECKBAUD(19200);\r
+#endif\r
+#ifdef B38400\r
+    CHECKBAUD(38400);\r
+#endif\r
+#ifdef B57600\r
+    CHECKBAUD(57600);\r
+#endif\r
+#ifdef B76800\r
+    CHECKBAUD(76800);\r
+#endif\r
+#ifdef B115200\r
+    CHECKBAUD(115200);\r
+#endif\r
+#ifdef B153600\r
+    CHECKBAUD(153600);\r
+#endif\r
+#ifdef B230400\r
+    CHECKBAUD(230400);\r
+#endif\r
+#ifdef B307200\r
+    CHECKBAUD(307200);\r
+#endif\r
+#ifdef B460800\r
+    CHECKBAUD(460800);\r
+#endif\r
+#ifdef B500000\r
+    CHECKBAUD(500000);\r
+#endif\r
+#ifdef B576000\r
+    CHECKBAUD(576000);\r
+#endif\r
+#ifdef B921600\r
+    CHECKBAUD(921600);\r
+#endif\r
+#ifdef B1000000\r
+    CHECKBAUD(1000000);\r
+#endif\r
+#ifdef B1152000\r
+    CHECKBAUD(1152000);\r
+#endif\r
+#ifdef B1500000\r
+    CHECKBAUD(1500000);\r
+#endif\r
+#ifdef B2000000\r
+    CHECKBAUD(2000000);\r
+#endif\r
+#ifdef B2500000\r
+    CHECKBAUD(2500000);\r
+#endif\r
+#ifdef B3000000\r
+    CHECKBAUD(3000000);\r
+#endif\r
+#ifdef B3500000\r
+    CHECKBAUD(3500000);\r
+#endif\r
+#ifdef B4000000\r
+    CHECKBAUD(4000000);\r
+#endif\r
+#undef CHECKBAUD\r
+#undef SETBAUD\r
+    cfsetispeed(&options, bflag);\r
+    cfsetospeed(&options, bflag);\r
+    msg = dupprintf("Configuring baud rate %d", bval);\r
+    logevent(serial->frontend, msg);\r
+    sfree(msg);\r
+\r
+    options.c_cflag &= ~CSIZE;\r
+    switch (cfg->serdatabits) {\r
+      case 5: options.c_cflag |= CS5; break;\r
+      case 6: options.c_cflag |= CS6; break;\r
+      case 7: options.c_cflag |= CS7; break;\r
+      case 8: options.c_cflag |= CS8; break;\r
+      default: return "Invalid number of data bits (need 5, 6, 7 or 8)";\r
+    }\r
+    msg = dupprintf("Configuring %d data bits", cfg->serdatabits);\r
+    logevent(serial->frontend, msg);\r
+    sfree(msg);\r
+\r
+    if (cfg->serstopbits >= 4) {\r
+       options.c_cflag |= CSTOPB;\r
+    } else {\r
+       options.c_cflag &= ~CSTOPB;\r
+    }\r
+    msg = dupprintf("Configuring %d stop bits",\r
+                   (options.c_cflag & CSTOPB ? 2 : 1));\r
+    logevent(serial->frontend, msg);\r
+    sfree(msg);\r
+\r
+    options.c_iflag &= ~(IXON|IXOFF);\r
+#ifdef CRTSCTS\r
+    options.c_cflag &= ~CRTSCTS;\r
+#endif\r
+#ifdef CNEW_RTSCTS\r
+    options.c_cflag &= ~CNEW_RTSCTS;\r
+#endif\r
+    if (cfg->serflow == SER_FLOW_XONXOFF) {\r
+       options.c_iflag |= IXON | IXOFF;\r
+       str = "XON/XOFF";\r
+    } else if (cfg->serflow == SER_FLOW_RTSCTS) {\r
+#ifdef CRTSCTS\r
+       options.c_cflag |= CRTSCTS;\r
+#endif\r
+#ifdef CNEW_RTSCTS\r
+       options.c_cflag |= CNEW_RTSCTS;\r
+#endif\r
+       str = "RTS/CTS";\r
+    } else\r
+       str = "no";\r
+    msg = dupprintf("Configuring %s flow control", str);\r
+    logevent(serial->frontend, msg);\r
+    sfree(msg);\r
+\r
+    /* Parity */\r
+    if (cfg->serparity == SER_PAR_ODD) {\r
+       options.c_cflag |= PARENB;\r
+       options.c_cflag |= PARODD;\r
+       str = "odd";\r
+    } else if (cfg->serparity == SER_PAR_EVEN) {\r
+       options.c_cflag |= PARENB;\r
+       options.c_cflag &= ~PARODD;\r
+       str = "even";\r
+    } else {\r
+       options.c_cflag &= ~PARENB;\r
+       str = "no";\r
+    }\r
+    msg = dupprintf("Configuring %s parity", str);\r
+    logevent(serial->frontend, msg);\r
+    sfree(msg);\r
+\r
+    options.c_cflag |= CLOCAL | CREAD;\r
+    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);\r
+    options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL\r
+#ifdef IUCLC\r
+                        | IUCLC\r
+#endif\r
+                        );\r
+    options.c_oflag &= ~(OPOST\r
+#ifdef ONLCR\r
+                        | ONLCR\r
+#endif\r
+#ifdef OCRNL\r
+                        | OCRNL\r
+#endif\r
+#ifdef ONOCR\r
+                        | ONOCR\r
+#endif\r
+#ifdef ONLRET\r
+                        | ONLRET\r
+#endif\r
+                        );\r
+    options.c_cc[VMIN] = 1;\r
+    options.c_cc[VTIME] = 0;\r
+\r
+    if (tcsetattr(serial->fd, TCSANOW, &options) < 0)\r
+       return "Unable to configure serial port";\r
+\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Called to set up the serial connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *serial_init(void *frontend_handle, void **backend_handle,\r
+                              Config *cfg,\r
+                              char *host, int port, char **realhost, int nodelay,\r
+                              int keepalive)\r
+{\r
+    Serial serial;\r
+    const char *err;\r
+\r
+    serial = snew(struct serial_backend_data);\r
+    *backend_handle = serial;\r
+\r
+    serial->frontend = frontend_handle;\r
+    serial->finished = FALSE;\r
+    serial->inbufsize = 0;\r
+    bufchain_init(&serial->output_data);\r
+\r
+    {\r
+       char *msg = dupprintf("Opening serial device %s", cfg->serline);\r
+       logevent(serial->frontend, msg);\r
+    }\r
+\r
+    serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);\r
+    if (serial->fd < 0)\r
+       return "Unable to open serial port";\r
+\r
+    cloexec(serial->fd);\r
+\r
+    err = serial_configure(serial, cfg);\r
+    if (err)\r
+       return err;\r
+\r
+    *realhost = dupstr(cfg->serline);\r
+\r
+    if (!serial_by_fd)\r
+       serial_by_fd = newtree234(serial_compare_by_fd);\r
+    add234(serial_by_fd, serial);\r
+\r
+    serial_uxsel_setup(serial);\r
+\r
+    /*\r
+     * Specials are always available.\r
+     */\r
+    update_specials_menu(serial->frontend);\r
+\r
+    return NULL;\r
+}\r
+\r
+static void serial_close(Serial serial)\r
+{\r
+    if (serial->fd >= 0) {\r
+       close(serial->fd);\r
+       serial->fd = -1;\r
+    }\r
+}\r
+\r
+static void serial_free(void *handle)\r
+{\r
+    Serial serial = (Serial) handle;\r
+\r
+    serial_close(serial);\r
+\r
+    bufchain_clear(&serial->output_data);\r
+\r
+    sfree(serial);\r
+}\r
+\r
+static void serial_reconfig(void *handle, Config *cfg)\r
+{\r
+    Serial serial = (Serial) handle;\r
+\r
+    /*\r
+     * FIXME: what should we do if this returns an error?\r
+     */\r
+    serial_configure(serial, cfg);\r
+}\r
+\r
+static int serial_select_result(int fd, int event)\r
+{\r
+    Serial serial;\r
+    char buf[4096];\r
+    int ret;\r
+    int finished = FALSE;\r
+\r
+    serial = find234(serial_by_fd, &fd, serial_find_by_fd);\r
+\r
+    if (!serial)\r
+       return 1;                      /* spurious event; keep going */\r
+\r
+    if (event == 1) {\r
+       ret = read(serial->fd, buf, sizeof(buf));\r
+\r
+       if (ret == 0) {\r
+           /*\r
+            * Shouldn't happen on a real serial port, but I'm open\r
+            * to the idea that there might be two-way devices we\r
+            * can treat _like_ serial ports which can return EOF.\r
+            */\r
+           finished = TRUE;\r
+       } else if (ret < 0) {\r
+#ifdef EAGAIN\r
+           if (errno == EAGAIN)\r
+               return 1;              /* spurious */\r
+#endif\r
+#ifdef EWOULDBLOCK\r
+           if (errno == EWOULDBLOCK)\r
+               return 1;              /* spurious */\r
+#endif\r
+           perror("read serial port");\r
+           exit(1);\r
+       } else if (ret > 0) {\r
+           serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);\r
+           serial_uxsel_setup(serial); /* might acquire backlog and freeze */\r
+       }\r
+    } else if (event == 2) {\r
+       /*\r
+        * Attempt to send data down the pty.\r
+        */\r
+       serial_try_write(serial);\r
+    }\r
+\r
+    if (finished) {\r
+       serial_close(serial);\r
+\r
+       serial->finished = TRUE;\r
+\r
+       notify_remote_exit(serial->frontend);\r
+    }\r
+\r
+    return !finished;\r
+}\r
+\r
+static void serial_uxsel_setup(Serial serial)\r
+{\r
+    int rwx = 0;\r
+\r
+    if (serial->inbufsize <= SERIAL_MAX_BACKLOG)\r
+       rwx |= 1;\r
+    if (bufchain_size(&serial->output_data))\r
+        rwx |= 2;                      /* might also want to write to it */\r
+    uxsel_set(serial->fd, rwx, serial_select_result);\r
+}\r
+\r
+static void serial_try_write(Serial serial)\r
+{\r
+    void *data;\r
+    int len, ret;\r
+\r
+    assert(serial->fd >= 0);\r
+\r
+    while (bufchain_size(&serial->output_data) > 0) {\r
+        bufchain_prefix(&serial->output_data, &data, &len);\r
+       ret = write(serial->fd, data, len);\r
+\r
+        if (ret < 0 && (errno == EWOULDBLOCK)) {\r
+            /*\r
+             * We've sent all we can for the moment.\r
+             */\r
+            break;\r
+        }\r
+       if (ret < 0) {\r
+           perror("write serial port");\r
+           exit(1);\r
+       }\r
+       bufchain_consume(&serial->output_data, ret);\r
+    }\r
+\r
+    serial_uxsel_setup(serial);\r
+}\r
+\r
+/*\r
+ * Called to send data down the serial connection.\r
+ */\r
+static int serial_send(void *handle, char *buf, int len)\r
+{\r
+    Serial serial = (Serial) handle;\r
+\r
+    if (serial->fd < 0)\r
+       return 0;\r
+\r
+    bufchain_add(&serial->output_data, buf, len);\r
+    serial_try_write(serial);\r
+\r
+    return bufchain_size(&serial->output_data);\r
+}\r
+\r
+/*\r
+ * Called to query the current sendability status.\r
+ */\r
+static int serial_sendbuffer(void *handle)\r
+{\r
+    Serial serial = (Serial) handle;\r
+    return bufchain_size(&serial->output_data);\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void serial_size(void *handle, int width, int height)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Send serial special codes.\r
+ */\r
+static void serial_special(void *handle, Telnet_Special code)\r
+{\r
+    Serial serial = (Serial) handle;\r
+\r
+    if (serial->fd >= 0 && code == TS_BRK) {\r
+       tcsendbreak(serial->fd, 0);\r
+       logevent(serial->frontend, "Sending serial break at user request");\r
+    }\r
+\r
+    return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *serial_get_specials(void *handle)\r
+{\r
+    static const struct telnet_special specials[] = {\r
+       {"Break", TS_BRK},\r
+       {NULL, TS_EXITMENU}\r
+    };\r
+    return specials;\r
+}\r
+\r
+static int serial_connected(void *handle)\r
+{\r
+    return 1;                         /* always connected */\r
+}\r
+\r
+static int serial_sendok(void *handle)\r
+{\r
+    return 1;\r
+}\r
+\r
+static void serial_unthrottle(void *handle, int backlog)\r
+{\r
+    Serial serial = (Serial) handle;\r
+    serial->inbufsize = backlog;\r
+    serial_uxsel_setup(serial);\r
+}\r
+\r
+static int serial_ldisc(void *handle, int option)\r
+{\r
+    /*\r
+     * Local editing and local echo are off by default.\r
+     */\r
+    return 0;\r
+}\r
+\r
+static void serial_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static void serial_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static int serial_exitcode(void *handle)\r
+{\r
+    Serial serial = (Serial) handle;\r
+    if (serial->fd >= 0)\r
+        return -1;                     /* still connected */\r
+    else\r
+        /* Exit codes are a meaningless concept with serial ports */\r
+        return INT_MAX;\r
+}\r
+\r
+/*\r
+ * cfg_info for Serial does nothing at all.\r
+ */\r
+static int serial_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+Backend serial_backend = {\r
+    serial_init,\r
+    serial_free,\r
+    serial_reconfig,\r
+    serial_send,\r
+    serial_sendbuffer,\r
+    serial_size,\r
+    serial_special,\r
+    serial_get_specials,\r
+    serial_connected,\r
+    serial_exitcode,\r
+    serial_sendok,\r
+    serial_ldisc,\r
+    serial_provide_ldisc,\r
+    serial_provide_logctx,\r
+    serial_unthrottle,\r
+    serial_cfg_info,\r
+    "serial",\r
+    PROT_SERIAL,\r
+    0\r
+};\r
diff --git a/putty/UNIX/UXSFTP.C b/putty/UNIX/UXSFTP.C
new file mode 100644 (file)
index 0000000..a0ae9fb
--- /dev/null
@@ -0,0 +1,615 @@
+/*\r
+ * uxsftp.c: the Unix-specific parts of PSFTP and PSCP.\r
+ */\r
+\r
+#include <sys/time.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <stdlib.h>\r
+#include <fcntl.h>\r
+#include <dirent.h>\r
+#include <unistd.h>\r
+#include <utime.h>\r
+#include <errno.h>\r
+#include <assert.h>\r
+#include <glob.h>\r
+#ifndef HAVE_NO_SYS_SELECT_H\r
+#include <sys/select.h>\r
+#endif\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "psftp.h"\r
+#include "int64.h"\r
+\r
+/*\r
+ * In PSFTP our selects are synchronous, so these functions are\r
+ * empty stubs.\r
+ */\r
+int uxsel_input_add(int fd, int rwx) { return 0; }\r
+void uxsel_input_remove(int id) { }\r
+\r
+char *x_get_default(const char *key)\r
+{\r
+    return NULL;                      /* this is a stub */\r
+}\r
+\r
+void platform_get_x11_auth(struct X11Display *display, const Config *cfg)\r
+{\r
+    /* Do nothing, therefore no auth. */\r
+}\r
+const int platform_uses_x11_unix_by_default = TRUE;\r
+\r
+/*\r
+ * Default settings that are specific to PSFTP.\r
+ */\r
+char *platform_default_s(const char *name)\r
+{\r
+    return NULL;\r
+}\r
+\r
+int platform_default_i(const char *name, int def)\r
+{\r
+    return def;\r
+}\r
+\r
+FontSpec platform_default_fontspec(const char *name)\r
+{\r
+    FontSpec ret;\r
+    *ret.name = '\0';\r
+    return ret;\r
+}\r
+\r
+Filename platform_default_filename(const char *name)\r
+{\r
+    Filename ret;\r
+    if (!strcmp(name, "LogFileName"))\r
+       strcpy(ret.path, "putty.log");\r
+    else\r
+       *ret.path = '\0';\r
+    return ret;\r
+}\r
+\r
+char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
+\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    int ret;\r
+    ret = cmdline_get_passwd_input(p, in, inlen);\r
+    if (ret == -1)\r
+       ret = console_get_userpass_input(p, in, inlen);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Set local current directory. Returns NULL on success, or else an\r
+ * error message which must be freed after printing.\r
+ */\r
+char *psftp_lcd(char *dir)\r
+{\r
+    if (chdir(dir) < 0)\r
+       return dupprintf("%s: chdir: %s", dir, strerror(errno));\r
+    else\r
+       return NULL;\r
+}\r
+\r
+/*\r
+ * Get local current directory. Returns a string which must be\r
+ * freed.\r
+ */\r
+char *psftp_getcwd(void)\r
+{\r
+    char *buffer, *ret;\r
+    int size = 256;\r
+\r
+    buffer = snewn(size, char);\r
+    while (1) {\r
+       ret = getcwd(buffer, size);\r
+       if (ret != NULL)\r
+           return ret;\r
+       if (errno != ERANGE) {\r
+           sfree(buffer);\r
+           return dupprintf("[cwd unavailable: %s]", strerror(errno));\r
+       }\r
+       /*\r
+        * Otherwise, ERANGE was returned, meaning the buffer\r
+        * wasn't big enough.\r
+        */\r
+       size = size * 3 / 2;\r
+       buffer = sresize(buffer, size, char);\r
+    }\r
+}\r
+\r
+struct RFile {\r
+    int fd;\r
+};\r
+\r
+RFile *open_existing_file(char *name, uint64 *size,\r
+                         unsigned long *mtime, unsigned long *atime)\r
+{\r
+    int fd;\r
+    RFile *ret;\r
+\r
+    fd = open(name, O_RDONLY);\r
+    if (fd < 0)\r
+       return NULL;\r
+\r
+    ret = snew(RFile);\r
+    ret->fd = fd;\r
+\r
+    if (size || mtime || atime) {\r
+       struct stat statbuf;\r
+       if (fstat(fd, &statbuf) < 0) {\r
+           fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));\r
+           memset(&statbuf, 0, sizeof(statbuf));\r
+       }\r
+\r
+       if (size)\r
+           *size = uint64_make((statbuf.st_size >> 16) >> 16,\r
+                               statbuf.st_size);\r
+               \r
+       if (mtime)\r
+           *mtime = statbuf.st_mtime;\r
+\r
+       if (atime)\r
+           *atime = statbuf.st_atime;\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+int read_from_file(RFile *f, void *buffer, int length)\r
+{\r
+    return read(f->fd, buffer, length);\r
+}\r
+\r
+void close_rfile(RFile *f)\r
+{\r
+    close(f->fd);\r
+    sfree(f);\r
+}\r
+\r
+struct WFile {\r
+    int fd;\r
+    char *name;\r
+};\r
+\r
+WFile *open_new_file(char *name)\r
+{\r
+    int fd;\r
+    WFile *ret;\r
+\r
+    fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666);\r
+    if (fd < 0)\r
+       return NULL;\r
+\r
+    ret = snew(WFile);\r
+    ret->fd = fd;\r
+    ret->name = dupstr(name);\r
+\r
+    return ret;\r
+}\r
+\r
+\r
+WFile *open_existing_wfile(char *name, uint64 *size)\r
+{\r
+    int fd;\r
+    WFile *ret;\r
+\r
+    fd = open(name, O_APPEND | O_WRONLY);\r
+    if (fd < 0)\r
+       return NULL;\r
+\r
+    ret = snew(WFile);\r
+    ret->fd = fd;\r
+    ret->name = dupstr(name);\r
+\r
+    if (size) {\r
+       struct stat statbuf;\r
+       if (fstat(fd, &statbuf) < 0) {\r
+           fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));\r
+           memset(&statbuf, 0, sizeof(statbuf));\r
+       }\r
+\r
+       *size = uint64_make((statbuf.st_size >> 16) >> 16,\r
+                           statbuf.st_size);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+int write_to_file(WFile *f, void *buffer, int length)\r
+{\r
+    char *p = (char *)buffer;\r
+    int so_far = 0;\r
+\r
+    /* Keep trying until we've really written as much as we can. */\r
+    while (length > 0) {\r
+       int ret = write(f->fd, p, length);\r
+\r
+       if (ret < 0)\r
+           return ret;\r
+\r
+       if (ret == 0)\r
+           break;\r
+\r
+       p += ret;\r
+       length -= ret;\r
+       so_far += ret;\r
+    }\r
+\r
+    return so_far;\r
+}\r
+\r
+void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)\r
+{\r
+    struct utimbuf ut;\r
+\r
+    ut.actime = atime;\r
+    ut.modtime = mtime;\r
+\r
+    utime(f->name, &ut);\r
+}\r
+\r
+/* Closes and frees the WFile */\r
+void close_wfile(WFile *f)\r
+{\r
+    close(f->fd);\r
+    sfree(f->name);\r
+    sfree(f);\r
+}\r
+\r
+/* Seek offset bytes through file, from whence, where whence is\r
+   FROM_START, FROM_CURRENT, or FROM_END */\r
+int seek_file(WFile *f, uint64 offset, int whence)\r
+{\r
+    off_t fileofft;\r
+    int lseek_whence;\r
+    \r
+    fileofft = (((off_t) offset.hi << 16) << 16) + offset.lo;\r
+\r
+    switch (whence) {\r
+    case FROM_START:\r
+       lseek_whence = SEEK_SET;\r
+       break;\r
+    case FROM_CURRENT:\r
+       lseek_whence = SEEK_CUR;\r
+       break;\r
+    case FROM_END:\r
+       lseek_whence = SEEK_END;\r
+       break;\r
+    default:\r
+       return -1;\r
+    }\r
+\r
+    return lseek(f->fd, fileofft, lseek_whence) >= 0 ? 0 : -1;\r
+}\r
+\r
+uint64 get_file_posn(WFile *f)\r
+{\r
+    off_t fileofft;\r
+    uint64 ret;\r
+\r
+    fileofft = lseek(f->fd, (off_t) 0, SEEK_CUR);\r
+\r
+    ret = uint64_make((fileofft >> 16) >> 16, fileofft);\r
+\r
+    return ret;\r
+}\r
+\r
+int file_type(char *name)\r
+{\r
+    struct stat statbuf;\r
+\r
+    if (stat(name, &statbuf) < 0) {\r
+       if (errno != ENOENT)\r
+           fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));\r
+       return FILE_TYPE_NONEXISTENT;\r
+    }\r
+\r
+    if (S_ISREG(statbuf.st_mode))\r
+       return FILE_TYPE_FILE;\r
+\r
+    if (S_ISDIR(statbuf.st_mode))\r
+       return FILE_TYPE_DIRECTORY;\r
+\r
+    return FILE_TYPE_WEIRD;\r
+}\r
+\r
+struct DirHandle {\r
+    DIR *dir;\r
+};\r
+\r
+DirHandle *open_directory(char *name)\r
+{\r
+    DIR *dir;\r
+    DirHandle *ret;\r
+\r
+    dir = opendir(name);\r
+    if (!dir)\r
+       return NULL;\r
+\r
+    ret = snew(DirHandle);\r
+    ret->dir = dir;\r
+    return ret;\r
+}\r
+\r
+char *read_filename(DirHandle *dir)\r
+{\r
+    struct dirent *de;\r
+\r
+    do {\r
+       de = readdir(dir->dir);\r
+       if (de == NULL)\r
+           return NULL;\r
+    } while ((de->d_name[0] == '.' &&\r
+             (de->d_name[1] == '\0' ||\r
+              (de->d_name[1] == '.' && de->d_name[2] == '\0'))));\r
+\r
+    return dupstr(de->d_name);\r
+}\r
+\r
+void close_directory(DirHandle *dir)\r
+{\r
+    closedir(dir->dir);\r
+    sfree(dir);\r
+}\r
+\r
+int test_wildcard(char *name, int cmdline)\r
+{\r
+    struct stat statbuf;\r
+\r
+    if (stat(name, &statbuf) == 0) {\r
+       return WCTYPE_FILENAME;\r
+    } else if (cmdline) {\r
+       /*\r
+        * On Unix, we never need to parse wildcards coming from\r
+        * the command line, because the shell will have expanded\r
+        * them into a filename list already.\r
+        */\r
+       return WCTYPE_NONEXISTENT;\r
+    } else {\r
+       glob_t globbed;\r
+       int ret = WCTYPE_NONEXISTENT;\r
+\r
+       if (glob(name, GLOB_ERR, NULL, &globbed) == 0) {\r
+           if (globbed.gl_pathc > 0)\r
+               ret = WCTYPE_WILDCARD;\r
+           globfree(&globbed);\r
+       }\r
+\r
+       return ret;\r
+    }\r
+}\r
+\r
+/*\r
+ * Actually return matching file names for a local wildcard.\r
+ */\r
+struct WildcardMatcher {\r
+    glob_t globbed;\r
+    int i;\r
+};\r
+WildcardMatcher *begin_wildcard_matching(char *name) {\r
+    WildcardMatcher *ret = snew(WildcardMatcher);\r
+\r
+    if (glob(name, 0, NULL, &ret->globbed) < 0) {\r
+       sfree(ret);\r
+       return NULL;\r
+    }\r
+\r
+    ret->i = 0;\r
+\r
+    return ret;\r
+}\r
+char *wildcard_get_filename(WildcardMatcher *dir) {\r
+    if (dir->i < dir->globbed.gl_pathc) {\r
+       return dupstr(dir->globbed.gl_pathv[dir->i++]);\r
+    } else\r
+       return NULL;\r
+}\r
+void finish_wildcard_matching(WildcardMatcher *dir) {\r
+    globfree(&dir->globbed);\r
+    sfree(dir);\r
+}\r
+\r
+int vet_filename(char *name)\r
+{\r
+    if (strchr(name, '/'))\r
+       return FALSE;\r
+\r
+    if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2])))\r
+       return FALSE;\r
+\r
+    return TRUE;\r
+}\r
+\r
+int create_directory(char *name)\r
+{\r
+    return mkdir(name, 0777) == 0;\r
+}\r
+\r
+char *dir_file_cat(char *dir, char *file)\r
+{\r
+    return dupcat(dir, "/", file, NULL);\r
+}\r
+\r
+/*\r
+ * Do a select() between all currently active network fds and\r
+ * optionally stdin.\r
+ */\r
+static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)\r
+{\r
+    fd_set rset, wset, xset;\r
+    int i, fdcount, fdsize, *fdlist;\r
+    int fd, fdstate, rwx, ret, maxfd;\r
+    long now = GETTICKCOUNT();\r
+\r
+    fdlist = NULL;\r
+    fdcount = fdsize = 0;\r
+\r
+    do {\r
+\r
+       /* Count the currently active fds. */\r
+       i = 0;\r
+       for (fd = first_fd(&fdstate, &rwx); fd >= 0;\r
+            fd = next_fd(&fdstate, &rwx)) i++;\r
+\r
+       if (i < 1 && !no_fds_ok)\r
+           return -1;                 /* doom */\r
+\r
+       /* Expand the fdlist buffer if necessary. */\r
+       if (i > fdsize) {\r
+           fdsize = i + 16;\r
+           fdlist = sresize(fdlist, fdsize, int);\r
+       }\r
+\r
+       FD_ZERO(&rset);\r
+       FD_ZERO(&wset);\r
+       FD_ZERO(&xset);\r
+       maxfd = 0;\r
+\r
+       /*\r
+        * Add all currently open fds to the select sets, and store\r
+        * them in fdlist as well.\r
+        */\r
+       fdcount = 0;\r
+       for (fd = first_fd(&fdstate, &rwx); fd >= 0;\r
+            fd = next_fd(&fdstate, &rwx)) {\r
+           fdlist[fdcount++] = fd;\r
+           if (rwx & 1)\r
+               FD_SET_MAX(fd, maxfd, rset);\r
+           if (rwx & 2)\r
+               FD_SET_MAX(fd, maxfd, wset);\r
+           if (rwx & 4)\r
+               FD_SET_MAX(fd, maxfd, xset);\r
+       }\r
+\r
+       if (include_stdin)\r
+           FD_SET_MAX(0, maxfd, rset);\r
+\r
+       do {\r
+           long next, ticks;\r
+           struct timeval tv, *ptv;\r
+\r
+           if (run_timers(now, &next)) {\r
+               ticks = next - GETTICKCOUNT();\r
+               if (ticks <= 0)\r
+                   ticks = 1;         /* just in case */\r
+               tv.tv_sec = ticks / 1000;\r
+               tv.tv_usec = ticks % 1000 * 1000;\r
+               ptv = &tv;\r
+           } else {\r
+               ptv = NULL;\r
+           }\r
+           ret = select(maxfd, &rset, &wset, &xset, ptv);\r
+           if (ret == 0)\r
+               now = next;\r
+           else {\r
+               long newnow = GETTICKCOUNT();\r
+               /*\r
+                * Check to see whether the system clock has\r
+                * changed massively during the select.\r
+                */\r
+               if (newnow - now < 0 || newnow - now > next - now) {\r
+                   /*\r
+                    * If so, look at the elapsed time in the\r
+                    * select and use it to compute a new\r
+                    * tickcount_offset.\r
+                    */\r
+                   long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000;\r
+                   /* So we'd like GETTICKCOUNT to have returned othernow,\r
+                    * but instead it return newnow. Hence ... */\r
+                   tickcount_offset += othernow - newnow;\r
+                   now = othernow;\r
+               } else {\r
+                   now = newnow;\r
+               }\r
+           }\r
+       } while (ret < 0 && errno != EINTR);\r
+    } while (ret == 0);\r
+\r
+    if (ret < 0) {\r
+       perror("select");\r
+       exit(1);\r
+    }\r
+\r
+    for (i = 0; i < fdcount; i++) {\r
+       fd = fdlist[i];\r
+       /*\r
+        * We must process exceptional notifications before\r
+        * ordinary readability ones, or we may go straight\r
+        * past the urgent marker.\r
+        */\r
+       if (FD_ISSET(fd, &xset))\r
+           select_result(fd, 4);\r
+       if (FD_ISSET(fd, &rset))\r
+           select_result(fd, 1);\r
+       if (FD_ISSET(fd, &wset))\r
+           select_result(fd, 2);\r
+    }\r
+\r
+    sfree(fdlist);\r
+\r
+    return FD_ISSET(0, &rset) ? 1 : 0;\r
+}\r
+\r
+/*\r
+ * Wait for some network data and process it.\r
+ */\r
+int ssh_sftp_loop_iteration(void)\r
+{\r
+    return ssh_sftp_do_select(FALSE, FALSE);\r
+}\r
+\r
+/*\r
+ * Read a PSFTP command line from stdin.\r
+ */\r
+char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok)\r
+{\r
+    char *buf;\r
+    int buflen, bufsize, ret;\r
+\r
+    fputs(prompt, stdout);\r
+    fflush(stdout);\r
+\r
+    buf = NULL;\r
+    buflen = bufsize = 0;\r
+\r
+    while (1) {\r
+       ret = ssh_sftp_do_select(TRUE, no_fds_ok);\r
+       if (ret < 0) {\r
+           printf("connection died\n");\r
+           return NULL;               /* woop woop */\r
+       }\r
+       if (ret > 0) {\r
+           if (buflen >= bufsize) {\r
+               bufsize = buflen + 512;\r
+               buf = sresize(buf, bufsize, char);\r
+           }\r
+           ret = read(0, buf+buflen, 1);\r
+           if (ret < 0) {\r
+               perror("read");\r
+               return NULL;\r
+           }\r
+           if (ret == 0) {\r
+               /* eof on stdin; no error, but no answer either */\r
+               return NULL;\r
+           }\r
+\r
+           if (buf[buflen++] == '\n') {\r
+               /* we have a full line */\r
+               return buf;\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Main program: do platform-specific initialisation and then call\r
+ * psftp_main().\r
+ */\r
+int main(int argc, char *argv[])\r
+{\r
+    uxsel_init();\r
+    return psftp_main(argc, argv);\r
+}\r
diff --git a/putty/UNIX/UXSIGNAL.C b/putty/UNIX/UXSIGNAL.C
new file mode 100644 (file)
index 0000000..7153903
--- /dev/null
@@ -0,0 +1,45 @@
+#include <signal.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+/*\r
+ * Calling signal() is non-portable, as it varies in meaning\r
+ * between platforms and depending on feature macros, and has\r
+ * stupid semantics at least some of the time.\r
+ *\r
+ * This function provides the same interface as the libc function,\r
+ * but provides consistent semantics.  It assumes POSIX semantics\r
+ * for sigaction() (so you might need to do some more work if you\r
+ * port to something ancient like SunOS 4)\r
+ */\r
+void (*putty_signal(int sig, void (*func)(int)))(int) {\r
+    struct sigaction sa;\r
+    struct sigaction old;\r
+    \r
+    sa.sa_handler = func;\r
+    if(sigemptyset(&sa.sa_mask) < 0)\r
+       return SIG_ERR;\r
+    sa.sa_flags = SA_RESTART;\r
+    if(sigaction(sig, &sa, &old) < 0)\r
+       return SIG_ERR;\r
+    return old.sa_handler;\r
+}\r
+\r
+void block_signal(int sig, int block_it)\r
+{\r
+    sigset_t ss;\r
+\r
+    sigemptyset(&ss);\r
+    sigaddset(&ss, sig);\r
+    if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) {\r
+       perror("sigprocmask");\r
+       exit(1);\r
+    }\r
+}\r
+\r
+/*\r
+Local Variables:\r
+c-basic-offset:4\r
+comment-column:40\r
+End:\r
+*/\r
diff --git a/putty/UNIX/UXSTORE.C b/putty/UNIX/UXSTORE.C
new file mode 100644 (file)
index 0000000..29cfa34
--- /dev/null
@@ -0,0 +1,685 @@
+/*\r
+ * uxstore.c: Unix-specific implementation of the interface defined\r
+ * in storage.h.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <assert.h>\r
+#include <errno.h>\r
+#include <ctype.h>\r
+#include <limits.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#include <dirent.h>\r
+#include <sys/stat.h>\r
+#include <sys/types.h>\r
+#include <pwd.h>\r
+#include "putty.h"\r
+#include "storage.h"\r
+#include "tree234.h"\r
+\r
+#ifdef PATH_MAX\r
+#define FNLEN PATH_MAX\r
+#else\r
+#define FNLEN 1024 /* XXX */\r
+#endif\r
+\r
+enum {\r
+    INDEX_DIR, INDEX_HOSTKEYS, INDEX_HOSTKEYS_TMP, INDEX_RANDSEED,\r
+    INDEX_SESSIONDIR, INDEX_SESSION,\r
+};\r
+\r
+static const char hex[16] = "0123456789ABCDEF";\r
+\r
+static char *mungestr(const char *in)\r
+{\r
+    char *out, *ret;\r
+\r
+    if (!in || !*in)\r
+        in = "Default Settings";\r
+\r
+    ret = out = snewn(3*strlen(in)+1, char);\r
+\r
+    while (*in) {\r
+        /*\r
+         * There are remarkably few punctuation characters that\r
+         * aren't shell-special in some way or likely to be used as\r
+         * separators in some file format or another! Hence we use\r
+         * opt-in for safe characters rather than opt-out for\r
+         * specific unsafe ones...\r
+         */\r
+       if (*in!='+' && *in!='-' && *in!='.' && *in!='@' && *in!='_' &&\r
+            !(*in >= '0' && *in <= '9') &&\r
+            !(*in >= 'A' && *in <= 'Z') &&\r
+            !(*in >= 'a' && *in <= 'z')) {\r
+           *out++ = '%';\r
+           *out++ = hex[((unsigned char) *in) >> 4];\r
+           *out++ = hex[((unsigned char) *in) & 15];\r
+       } else\r
+           *out++ = *in;\r
+       in++;\r
+    }\r
+    *out = '\0';\r
+    return ret;\r
+}\r
+\r
+static char *unmungestr(const char *in)\r
+{\r
+    char *out, *ret;\r
+    out = ret = snewn(strlen(in)+1, char);\r
+    while (*in) {\r
+       if (*in == '%' && in[1] && in[2]) {\r
+           int i, j;\r
+\r
+           i = in[1] - '0';\r
+           i -= (i > 9 ? 7 : 0);\r
+           j = in[2] - '0';\r
+           j -= (j > 9 ? 7 : 0);\r
+\r
+           *out++ = (i << 4) + j;\r
+           in += 3;\r
+       } else {\r
+           *out++ = *in++;\r
+       }\r
+    }\r
+    *out = '\0';\r
+    return ret;\r
+}\r
+\r
+static char *make_filename(int index, const char *subname)\r
+{\r
+    char *env, *tmp, *ret;\r
+\r
+    /*\r
+     * Allow override of the PuTTY configuration location, and of\r
+     * specific subparts of it, by means of environment variables.\r
+     */\r
+    if (index == INDEX_DIR) {\r
+       struct passwd *pwd;\r
+\r
+       env = getenv("PUTTYDIR");\r
+       if (env)\r
+           return dupstr(env);\r
+       env = getenv("HOME");\r
+       if (env)\r
+           return dupprintf("%s/.putty", env);\r
+       pwd = getpwuid(getuid());\r
+       if (pwd && pwd->pw_dir)\r
+           return dupprintf("%s/.putty", pwd->pw_dir);\r
+       return dupstr("/.putty");\r
+    }\r
+    if (index == INDEX_SESSIONDIR) {\r
+       env = getenv("PUTTYSESSIONS");\r
+       if (env)\r
+           return dupstr(env);\r
+       tmp = make_filename(INDEX_DIR, NULL);\r
+       ret = dupprintf("%s/sessions", tmp);\r
+       sfree(tmp);\r
+       return ret;\r
+    }\r
+    if (index == INDEX_SESSION) {\r
+        char *munged = mungestr(subname);\r
+       tmp = make_filename(INDEX_SESSIONDIR, NULL);\r
+       ret = dupprintf("%s/%s", tmp, munged);\r
+       sfree(tmp);\r
+       sfree(munged);\r
+       return ret;\r
+    }\r
+    if (index == INDEX_HOSTKEYS) {\r
+       env = getenv("PUTTYSSHHOSTKEYS");\r
+       if (env)\r
+           return dupstr(env);\r
+       tmp = make_filename(INDEX_DIR, NULL);\r
+       ret = dupprintf("%s/sshhostkeys", tmp);\r
+       sfree(tmp);\r
+       return ret;\r
+    }\r
+    if (index == INDEX_HOSTKEYS_TMP) {\r
+       tmp = make_filename(INDEX_HOSTKEYS, NULL);\r
+       ret = dupprintf("%s.tmp", tmp);\r
+       sfree(tmp);\r
+       return ret;\r
+    }\r
+    if (index == INDEX_RANDSEED) {\r
+       env = getenv("PUTTYRANDOMSEED");\r
+       if (env)\r
+           return dupstr(env);\r
+       tmp = make_filename(INDEX_DIR, NULL);\r
+       ret = dupprintf("%s/randomseed", tmp);\r
+       sfree(tmp);\r
+       return ret;\r
+    }\r
+    tmp = make_filename(INDEX_DIR, NULL);\r
+    ret = dupprintf("%s/ERROR", tmp);\r
+    sfree(tmp);\r
+    return ret;\r
+}\r
+\r
+void *open_settings_w(const char *sessionname, char **errmsg)\r
+{\r
+    char *filename;\r
+    FILE *fp;\r
+\r
+    *errmsg = NULL;\r
+\r
+    /*\r
+     * Start by making sure the .putty directory and its sessions\r
+     * subdir actually exist. Ignore error returns from mkdir since\r
+     * they're perfectly likely to be `already exists', and any\r
+     * other error will trip us up later on so there's no real need\r
+     * to catch it now.\r
+     */\r
+    filename = make_filename(INDEX_SESSIONDIR, NULL);\r
+    if (mkdir(filename, 0700) != 0) {\r
+       char *filename2 = make_filename(INDEX_DIR, NULL);\r
+       mkdir(filename2, 0700);\r
+       sfree(filename2);\r
+       mkdir(filename, 0700);\r
+    }\r
+    sfree(filename);\r
+\r
+    filename = make_filename(INDEX_SESSION, sessionname);\r
+    fp = fopen(filename, "w");\r
+    if (!fp) {\r
+        *errmsg = dupprintf("Unable to create %s: %s",\r
+                            filename, strerror(errno));\r
+       sfree(filename);\r
+       return NULL;                   /* can't open */\r
+    }\r
+    sfree(filename);\r
+    return fp;\r
+}\r
+\r
+void write_setting_s(void *handle, const char *key, const char *value)\r
+{\r
+    FILE *fp = (FILE *)handle;\r
+    fprintf(fp, "%s=%s\n", key, value);\r
+}\r
+\r
+void write_setting_i(void *handle, const char *key, int value)\r
+{\r
+    FILE *fp = (FILE *)handle;\r
+    fprintf(fp, "%s=%d\n", key, value);\r
+}\r
+\r
+void close_settings_w(void *handle)\r
+{\r
+    FILE *fp = (FILE *)handle;\r
+    fclose(fp);\r
+}\r
+\r
+/*\r
+ * Reading settings, for the moment, is done by retrieving X\r
+ * resources from the X display. When we introduce disk files, I\r
+ * think what will happen is that the X resources will override\r
+ * PuTTY's inbuilt defaults, but that the disk files will then\r
+ * override those. This isn't optimal, but it's the best I can\r
+ * immediately work out.\r
+ * FIXME: the above comment is a bit out of date. Did it happen?\r
+ */\r
+\r
+struct skeyval {\r
+    const char *key;\r
+    const char *value;\r
+};\r
+\r
+static tree234 *xrmtree = NULL;\r
+\r
+int keycmp(void *av, void *bv)\r
+{\r
+    struct skeyval *a = (struct skeyval *)av;\r
+    struct skeyval *b = (struct skeyval *)bv;\r
+    return strcmp(a->key, b->key);\r
+}\r
+\r
+void provide_xrm_string(char *string)\r
+{\r
+    char *p, *q, *key;\r
+    struct skeyval *xrms, *ret;\r
+\r
+    p = q = strchr(string, ':');\r
+    if (!q) {\r
+       fprintf(stderr, "pterm: expected a colon in resource string"\r
+               " \"%s\"\n", string);\r
+       return;\r
+    }\r
+    q++;\r
+    while (p > string && p[-1] != '.' && p[-1] != '*')\r
+       p--;\r
+    xrms = snew(struct skeyval);\r
+    key = snewn(q-p, char);\r
+    memcpy(key, p, q-p);\r
+    key[q-p-1] = '\0';\r
+    xrms->key = key;\r
+    while (*q && isspace((unsigned char)*q))\r
+       q++;\r
+    xrms->value = dupstr(q);\r
+\r
+    if (!xrmtree)\r
+       xrmtree = newtree234(keycmp);\r
+\r
+    ret = add234(xrmtree, xrms);\r
+    if (ret) {\r
+       /* Override an existing string. */\r
+       del234(xrmtree, ret);\r
+       add234(xrmtree, xrms);\r
+    }\r
+}\r
+\r
+const char *get_setting(const char *key)\r
+{\r
+    struct skeyval tmp, *ret;\r
+    tmp.key = key;\r
+    if (xrmtree) {\r
+       ret = find234(xrmtree, &tmp, NULL);\r
+       if (ret)\r
+           return ret->value;\r
+    }\r
+    return x_get_default(key);\r
+}\r
+\r
+void *open_settings_r(const char *sessionname)\r
+{\r
+    char *filename;\r
+    FILE *fp;\r
+    char *line;\r
+    tree234 *ret;\r
+\r
+    filename = make_filename(INDEX_SESSION, sessionname);\r
+    fp = fopen(filename, "r");\r
+    sfree(filename);\r
+    if (!fp)\r
+       return NULL;                   /* can't open */\r
+\r
+    ret = newtree234(keycmp);\r
+\r
+    while ( (line = fgetline(fp)) ) {\r
+        char *value = strchr(line, '=');\r
+        struct skeyval *kv;\r
+\r
+        if (!value)\r
+            continue;\r
+        *value++ = '\0';\r
+        value[strcspn(value, "\r\n")] = '\0';   /* trim trailing NL */\r
+\r
+        kv = snew(struct skeyval);\r
+        kv->key = dupstr(line);\r
+        kv->value = dupstr(value);\r
+        add234(ret, kv);\r
+\r
+        sfree(line);\r
+    }\r
+\r
+    fclose(fp);\r
+\r
+    return ret;\r
+}\r
+\r
+char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)\r
+{\r
+    tree234 *tree = (tree234 *)handle;\r
+    const char *val;\r
+    struct skeyval tmp, *kv;\r
+\r
+    tmp.key = key;\r
+    if (tree != NULL &&\r
+        (kv = find234(tree, &tmp, NULL)) != NULL) {\r
+        val = kv->value;\r
+        assert(val != NULL);\r
+    } else\r
+        val = get_setting(key);\r
+\r
+    if (!val)\r
+       return NULL;\r
+    else {\r
+       strncpy(buffer, val, buflen);\r
+       buffer[buflen-1] = '\0';\r
+       return buffer;\r
+    }\r
+}\r
+\r
+int read_setting_i(void *handle, const char *key, int defvalue)\r
+{\r
+    tree234 *tree = (tree234 *)handle;\r
+    const char *val;\r
+    struct skeyval tmp, *kv;\r
+\r
+    tmp.key = key;\r
+    if (tree != NULL &&\r
+        (kv = find234(tree, &tmp, NULL)) != NULL) {\r
+        val = kv->value;\r
+        assert(val != NULL);\r
+    } else\r
+        val = get_setting(key);\r
+\r
+    if (!val)\r
+       return defvalue;\r
+    else\r
+       return atoi(val);\r
+}\r
+\r
+int read_setting_fontspec(void *handle, const char *name, FontSpec *result)\r
+{\r
+    /*\r
+     * In GTK1-only PuTTY, we used to store font names simply as a\r
+     * valid X font description string (logical or alias), under a\r
+     * bare key such as "Font".\r
+     * \r
+     * In GTK2 PuTTY, we have a prefix system where "client:"\r
+     * indicates a Pango font and "server:" an X one; existing\r
+     * configuration needs to be reinterpreted as having the\r
+     * "server:" prefix, so we change the storage key from the\r
+     * provided name string (e.g. "Font") to a suffixed one\r
+     * ("FontName").\r
+     */\r
+    char *suffname = dupcat(name, "Name", NULL);\r
+    if (read_setting_s(handle, suffname, result->name, sizeof(result->name))) {\r
+       sfree(suffname);\r
+       return TRUE;                   /* got new-style name */\r
+    }\r
+    sfree(suffname);\r
+\r
+    /* Fall back to old-style name. */\r
+    memcpy(result->name, "server:", 7);\r
+    if (!read_setting_s(handle, name,\r
+                       result->name + 7, sizeof(result->name) - 7) ||\r
+       !result->name[7]) {\r
+       result->name[0] = '\0';\r
+       return FALSE;\r
+    } else {\r
+       return TRUE;\r
+    }\r
+}\r
+int read_setting_filename(void *handle, const char *name, Filename *result)\r
+{\r
+    return !!read_setting_s(handle, name, result->path, sizeof(result->path));\r
+}\r
+\r
+void write_setting_fontspec(void *handle, const char *name, FontSpec result)\r
+{\r
+    /*\r
+     * read_setting_fontspec had to handle two cases, but when\r
+     * writing our settings back out we simply always generate the\r
+     * new-style name.\r
+     */\r
+    char *suffname = dupcat(name, "Name", NULL);\r
+    write_setting_s(handle, suffname, result.name);\r
+    sfree(suffname);\r
+}\r
+void write_setting_filename(void *handle, const char *name, Filename result)\r
+{\r
+    write_setting_s(handle, name, result.path);\r
+}\r
+\r
+void close_settings_r(void *handle)\r
+{\r
+    tree234 *tree = (tree234 *)handle;\r
+    struct skeyval *kv;\r
+\r
+    if (!tree)\r
+        return;\r
+\r
+    while ( (kv = index234(tree, 0)) != NULL) {\r
+        del234(tree, kv);\r
+        sfree((char *)kv->key);\r
+        sfree((char *)kv->value);\r
+        sfree(kv);\r
+    }\r
+\r
+    freetree234(tree);\r
+}\r
+\r
+void del_settings(const char *sessionname)\r
+{\r
+    char *filename;\r
+    filename = make_filename(INDEX_SESSION, sessionname);\r
+    unlink(filename);\r
+    sfree(filename);\r
+}\r
+\r
+void *enum_settings_start(void)\r
+{\r
+    DIR *dp;\r
+    char *filename;\r
+\r
+    filename = make_filename(INDEX_SESSIONDIR, NULL);\r
+    dp = opendir(filename);\r
+    sfree(filename);\r
+\r
+    return dp;\r
+}\r
+\r
+char *enum_settings_next(void *handle, char *buffer, int buflen)\r
+{\r
+    DIR *dp = (DIR *)handle;\r
+    struct dirent *de;\r
+    struct stat st;\r
+    char *fullpath;\r
+    int maxlen, thislen, len;\r
+    char *unmunged;\r
+\r
+    fullpath = make_filename(INDEX_SESSIONDIR, NULL);\r
+    maxlen = len = strlen(fullpath);\r
+\r
+    while ( (de = readdir(dp)) != NULL ) {\r
+        thislen = len + 1 + strlen(de->d_name);\r
+       if (maxlen < thislen) {\r
+           maxlen = thislen;\r
+           fullpath = sresize(fullpath, maxlen+1, char);\r
+       }\r
+       fullpath[len] = '/';\r
+       strncpy(fullpath+len+1, de->d_name, thislen - (len+1));\r
+       fullpath[thislen] = '\0';\r
+\r
+        if (stat(fullpath, &st) < 0 || !S_ISREG(st.st_mode))\r
+            continue;                  /* try another one */\r
+\r
+        unmunged = unmungestr(de->d_name);\r
+        strncpy(buffer, unmunged, buflen);\r
+        buffer[buflen-1] = '\0';\r
+        sfree(unmunged);\r
+       sfree(fullpath);\r
+        return buffer;\r
+    }\r
+\r
+    sfree(fullpath);\r
+    return NULL;\r
+}\r
+\r
+void enum_settings_finish(void *handle)\r
+{\r
+    DIR *dp = (DIR *)handle;\r
+    closedir(dp);\r
+}\r
+\r
+/*\r
+ * Lines in the host keys file are of the form\r
+ * \r
+ *   type@port:hostname keydata\r
+ * \r
+ * e.g.\r
+ * \r
+ *   rsa@22:foovax.example.org 0x23,0x293487364395345345....2343\r
+ */\r
+int verify_host_key(const char *hostname, int port,\r
+                   const char *keytype, const char *key)\r
+{\r
+    FILE *fp;\r
+    char *filename;\r
+    char *line;\r
+    int ret;\r
+\r
+    filename = make_filename(INDEX_HOSTKEYS, NULL);\r
+    fp = fopen(filename, "r");\r
+    sfree(filename);\r
+    if (!fp)\r
+       return 1;                      /* key does not exist */\r
+\r
+    ret = 1;\r
+    while ( (line = fgetline(fp)) ) {\r
+       int i;\r
+       char *p = line;\r
+       char porttext[20];\r
+\r
+       line[strcspn(line, "\n")] = '\0';   /* strip trailing newline */\r
+\r
+       i = strlen(keytype);\r
+       if (strncmp(p, keytype, i))\r
+           goto done;\r
+       p += i;\r
+\r
+       if (*p != '@')\r
+           goto done;\r
+       p++;\r
+\r
+       sprintf(porttext, "%d", port);\r
+       i = strlen(porttext);\r
+       if (strncmp(p, porttext, i))\r
+           goto done;\r
+       p += i;\r
+\r
+       if (*p != ':')\r
+           goto done;\r
+       p++;\r
+\r
+       i = strlen(hostname);\r
+       if (strncmp(p, hostname, i))\r
+           goto done;\r
+       p += i;\r
+\r
+       if (*p != ' ')\r
+           goto done;\r
+       p++;\r
+\r
+       /*\r
+        * Found the key. Now just work out whether it's the right\r
+        * one or not.\r
+        */\r
+       if (!strcmp(p, key))\r
+           ret = 0;                   /* key matched OK */\r
+       else\r
+           ret = 2;                   /* key mismatch */\r
+\r
+       done:\r
+       sfree(line);\r
+       if (ret != 1)\r
+           break;\r
+    }\r
+\r
+    fclose(fp);\r
+    return ret;\r
+}\r
+\r
+void store_host_key(const char *hostname, int port,\r
+                   const char *keytype, const char *key)\r
+{\r
+    FILE *rfp, *wfp;\r
+    char *newtext, *line;\r
+    int headerlen;\r
+    char *filename, *tmpfilename;\r
+\r
+    newtext = dupprintf("%s@%d:%s %s\n", keytype, port, hostname, key);\r
+    headerlen = 1 + strcspn(newtext, " ");   /* count the space too */\r
+\r
+    /*\r
+     * Open both the old file and a new file.\r
+     */\r
+    tmpfilename = make_filename(INDEX_HOSTKEYS_TMP, NULL);\r
+    wfp = fopen(tmpfilename, "w");\r
+    if (!wfp) {\r
+        char *dir;\r
+\r
+        dir = make_filename(INDEX_DIR, NULL);\r
+        mkdir(dir, 0700);\r
+       sfree(dir);\r
+\r
+        wfp = fopen(tmpfilename, "w");\r
+    }\r
+    if (!wfp) {\r
+       sfree(tmpfilename);\r
+       return;\r
+    }\r
+    filename = make_filename(INDEX_HOSTKEYS, NULL);\r
+    rfp = fopen(filename, "r");\r
+\r
+    /*\r
+     * Copy all lines from the old file to the new one that _don't_\r
+     * involve the same host key identifier as the one we're adding.\r
+     */\r
+    if (rfp) {\r
+        while ( (line = fgetline(rfp)) ) {\r
+            if (strncmp(line, newtext, headerlen))\r
+                fputs(line, wfp);\r
+        }\r
+        fclose(rfp);\r
+    }\r
+\r
+    /*\r
+     * Now add the new line at the end.\r
+     */\r
+    fputs(newtext, wfp);\r
+\r
+    fclose(wfp);\r
+\r
+    rename(tmpfilename, filename);\r
+\r
+    sfree(tmpfilename);\r
+    sfree(filename);\r
+    sfree(newtext);\r
+}\r
+\r
+void read_random_seed(noise_consumer_t consumer)\r
+{\r
+    int fd;\r
+    char *fname;\r
+\r
+    fname = make_filename(INDEX_RANDSEED, NULL);\r
+    fd = open(fname, O_RDONLY);\r
+    sfree(fname);\r
+    if (fd >= 0) {\r
+       char buf[512];\r
+       int ret;\r
+       while ( (ret = read(fd, buf, sizeof(buf))) > 0)\r
+           consumer(buf, ret);\r
+       close(fd);\r
+    }\r
+}\r
+\r
+void write_random_seed(void *data, int len)\r
+{\r
+    int fd;\r
+    char *fname;\r
+\r
+    fname = make_filename(INDEX_RANDSEED, NULL);\r
+    /*\r
+     * Don't truncate the random seed file if it already exists; if\r
+     * something goes wrong half way through writing it, it would\r
+     * be better to leave the old data there than to leave it empty.\r
+     */\r
+    fd = open(fname, O_CREAT | O_WRONLY, 0600);\r
+    if (fd < 0) {\r
+       char *dir;\r
+\r
+       dir = make_filename(INDEX_DIR, NULL);\r
+       mkdir(dir, 0700);\r
+       sfree(dir);\r
+\r
+       fd = open(fname, O_CREAT | O_WRONLY, 0600);\r
+    }\r
+\r
+    while (len > 0) {\r
+       int ret = write(fd, data, len);\r
+       if (ret <= 0) break;\r
+       len -= ret;\r
+       data = (char *)data + len;\r
+    }\r
+\r
+    close(fd);\r
+    sfree(fname);\r
+}\r
+\r
+void cleanup_all(void)\r
+{\r
+}\r
diff --git a/putty/UNIX/UXUCS.C b/putty/UNIX/UXUCS.C
new file mode 100644 (file)
index 0000000..d2d6522
--- /dev/null
@@ -0,0 +1,273 @@
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <locale.h>\r
+#include <limits.h>\r
+#include <wchar.h>\r
+\r
+#include <time.h>\r
+\r
+#include "putty.h"\r
+#include "charset.h"\r
+#include "terminal.h"\r
+#include "misc.h"\r
+\r
+/*\r
+ * Unix Unicode-handling routines.\r
+ */\r
+\r
+int is_dbcs_leadbyte(int codepage, char byte)\r
+{\r
+    return 0;                         /* we don't do DBCS */\r
+}\r
+\r
+int mb_to_wc(int codepage, int flags, char *mbstr, int mblen,\r
+            wchar_t *wcstr, int wclen)\r
+{\r
+    if (codepage == DEFAULT_CODEPAGE) {\r
+       int n = 0;\r
+       mbstate_t state;\r
+\r
+       memset(&state, 0, sizeof state);\r
+       setlocale(LC_CTYPE, "");\r
+\r
+       while (mblen > 0) {\r
+           size_t i = mbrtowc(wcstr+n, mbstr, (size_t)mblen, &state);\r
+           if (i == (size_t)-1 || i == (size_t)-2)\r
+               break;\r
+           n++;\r
+           mbstr += i;\r
+           mblen -= i;\r
+       }\r
+\r
+       setlocale(LC_CTYPE, "C");\r
+\r
+       return n;\r
+    } else if (codepage == CS_NONE) {\r
+       int n = 0;\r
+\r
+       while (mblen > 0) {\r
+           wcstr[n] = 0xD800 | (mbstr[0] & 0xFF);\r
+           n++;\r
+           mbstr++;\r
+           mblen--;\r
+       }\r
+\r
+       return n;\r
+    } else\r
+       return charset_to_unicode(&mbstr, &mblen, wcstr, wclen, codepage,\r
+                                 NULL, NULL, 0);\r
+}\r
+\r
+int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen,\r
+            char *mbstr, int mblen, char *defchr, int *defused,\r
+            struct unicode_data *ucsdata)\r
+{\r
+    /* FIXME: we should remove the defused param completely... */\r
+    if (defused)\r
+       *defused = 0;\r
+\r
+    if (codepage == DEFAULT_CODEPAGE) {\r
+       char output[MB_LEN_MAX];\r
+       mbstate_t state;\r
+       int n = 0;\r
+\r
+       memset(&state, 0, sizeof state);\r
+       setlocale(LC_CTYPE, "");\r
+\r
+       while (wclen > 0) {\r
+           int i = wcrtomb(output, wcstr[0], &state);\r
+           if (i == (size_t)-1 || i > n - mblen)\r
+               break;\r
+           memcpy(mbstr+n, output, i);\r
+           n += i;\r
+           wcstr++;\r
+           wclen--;\r
+       }\r
+\r
+       setlocale(LC_CTYPE, "C");\r
+\r
+       return n;\r
+    } else if (codepage == CS_NONE) {\r
+       int n = 0;\r
+       while (wclen > 0 && n < mblen) {\r
+           if (*wcstr >= 0xD800 && *wcstr < 0xD900)\r
+               mbstr[n++] = (*wcstr & 0xFF);\r
+           else if (defchr)\r
+               mbstr[n++] = *defchr;\r
+           wcstr++;\r
+           wclen--;\r
+       }\r
+       return n;\r
+    } else {\r
+       return charset_from_unicode(&wcstr, &wclen, mbstr, mblen, codepage,\r
+                                   NULL, defchr?defchr:NULL, defchr?1:0);\r
+    }\r
+}\r
+\r
+/*\r
+ * Return value is TRUE if pterm is to run in direct-to-font mode.\r
+ */\r
+int init_ucs(struct unicode_data *ucsdata, char *linecharset,\r
+            int utf8_override, int font_charset, int vtmode)\r
+{\r
+    int i, ret = 0;\r
+\r
+    /*\r
+     * In the platform-independent parts of the code, font_codepage\r
+     * is used only for system DBCS support - which we don't\r
+     * support at all. So we set this to something which will never\r
+     * be used.\r
+     */\r
+    ucsdata->font_codepage = -1;\r
+\r
+    /*\r
+     * If utf8_override is set and the POSIX locale settings\r
+     * dictate a UTF-8 character set, then just go straight for\r
+     * UTF-8.\r
+     */\r
+    ucsdata->line_codepage = CS_NONE;\r
+    if (utf8_override) {\r
+       const char *s;\r
+       if (((s = getenv("LC_ALL"))   && *s) ||\r
+           ((s = getenv("LC_CTYPE")) && *s) ||\r
+           ((s = getenv("LANG"))     && *s)) {\r
+           if (strstr(s, "UTF-8"))\r
+               ucsdata->line_codepage = CS_UTF8;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Failing that, line_codepage should be decoded from the\r
+     * specification in cfg.\r
+     */\r
+    if (ucsdata->line_codepage == CS_NONE)\r
+       ucsdata->line_codepage = decode_codepage(linecharset);\r
+\r
+    /*\r
+     * If line_codepage is _still_ CS_NONE, we assume we're using\r
+     * the font's own encoding. This has been passed in to us, so\r
+     * we use that. If it's still CS_NONE after _that_ - i.e. the\r
+     * font we were given had an incomprehensible charset - then we\r
+     * fall back to using the D800 page.\r
+     */\r
+    if (ucsdata->line_codepage == CS_NONE)\r
+       ucsdata->line_codepage = font_charset;\r
+\r
+    if (ucsdata->line_codepage == CS_NONE)\r
+       ret = 1;\r
+\r
+    /*\r
+     * Set up unitab_line, by translating each individual character\r
+     * in the line codepage into Unicode.\r
+     */\r
+    for (i = 0; i < 256; i++) {\r
+       char c[1], *p;\r
+       wchar_t wc[1];\r
+       int len;\r
+       c[0] = i;\r
+       p = c;\r
+       len = 1;\r
+       if (ucsdata->line_codepage == CS_NONE)\r
+           ucsdata->unitab_line[i] = 0xD800 | i;\r
+       else if (1 == charset_to_unicode(&p, &len, wc, 1,\r
+                                        ucsdata->line_codepage,\r
+                                        NULL, L"", 0))\r
+           ucsdata->unitab_line[i] = wc[0];\r
+       else\r
+           ucsdata->unitab_line[i] = 0xFFFD;\r
+    }\r
+\r
+    /*\r
+     * Set up unitab_xterm. This is the same as unitab_line except\r
+     * in the line-drawing regions, where it follows the Unicode\r
+     * encoding.\r
+     * \r
+     * (Note that the strange X encoding of line-drawing characters\r
+     * in the bottom 32 glyphs of ISO8859-1 fonts is taken care of\r
+     * by the font encoding, which will spot such a font and act as\r
+     * if it were in a variant encoding of ISO8859-1.)\r
+     */\r
+    for (i = 0; i < 256; i++) {\r
+       static const wchar_t unitab_xterm_std[32] = {\r
+           0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,\r
+           0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,\r
+           0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,\r
+           0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020\r
+       };\r
+       static const wchar_t unitab_xterm_poorman[32] =\r
+           L"*#****o~**+++++-----++++|****L. ";\r
+\r
+       const wchar_t *ptr;\r
+\r
+       if (vtmode == VT_POORMAN)\r
+           ptr = unitab_xterm_poorman;\r
+       else\r
+           ptr = unitab_xterm_std;\r
+\r
+       if (i >= 0x5F && i < 0x7F)\r
+           ucsdata->unitab_xterm[i] = ptr[i & 0x1F];\r
+       else\r
+           ucsdata->unitab_xterm[i] = ucsdata->unitab_line[i];\r
+    }\r
+\r
+    /*\r
+     * Set up unitab_scoacs. The SCO Alternate Character Set is\r
+     * simply CP437.\r
+     */\r
+    for (i = 0; i < 256; i++) {\r
+       char c[1], *p;\r
+       wchar_t wc[1];\r
+       int len;\r
+       c[0] = i;\r
+       p = c;\r
+       len = 1;\r
+       if (1 == charset_to_unicode(&p, &len, wc, 1, CS_CP437, NULL, L"", 0))\r
+           ucsdata->unitab_scoacs[i] = wc[0];\r
+       else\r
+           ucsdata->unitab_scoacs[i] = 0xFFFD;\r
+    }\r
+\r
+    /*\r
+     * Find the control characters in the line codepage. For\r
+     * direct-to-font mode using the D800 hack, we assume 00-1F and\r
+     * 7F are controls, but allow 80-9F through. (It's as good a\r
+     * guess as anything; and my bet is that half the weird fonts\r
+     * used in this way will be IBM or MS code pages anyway.)\r
+     */\r
+    for (i = 0; i < 256; i++) {\r
+       int lineval = ucsdata->unitab_line[i];\r
+       if (lineval < ' ' || (lineval >= 0x7F && lineval < 0xA0) ||\r
+           (lineval >= 0xD800 && lineval < 0xD820) || (lineval == 0xD87F))\r
+           ucsdata->unitab_ctrl[i] = i;\r
+       else\r
+           ucsdata->unitab_ctrl[i] = 0xFF;\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+const char *cp_name(int codepage)\r
+{\r
+    if (codepage == CS_NONE)\r
+       return "Use font encoding";\r
+    return charset_to_localenc(codepage);\r
+}\r
+\r
+const char *cp_enumerate(int index)\r
+{\r
+    int charset;\r
+    if (index == 0)\r
+       return "Use font encoding";\r
+    charset = charset_localenc_nth(index-1);\r
+    if (charset == CS_NONE)\r
+       return NULL;\r
+    return charset_to_localenc(charset);\r
+}\r
+\r
+int decode_codepage(char *cp_name)\r
+{\r
+    if (!*cp_name)\r
+       return CS_NONE;                /* use font encoding */\r
+    return charset_from_localenc(cp_name);\r
+}\r
diff --git a/putty/UNIX/UX_X11.C b/putty/UNIX/UX_X11.C
new file mode 100644 (file)
index 0000000..7dd17df
--- /dev/null
@@ -0,0 +1,40 @@
+/*\r
+ * ux_x11.c: fetch local auth data for X forwarding.\r
+ */\r
+\r
+#include <ctype.h>\r
+#include <unistd.h>\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <errno.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "network.h"\r
+\r
+void platform_get_x11_auth(struct X11Display *disp, const Config *cfg)\r
+{\r
+    char *xauthfile;\r
+    int needs_free;\r
+\r
+    /*\r
+     * Find the .Xauthority file.\r
+     */\r
+    needs_free = FALSE;\r
+    xauthfile = getenv("XAUTHORITY");\r
+    if (!xauthfile) {\r
+       xauthfile = getenv("HOME");\r
+       if (xauthfile) {\r
+           xauthfile = dupcat(xauthfile, "/.Xauthority", NULL);\r
+           needs_free = TRUE;\r
+       }\r
+    }\r
+\r
+    if (xauthfile) {\r
+       x11_get_auth_from_authfile(disp, xauthfile);\r
+       if (needs_free)\r
+           sfree(xauthfile);\r
+    }\r
+}\r
+\r
+const int platform_uses_x11_unix_by_default = TRUE;\r
diff --git a/putty/UNIX/XKEYSYM.C b/putty/UNIX/XKEYSYM.C
new file mode 100644 (file)
index 0000000..cdad7de
--- /dev/null
@@ -0,0 +1,1011 @@
+/*\r
+ * xkeysym.c: mapping from X keysyms to Unicode values\r
+ * \r
+ * The basic idea of this is shamelessly cribbed from xterm. The\r
+ * actual character data is generated from Markus Kuhn's proposed\r
+ * redraft of the X11 keysym mapping table, using the following\r
+ * piece of Perl/sh code:\r
+\r
+wget -q -O - http://www.cl.cam.ac.uk/~mgk25/ucs/X11.keysyms | \\r
+perl -ne '/^(\d+)\s+(\d+)\s+[\d\/]+\s+U\+([\dA-Fa-f]+)/ and' \\r
+      -e '  do { $a{$1 * 256+ $2} = hex $3; };' \\r
+      -e 'END { foreach $i (sort {$a <=> $b} keys %a) {' \\r
+      -e '  printf "    {0x%x, 0x%x},\n", $i, $a{$i} } }' \\r
+      -e 'BEGIN { $a{0x13a4} = 0x20ac }'\r
+\r
+ * (The BEGIN clause inserts a mapping for the Euro sign which for\r
+ * some reason isn't in the list but xterm supports. *shrug*.)\r
+ */\r
+\r
+#include "misc.h"\r
+\r
+struct keysym {\r
+    /*\r
+     * Currently nothing in here is above 0xFFFF, so I'll use\r
+     * `unsigned short' to save space.\r
+     */\r
+    unsigned short keysym;\r
+    unsigned short unicode;\r
+};\r
+\r
+static struct keysym keysyms[] = {\r
+    {0x20, 0x20},\r
+    {0x21, 0x21},\r
+    {0x22, 0x22},\r
+    {0x23, 0x23},\r
+    {0x24, 0x24},\r
+    {0x25, 0x25},\r
+    {0x26, 0x26},\r
+    {0x27, 0x27},\r
+    {0x28, 0x28},\r
+    {0x29, 0x29},\r
+    {0x2a, 0x2a},\r
+    {0x2b, 0x2b},\r
+    {0x2c, 0x2c},\r
+    {0x2d, 0x2d},\r
+    {0x2e, 0x2e},\r
+    {0x2f, 0x2f},\r
+    {0x30, 0x30},\r
+    {0x31, 0x31},\r
+    {0x32, 0x32},\r
+    {0x33, 0x33},\r
+    {0x34, 0x34},\r
+    {0x35, 0x35},\r
+    {0x36, 0x36},\r
+    {0x37, 0x37},\r
+    {0x38, 0x38},\r
+    {0x39, 0x39},\r
+    {0x3a, 0x3a},\r
+    {0x3b, 0x3b},\r
+    {0x3c, 0x3c},\r
+    {0x3d, 0x3d},\r
+    {0x3e, 0x3e},\r
+    {0x3f, 0x3f},\r
+    {0x40, 0x40},\r
+    {0x41, 0x41},\r
+    {0x42, 0x42},\r
+    {0x43, 0x43},\r
+    {0x44, 0x44},\r
+    {0x45, 0x45},\r
+    {0x46, 0x46},\r
+    {0x47, 0x47},\r
+    {0x48, 0x48},\r
+    {0x49, 0x49},\r
+    {0x4a, 0x4a},\r
+    {0x4b, 0x4b},\r
+    {0x4c, 0x4c},\r
+    {0x4d, 0x4d},\r
+    {0x4e, 0x4e},\r
+    {0x4f, 0x4f},\r
+    {0x50, 0x50},\r
+    {0x51, 0x51},\r
+    {0x52, 0x52},\r
+    {0x53, 0x53},\r
+    {0x54, 0x54},\r
+    {0x55, 0x55},\r
+    {0x56, 0x56},\r
+    {0x57, 0x57},\r
+    {0x58, 0x58},\r
+    {0x59, 0x59},\r
+    {0x5a, 0x5a},\r
+    {0x5b, 0x5b},\r
+    {0x5c, 0x5c},\r
+    {0x5d, 0x5d},\r
+    {0x5e, 0x5e},\r
+    {0x5f, 0x5f},\r
+    {0x60, 0x60},\r
+    {0x61, 0x61},\r
+    {0x62, 0x62},\r
+    {0x63, 0x63},\r
+    {0x64, 0x64},\r
+    {0x65, 0x65},\r
+    {0x66, 0x66},\r
+    {0x67, 0x67},\r
+    {0x68, 0x68},\r
+    {0x69, 0x69},\r
+    {0x6a, 0x6a},\r
+    {0x6b, 0x6b},\r
+    {0x6c, 0x6c},\r
+    {0x6d, 0x6d},\r
+    {0x6e, 0x6e},\r
+    {0x6f, 0x6f},\r
+    {0x70, 0x70},\r
+    {0x71, 0x71},\r
+    {0x72, 0x72},\r
+    {0x73, 0x73},\r
+    {0x74, 0x74},\r
+    {0x75, 0x75},\r
+    {0x76, 0x76},\r
+    {0x77, 0x77},\r
+    {0x78, 0x78},\r
+    {0x79, 0x79},\r
+    {0x7a, 0x7a},\r
+    {0x7b, 0x7b},\r
+    {0x7c, 0x7c},\r
+    {0x7d, 0x7d},\r
+    {0x7e, 0x7e},\r
+    {0xa0, 0xa0},\r
+    {0xa1, 0xa1},\r
+    {0xa2, 0xa2},\r
+    {0xa3, 0xa3},\r
+    {0xa4, 0xa4},\r
+    {0xa5, 0xa5},\r
+    {0xa6, 0xa6},\r
+    {0xa7, 0xa7},\r
+    {0xa8, 0xa8},\r
+    {0xa9, 0xa9},\r
+    {0xaa, 0xaa},\r
+    {0xab, 0xab},\r
+    {0xac, 0xac},\r
+    {0xad, 0xad},\r
+    {0xae, 0xae},\r
+    {0xaf, 0xaf},\r
+    {0xb0, 0xb0},\r
+    {0xb1, 0xb1},\r
+    {0xb2, 0xb2},\r
+    {0xb3, 0xb3},\r
+    {0xb4, 0xb4},\r
+    {0xb5, 0xb5},\r
+    {0xb6, 0xb6},\r
+    {0xb7, 0xb7},\r
+    {0xb8, 0xb8},\r
+    {0xb9, 0xb9},\r
+    {0xba, 0xba},\r
+    {0xbb, 0xbb},\r
+    {0xbc, 0xbc},\r
+    {0xbd, 0xbd},\r
+    {0xbe, 0xbe},\r
+    {0xbf, 0xbf},\r
+    {0xc0, 0xc0},\r
+    {0xc1, 0xc1},\r
+    {0xc2, 0xc2},\r
+    {0xc3, 0xc3},\r
+    {0xc4, 0xc4},\r
+    {0xc5, 0xc5},\r
+    {0xc6, 0xc6},\r
+    {0xc7, 0xc7},\r
+    {0xc8, 0xc8},\r
+    {0xc9, 0xc9},\r
+    {0xca, 0xca},\r
+    {0xcb, 0xcb},\r
+    {0xcc, 0xcc},\r
+    {0xcd, 0xcd},\r
+    {0xce, 0xce},\r
+    {0xcf, 0xcf},\r
+    {0xd0, 0xd0},\r
+    {0xd1, 0xd1},\r
+    {0xd2, 0xd2},\r
+    {0xd3, 0xd3},\r
+    {0xd4, 0xd4},\r
+    {0xd5, 0xd5},\r
+    {0xd6, 0xd6},\r
+    {0xd7, 0xd7},\r
+    {0xd8, 0xd8},\r
+    {0xd9, 0xd9},\r
+    {0xda, 0xda},\r
+    {0xdb, 0xdb},\r
+    {0xdc, 0xdc},\r
+    {0xdd, 0xdd},\r
+    {0xde, 0xde},\r
+    {0xdf, 0xdf},\r
+    {0xe0, 0xe0},\r
+    {0xe1, 0xe1},\r
+    {0xe2, 0xe2},\r
+    {0xe3, 0xe3},\r
+    {0xe4, 0xe4},\r
+    {0xe5, 0xe5},\r
+    {0xe6, 0xe6},\r
+    {0xe7, 0xe7},\r
+    {0xe8, 0xe8},\r
+    {0xe9, 0xe9},\r
+    {0xea, 0xea},\r
+    {0xeb, 0xeb},\r
+    {0xec, 0xec},\r
+    {0xed, 0xed},\r
+    {0xee, 0xee},\r
+    {0xef, 0xef},\r
+    {0xf0, 0xf0},\r
+    {0xf1, 0xf1},\r
+    {0xf2, 0xf2},\r
+    {0xf3, 0xf3},\r
+    {0xf4, 0xf4},\r
+    {0xf5, 0xf5},\r
+    {0xf6, 0xf6},\r
+    {0xf7, 0xf7},\r
+    {0xf8, 0xf8},\r
+    {0xf9, 0xf9},\r
+    {0xfa, 0xfa},\r
+    {0xfb, 0xfb},\r
+    {0xfc, 0xfc},\r
+    {0xfd, 0xfd},\r
+    {0xfe, 0xfe},\r
+    {0xff, 0xff},\r
+    {0x1a1, 0x104},\r
+    {0x1a2, 0x2d8},\r
+    {0x1a3, 0x141},\r
+    {0x1a5, 0x13d},\r
+    {0x1a6, 0x15a},\r
+    {0x1a9, 0x160},\r
+    {0x1aa, 0x15e},\r
+    {0x1ab, 0x164},\r
+    {0x1ac, 0x179},\r
+    {0x1ae, 0x17d},\r
+    {0x1af, 0x17b},\r
+    {0x1b1, 0x105},\r
+    {0x1b2, 0x2db},\r
+    {0x1b3, 0x142},\r
+    {0x1b5, 0x13e},\r
+    {0x1b6, 0x15b},\r
+    {0x1b7, 0x2c7},\r
+    {0x1b9, 0x161},\r
+    {0x1ba, 0x15f},\r
+    {0x1bb, 0x165},\r
+    {0x1bc, 0x17a},\r
+    {0x1bd, 0x2dd},\r
+    {0x1be, 0x17e},\r
+    {0x1bf, 0x17c},\r
+    {0x1c0, 0x154},\r
+    {0x1c3, 0x102},\r
+    {0x1c5, 0x139},\r
+    {0x1c6, 0x106},\r
+    {0x1c8, 0x10c},\r
+    {0x1ca, 0x118},\r
+    {0x1cc, 0x11a},\r
+    {0x1cf, 0x10e},\r
+    {0x1d0, 0x110},\r
+    {0x1d1, 0x143},\r
+    {0x1d2, 0x147},\r
+    {0x1d5, 0x150},\r
+    {0x1d8, 0x158},\r
+    {0x1d9, 0x16e},\r
+    {0x1db, 0x170},\r
+    {0x1de, 0x162},\r
+    {0x1e0, 0x155},\r
+    {0x1e3, 0x103},\r
+    {0x1e5, 0x13a},\r
+    {0x1e6, 0x107},\r
+    {0x1e8, 0x10d},\r
+    {0x1ea, 0x119},\r
+    {0x1ec, 0x11b},\r
+    {0x1ef, 0x10f},\r
+    {0x1f0, 0x111},\r
+    {0x1f1, 0x144},\r
+    {0x1f2, 0x148},\r
+    {0x1f5, 0x151},\r
+    {0x1f8, 0x159},\r
+    {0x1f9, 0x16f},\r
+    {0x1fb, 0x171},\r
+    {0x1fe, 0x163},\r
+    {0x1ff, 0x2d9},\r
+    {0x2a1, 0x126},\r
+    {0x2a6, 0x124},\r
+    {0x2a9, 0x130},\r
+    {0x2ab, 0x11e},\r
+    {0x2ac, 0x134},\r
+    {0x2b1, 0x127},\r
+    {0x2b6, 0x125},\r
+    {0x2b9, 0x131},\r
+    {0x2bb, 0x11f},\r
+    {0x2bc, 0x135},\r
+    {0x2c5, 0x10a},\r
+    {0x2c6, 0x108},\r
+    {0x2d5, 0x120},\r
+    {0x2d8, 0x11c},\r
+    {0x2dd, 0x16c},\r
+    {0x2de, 0x15c},\r
+    {0x2e5, 0x10b},\r
+    {0x2e6, 0x109},\r
+    {0x2f5, 0x121},\r
+    {0x2f8, 0x11d},\r
+    {0x2fd, 0x16d},\r
+    {0x2fe, 0x15d},\r
+    {0x3a2, 0x138},\r
+    {0x3a3, 0x156},\r
+    {0x3a5, 0x128},\r
+    {0x3a6, 0x13b},\r
+    {0x3aa, 0x112},\r
+    {0x3ab, 0x122},\r
+    {0x3ac, 0x166},\r
+    {0x3b3, 0x157},\r
+    {0x3b5, 0x129},\r
+    {0x3b6, 0x13c},\r
+    {0x3ba, 0x113},\r
+    {0x3bb, 0x123},\r
+    {0x3bc, 0x167},\r
+    {0x3bd, 0x14a},\r
+    {0x3bf, 0x14b},\r
+    {0x3c0, 0x100},\r
+    {0x3c7, 0x12e},\r
+    {0x3cc, 0x116},\r
+    {0x3cf, 0x12a},\r
+    {0x3d1, 0x145},\r
+    {0x3d2, 0x14c},\r
+    {0x3d3, 0x136},\r
+    {0x3d9, 0x172},\r
+    {0x3dd, 0x168},\r
+    {0x3de, 0x16a},\r
+    {0x3e0, 0x101},\r
+    {0x3e7, 0x12f},\r
+    {0x3ec, 0x117},\r
+    {0x3ef, 0x12b},\r
+    {0x3f1, 0x146},\r
+    {0x3f2, 0x14d},\r
+    {0x3f3, 0x137},\r
+    {0x3f9, 0x173},\r
+    {0x3fd, 0x169},\r
+    {0x3fe, 0x16b},\r
+    {0x47e, 0x203e},\r
+    {0x4a1, 0x3002},\r
+    {0x4a2, 0x300c},\r
+    {0x4a3, 0x300d},\r
+    {0x4a4, 0x3001},\r
+    {0x4a5, 0x30fb},\r
+    {0x4a6, 0x30f2},\r
+    {0x4a7, 0x30a1},\r
+    {0x4a8, 0x30a3},\r
+    {0x4a9, 0x30a5},\r
+    {0x4aa, 0x30a7},\r
+    {0x4ab, 0x30a9},\r
+    {0x4ac, 0x30e3},\r
+    {0x4ad, 0x30e5},\r
+    {0x4ae, 0x30e7},\r
+    {0x4af, 0x30c3},\r
+    {0x4b0, 0x30fc},\r
+    {0x4b1, 0x30a2},\r
+    {0x4b2, 0x30a4},\r
+    {0x4b3, 0x30a6},\r
+    {0x4b4, 0x30a8},\r
+    {0x4b5, 0x30aa},\r
+    {0x4b6, 0x30ab},\r
+    {0x4b7, 0x30ad},\r
+    {0x4b8, 0x30af},\r
+    {0x4b9, 0x30b1},\r
+    {0x4ba, 0x30b3},\r
+    {0x4bb, 0x30b5},\r
+    {0x4bc, 0x30b7},\r
+    {0x4bd, 0x30b9},\r
+    {0x4be, 0x30bb},\r
+    {0x4bf, 0x30bd},\r
+    {0x4c0, 0x30bf},\r
+    {0x4c1, 0x30c1},\r
+    {0x4c2, 0x30c4},\r
+    {0x4c3, 0x30c6},\r
+    {0x4c4, 0x30c8},\r
+    {0x4c5, 0x30ca},\r
+    {0x4c6, 0x30cb},\r
+    {0x4c7, 0x30cc},\r
+    {0x4c8, 0x30cd},\r
+    {0x4c9, 0x30ce},\r
+    {0x4ca, 0x30cf},\r
+    {0x4cb, 0x30d2},\r
+    {0x4cc, 0x30d5},\r
+    {0x4cd, 0x30d8},\r
+    {0x4ce, 0x30db},\r
+    {0x4cf, 0x30de},\r
+    {0x4d0, 0x30df},\r
+    {0x4d1, 0x30e0},\r
+    {0x4d2, 0x30e1},\r
+    {0x4d3, 0x30e2},\r
+    {0x4d4, 0x30e4},\r
+    {0x4d5, 0x30e6},\r
+    {0x4d6, 0x30e8},\r
+    {0x4d7, 0x30e9},\r
+    {0x4d8, 0x30ea},\r
+    {0x4d9, 0x30eb},\r
+    {0x4da, 0x30ec},\r
+    {0x4db, 0x30ed},\r
+    {0x4dc, 0x30ef},\r
+    {0x4dd, 0x30f3},\r
+    {0x4de, 0x309b},\r
+    {0x4df, 0x309c},\r
+    {0x5ac, 0x60c},\r
+    {0x5bb, 0x61b},\r
+    {0x5bf, 0x61f},\r
+    {0x5c1, 0x621},\r
+    {0x5c2, 0x622},\r
+    {0x5c3, 0x623},\r
+    {0x5c4, 0x624},\r
+    {0x5c5, 0x625},\r
+    {0x5c6, 0x626},\r
+    {0x5c7, 0x627},\r
+    {0x5c8, 0x628},\r
+    {0x5c9, 0x629},\r
+    {0x5ca, 0x62a},\r
+    {0x5cb, 0x62b},\r
+    {0x5cc, 0x62c},\r
+    {0x5cd, 0x62d},\r
+    {0x5ce, 0x62e},\r
+    {0x5cf, 0x62f},\r
+    {0x5d0, 0x630},\r
+    {0x5d1, 0x631},\r
+    {0x5d2, 0x632},\r
+    {0x5d3, 0x633},\r
+    {0x5d4, 0x634},\r
+    {0x5d5, 0x635},\r
+    {0x5d6, 0x636},\r
+    {0x5d7, 0x637},\r
+    {0x5d8, 0x638},\r
+    {0x5d9, 0x639},\r
+    {0x5da, 0x63a},\r
+    {0x5e0, 0x640},\r
+    {0x5e1, 0x641},\r
+    {0x5e2, 0x642},\r
+    {0x5e3, 0x643},\r
+    {0x5e4, 0x644},\r
+    {0x5e5, 0x645},\r
+    {0x5e6, 0x646},\r
+    {0x5e7, 0x647},\r
+    {0x5e8, 0x648},\r
+    {0x5e9, 0x649},\r
+    {0x5ea, 0x64a},\r
+    {0x5eb, 0x64b},\r
+    {0x5ec, 0x64c},\r
+    {0x5ed, 0x64d},\r
+    {0x5ee, 0x64e},\r
+    {0x5ef, 0x64f},\r
+    {0x5f0, 0x650},\r
+    {0x5f1, 0x651},\r
+    {0x5f2, 0x652},\r
+    {0x6a1, 0x452},\r
+    {0x6a2, 0x453},\r
+    {0x6a3, 0x451},\r
+    {0x6a4, 0x454},\r
+    {0x6a5, 0x455},\r
+    {0x6a6, 0x456},\r
+    {0x6a7, 0x457},\r
+    {0x6a8, 0x458},\r
+    {0x6a9, 0x459},\r
+    {0x6aa, 0x45a},\r
+    {0x6ab, 0x45b},\r
+    {0x6ac, 0x45c},\r
+    {0x6ae, 0x45e},\r
+    {0x6af, 0x45f},\r
+    {0x6b0, 0x2116},\r
+    {0x6b1, 0x402},\r
+    {0x6b2, 0x403},\r
+    {0x6b3, 0x401},\r
+    {0x6b4, 0x404},\r
+    {0x6b5, 0x405},\r
+    {0x6b6, 0x406},\r
+    {0x6b7, 0x407},\r
+    {0x6b8, 0x408},\r
+    {0x6b9, 0x409},\r
+    {0x6ba, 0x40a},\r
+    {0x6bb, 0x40b},\r
+    {0x6bc, 0x40c},\r
+    {0x6be, 0x40e},\r
+    {0x6bf, 0x40f},\r
+    {0x6c0, 0x44e},\r
+    {0x6c1, 0x430},\r
+    {0x6c2, 0x431},\r
+    {0x6c3, 0x446},\r
+    {0x6c4, 0x434},\r
+    {0x6c5, 0x435},\r
+    {0x6c6, 0x444},\r
+    {0x6c7, 0x433},\r
+    {0x6c8, 0x445},\r
+    {0x6c9, 0x438},\r
+    {0x6ca, 0x439},\r
+    {0x6cb, 0x43a},\r
+    {0x6cc, 0x43b},\r
+    {0x6cd, 0x43c},\r
+    {0x6ce, 0x43d},\r
+    {0x6cf, 0x43e},\r
+    {0x6d0, 0x43f},\r
+    {0x6d1, 0x44f},\r
+    {0x6d2, 0x440},\r
+    {0x6d3, 0x441},\r
+    {0x6d4, 0x442},\r
+    {0x6d5, 0x443},\r
+    {0x6d6, 0x436},\r
+    {0x6d7, 0x432},\r
+    {0x6d8, 0x44c},\r
+    {0x6d9, 0x44b},\r
+    {0x6da, 0x437},\r
+    {0x6db, 0x448},\r
+    {0x6dc, 0x44d},\r
+    {0x6dd, 0x449},\r
+    {0x6de, 0x447},\r
+    {0x6df, 0x44a},\r
+    {0x6e0, 0x42e},\r
+    {0x6e1, 0x410},\r
+    {0x6e2, 0x411},\r
+    {0x6e3, 0x426},\r
+    {0x6e4, 0x414},\r
+    {0x6e5, 0x415},\r
+    {0x6e6, 0x424},\r
+    {0x6e7, 0x413},\r
+    {0x6e8, 0x425},\r
+    {0x6e9, 0x418},\r
+    {0x6ea, 0x419},\r
+    {0x6eb, 0x41a},\r
+    {0x6ec, 0x41b},\r
+    {0x6ed, 0x41c},\r
+    {0x6ee, 0x41d},\r
+    {0x6ef, 0x41e},\r
+    {0x6f0, 0x41f},\r
+    {0x6f1, 0x42f},\r
+    {0x6f2, 0x420},\r
+    {0x6f3, 0x421},\r
+    {0x6f4, 0x422},\r
+    {0x6f5, 0x423},\r
+    {0x6f6, 0x416},\r
+    {0x6f7, 0x412},\r
+    {0x6f8, 0x42c},\r
+    {0x6f9, 0x42b},\r
+    {0x6fa, 0x417},\r
+    {0x6fb, 0x428},\r
+    {0x6fc, 0x42d},\r
+    {0x6fd, 0x429},\r
+    {0x6fe, 0x427},\r
+    {0x6ff, 0x42a},\r
+    {0x7a1, 0x386},\r
+    {0x7a2, 0x388},\r
+    {0x7a3, 0x389},\r
+    {0x7a4, 0x38a},\r
+    {0x7a5, 0x3aa},\r
+    {0x7a7, 0x38c},\r
+    {0x7a8, 0x38e},\r
+    {0x7a9, 0x3ab},\r
+    {0x7ab, 0x38f},\r
+    {0x7ae, 0x385},\r
+    {0x7af, 0x2015},\r
+    {0x7b1, 0x3ac},\r
+    {0x7b2, 0x3ad},\r
+    {0x7b3, 0x3ae},\r
+    {0x7b4, 0x3af},\r
+    {0x7b5, 0x3ca},\r
+    {0x7b6, 0x390},\r
+    {0x7b7, 0x3cc},\r
+    {0x7b8, 0x3cd},\r
+    {0x7b9, 0x3cb},\r
+    {0x7ba, 0x3b0},\r
+    {0x7bb, 0x3ce},\r
+    {0x7c1, 0x391},\r
+    {0x7c2, 0x392},\r
+    {0x7c3, 0x393},\r
+    {0x7c4, 0x394},\r
+    {0x7c5, 0x395},\r
+    {0x7c6, 0x396},\r
+    {0x7c7, 0x397},\r
+    {0x7c8, 0x398},\r
+    {0x7c9, 0x399},\r
+    {0x7ca, 0x39a},\r
+    {0x7cb, 0x39b},\r
+    {0x7cc, 0x39c},\r
+    {0x7cd, 0x39d},\r
+    {0x7ce, 0x39e},\r
+    {0x7cf, 0x39f},\r
+    {0x7d0, 0x3a0},\r
+    {0x7d1, 0x3a1},\r
+    {0x7d2, 0x3a3},\r
+    {0x7d4, 0x3a4},\r
+    {0x7d5, 0x3a5},\r
+    {0x7d6, 0x3a6},\r
+    {0x7d7, 0x3a7},\r
+    {0x7d8, 0x3a8},\r
+    {0x7d9, 0x3a9},\r
+    {0x7e1, 0x3b1},\r
+    {0x7e2, 0x3b2},\r
+    {0x7e3, 0x3b3},\r
+    {0x7e4, 0x3b4},\r
+    {0x7e5, 0x3b5},\r
+    {0x7e6, 0x3b6},\r
+    {0x7e7, 0x3b7},\r
+    {0x7e8, 0x3b8},\r
+    {0x7e9, 0x3b9},\r
+    {0x7ea, 0x3ba},\r
+    {0x7eb, 0x3bb},\r
+    {0x7ec, 0x3bc},\r
+    {0x7ed, 0x3bd},\r
+    {0x7ee, 0x3be},\r
+    {0x7ef, 0x3bf},\r
+    {0x7f0, 0x3c0},\r
+    {0x7f1, 0x3c1},\r
+    {0x7f2, 0x3c3},\r
+    {0x7f3, 0x3c2},\r
+    {0x7f4, 0x3c4},\r
+    {0x7f5, 0x3c5},\r
+    {0x7f6, 0x3c6},\r
+    {0x7f7, 0x3c7},\r
+    {0x7f8, 0x3c8},\r
+    {0x7f9, 0x3c9},\r
+    {0x8a1, 0x23b7},\r
+    {0x8a2, 0x250c},\r
+    {0x8a3, 0x2500},\r
+    {0x8a4, 0x2320},\r
+    {0x8a5, 0x2321},\r
+    {0x8a6, 0x2502},\r
+    {0x8a7, 0x23a1},\r
+    {0x8a8, 0x23a3},\r
+    {0x8a9, 0x23a4},\r
+    {0x8aa, 0x23a6},\r
+    {0x8ab, 0x239b},\r
+    {0x8ac, 0x239d},\r
+    {0x8ad, 0x239e},\r
+    {0x8ae, 0x23a0},\r
+    {0x8af, 0x23a8},\r
+    {0x8b0, 0x23ac},\r
+    {0x8bc, 0x2264},\r
+    {0x8bd, 0x2260},\r
+    {0x8be, 0x2265},\r
+    {0x8bf, 0x222b},\r
+    {0x8c0, 0x2234},\r
+    {0x8c1, 0x221d},\r
+    {0x8c2, 0x221e},\r
+    {0x8c5, 0x2207},\r
+    {0x8c8, 0x223c},\r
+    {0x8c9, 0x2243},\r
+    {0x8cd, 0x21d4},\r
+    {0x8ce, 0x21d2},\r
+    {0x8cf, 0x2261},\r
+    {0x8d6, 0x221a},\r
+    {0x8da, 0x2282},\r
+    {0x8db, 0x2283},\r
+    {0x8dc, 0x2229},\r
+    {0x8dd, 0x222a},\r
+    {0x8de, 0x2227},\r
+    {0x8df, 0x2228},\r
+    {0x8ef, 0x2202},\r
+    {0x8f6, 0x192},\r
+    {0x8fb, 0x2190},\r
+    {0x8fc, 0x2191},\r
+    {0x8fd, 0x2192},\r
+    {0x8fe, 0x2193},\r
+    {0x9e0, 0x25c6},\r
+    {0x9e1, 0x2592},\r
+    {0x9e2, 0x2409},\r
+    {0x9e3, 0x240c},\r
+    {0x9e4, 0x240d},\r
+    {0x9e5, 0x240a},\r
+    {0x9e8, 0x2424},\r
+    {0x9e9, 0x240b},\r
+    {0x9ea, 0x2518},\r
+    {0x9eb, 0x2510},\r
+    {0x9ec, 0x250c},\r
+    {0x9ed, 0x2514},\r
+    {0x9ee, 0x253c},\r
+    {0x9ef, 0x23ba},\r
+    {0x9f0, 0x23bb},\r
+    {0x9f1, 0x2500},\r
+    {0x9f2, 0x23bc},\r
+    {0x9f3, 0x23bd},\r
+    {0x9f4, 0x251c},\r
+    {0x9f5, 0x2524},\r
+    {0x9f6, 0x2534},\r
+    {0x9f7, 0x252c},\r
+    {0x9f8, 0x2502},\r
+    {0xaa1, 0x2003},\r
+    {0xaa2, 0x2002},\r
+    {0xaa3, 0x2004},\r
+    {0xaa4, 0x2005},\r
+    {0xaa5, 0x2007},\r
+    {0xaa6, 0x2008},\r
+    {0xaa7, 0x2009},\r
+    {0xaa8, 0x200a},\r
+    {0xaa9, 0x2014},\r
+    {0xaaa, 0x2013},\r
+    {0xaae, 0x2026},\r
+    {0xaaf, 0x2025},\r
+    {0xab0, 0x2153},\r
+    {0xab1, 0x2154},\r
+    {0xab2, 0x2155},\r
+    {0xab3, 0x2156},\r
+    {0xab4, 0x2157},\r
+    {0xab5, 0x2158},\r
+    {0xab6, 0x2159},\r
+    {0xab7, 0x215a},\r
+    {0xab8, 0x2105},\r
+    {0xabb, 0x2012},\r
+    {0xabc, 0x2329},\r
+    {0xabe, 0x232a},\r
+    {0xac3, 0x215b},\r
+    {0xac4, 0x215c},\r
+    {0xac5, 0x215d},\r
+    {0xac6, 0x215e},\r
+    {0xac9, 0x2122},\r
+    {0xaca, 0x2613},\r
+    {0xacc, 0x25c1},\r
+    {0xacd, 0x25b7},\r
+    {0xace, 0x25cb},\r
+    {0xacf, 0x25af},\r
+    {0xad0, 0x2018},\r
+    {0xad1, 0x2019},\r
+    {0xad2, 0x201c},\r
+    {0xad3, 0x201d},\r
+    {0xad4, 0x211e},\r
+    {0xad6, 0x2032},\r
+    {0xad7, 0x2033},\r
+    {0xad9, 0x271d},\r
+    {0xadb, 0x25ac},\r
+    {0xadc, 0x25c0},\r
+    {0xadd, 0x25b6},\r
+    {0xade, 0x25cf},\r
+    {0xadf, 0x25ae},\r
+    {0xae0, 0x25e6},\r
+    {0xae1, 0x25ab},\r
+    {0xae2, 0x25ad},\r
+    {0xae3, 0x25b3},\r
+    {0xae4, 0x25bd},\r
+    {0xae5, 0x2606},\r
+    {0xae6, 0x2022},\r
+    {0xae7, 0x25aa},\r
+    {0xae8, 0x25b2},\r
+    {0xae9, 0x25bc},\r
+    {0xaea, 0x261c},\r
+    {0xaeb, 0x261e},\r
+    {0xaec, 0x2663},\r
+    {0xaed, 0x2666},\r
+    {0xaee, 0x2665},\r
+    {0xaf0, 0x2720},\r
+    {0xaf1, 0x2020},\r
+    {0xaf2, 0x2021},\r
+    {0xaf3, 0x2713},\r
+    {0xaf4, 0x2717},\r
+    {0xaf5, 0x266f},\r
+    {0xaf6, 0x266d},\r
+    {0xaf7, 0x2642},\r
+    {0xaf8, 0x2640},\r
+    {0xaf9, 0x260e},\r
+    {0xafa, 0x2315},\r
+    {0xafb, 0x2117},\r
+    {0xafc, 0x2038},\r
+    {0xafd, 0x201a},\r
+    {0xafe, 0x201e},\r
+    {0xba3, 0x3c},\r
+    {0xba6, 0x3e},\r
+    {0xba8, 0x2228},\r
+    {0xba9, 0x2227},\r
+    {0xbc0, 0xaf},\r
+    {0xbc2, 0x22a5},\r
+    {0xbc3, 0x2229},\r
+    {0xbc4, 0x230a},\r
+    {0xbc6, 0x5f},\r
+    {0xbca, 0x2218},\r
+    {0xbcc, 0x2395},\r
+    {0xbce, 0x22a4},\r
+    {0xbcf, 0x25cb},\r
+    {0xbd3, 0x2308},\r
+    {0xbd6, 0x222a},\r
+    {0xbd8, 0x2283},\r
+    {0xbda, 0x2282},\r
+    {0xbdc, 0x22a2},\r
+    {0xbfc, 0x22a3},\r
+    {0xcdf, 0x2017},\r
+    {0xce0, 0x5d0},\r
+    {0xce1, 0x5d1},\r
+    {0xce2, 0x5d2},\r
+    {0xce3, 0x5d3},\r
+    {0xce4, 0x5d4},\r
+    {0xce5, 0x5d5},\r
+    {0xce6, 0x5d6},\r
+    {0xce7, 0x5d7},\r
+    {0xce8, 0x5d8},\r
+    {0xce9, 0x5d9},\r
+    {0xcea, 0x5da},\r
+    {0xceb, 0x5db},\r
+    {0xcec, 0x5dc},\r
+    {0xced, 0x5dd},\r
+    {0xcee, 0x5de},\r
+    {0xcef, 0x5df},\r
+    {0xcf0, 0x5e0},\r
+    {0xcf1, 0x5e1},\r
+    {0xcf2, 0x5e2},\r
+    {0xcf3, 0x5e3},\r
+    {0xcf4, 0x5e4},\r
+    {0xcf5, 0x5e5},\r
+    {0xcf6, 0x5e6},\r
+    {0xcf7, 0x5e7},\r
+    {0xcf8, 0x5e8},\r
+    {0xcf9, 0x5e9},\r
+    {0xcfa, 0x5ea},\r
+    {0xda1, 0xe01},\r
+    {0xda2, 0xe02},\r
+    {0xda3, 0xe03},\r
+    {0xda4, 0xe04},\r
+    {0xda5, 0xe05},\r
+    {0xda6, 0xe06},\r
+    {0xda7, 0xe07},\r
+    {0xda8, 0xe08},\r
+    {0xda9, 0xe09},\r
+    {0xdaa, 0xe0a},\r
+    {0xdab, 0xe0b},\r
+    {0xdac, 0xe0c},\r
+    {0xdad, 0xe0d},\r
+    {0xdae, 0xe0e},\r
+    {0xdaf, 0xe0f},\r
+    {0xdb0, 0xe10},\r
+    {0xdb1, 0xe11},\r
+    {0xdb2, 0xe12},\r
+    {0xdb3, 0xe13},\r
+    {0xdb4, 0xe14},\r
+    {0xdb5, 0xe15},\r
+    {0xdb6, 0xe16},\r
+    {0xdb7, 0xe17},\r
+    {0xdb8, 0xe18},\r
+    {0xdb9, 0xe19},\r
+    {0xdba, 0xe1a},\r
+    {0xdbb, 0xe1b},\r
+    {0xdbc, 0xe1c},\r
+    {0xdbd, 0xe1d},\r
+    {0xdbe, 0xe1e},\r
+    {0xdbf, 0xe1f},\r
+    {0xdc0, 0xe20},\r
+    {0xdc1, 0xe21},\r
+    {0xdc2, 0xe22},\r
+    {0xdc3, 0xe23},\r
+    {0xdc4, 0xe24},\r
+    {0xdc5, 0xe25},\r
+    {0xdc6, 0xe26},\r
+    {0xdc7, 0xe27},\r
+    {0xdc8, 0xe28},\r
+    {0xdc9, 0xe29},\r
+    {0xdca, 0xe2a},\r
+    {0xdcb, 0xe2b},\r
+    {0xdcc, 0xe2c},\r
+    {0xdcd, 0xe2d},\r
+    {0xdce, 0xe2e},\r
+    {0xdcf, 0xe2f},\r
+    {0xdd0, 0xe30},\r
+    {0xdd1, 0xe31},\r
+    {0xdd2, 0xe32},\r
+    {0xdd3, 0xe33},\r
+    {0xdd4, 0xe34},\r
+    {0xdd5, 0xe35},\r
+    {0xdd6, 0xe36},\r
+    {0xdd7, 0xe37},\r
+    {0xdd8, 0xe38},\r
+    {0xdd9, 0xe39},\r
+    {0xdda, 0xe3a},\r
+    {0xddf, 0xe3f},\r
+    {0xde0, 0xe40},\r
+    {0xde1, 0xe41},\r
+    {0xde2, 0xe42},\r
+    {0xde3, 0xe43},\r
+    {0xde4, 0xe44},\r
+    {0xde5, 0xe45},\r
+    {0xde6, 0xe46},\r
+    {0xde7, 0xe47},\r
+    {0xde8, 0xe48},\r
+    {0xde9, 0xe49},\r
+    {0xdea, 0xe4a},\r
+    {0xdeb, 0xe4b},\r
+    {0xdec, 0xe4c},\r
+    {0xded, 0xe4d},\r
+    {0xdf0, 0xe50},\r
+    {0xdf1, 0xe51},\r
+    {0xdf2, 0xe52},\r
+    {0xdf3, 0xe53},\r
+    {0xdf4, 0xe54},\r
+    {0xdf5, 0xe55},\r
+    {0xdf6, 0xe56},\r
+    {0xdf7, 0xe57},\r
+    {0xdf8, 0xe58},\r
+    {0xdf9, 0xe59},\r
+    {0xea1, 0x3131},\r
+    {0xea2, 0x3132},\r
+    {0xea3, 0x3133},\r
+    {0xea4, 0x3134},\r
+    {0xea5, 0x3135},\r
+    {0xea6, 0x3136},\r
+    {0xea7, 0x3137},\r
+    {0xea8, 0x3138},\r
+    {0xea9, 0x3139},\r
+    {0xeaa, 0x313a},\r
+    {0xeab, 0x313b},\r
+    {0xeac, 0x313c},\r
+    {0xead, 0x313d},\r
+    {0xeae, 0x313e},\r
+    {0xeaf, 0x313f},\r
+    {0xeb0, 0x3140},\r
+    {0xeb1, 0x3141},\r
+    {0xeb2, 0x3142},\r
+    {0xeb3, 0x3143},\r
+    {0xeb4, 0x3144},\r
+    {0xeb5, 0x3145},\r
+    {0xeb6, 0x3146},\r
+    {0xeb7, 0x3147},\r
+    {0xeb8, 0x3148},\r
+    {0xeb9, 0x3149},\r
+    {0xeba, 0x314a},\r
+    {0xebb, 0x314b},\r
+    {0xebc, 0x314c},\r
+    {0xebd, 0x314d},\r
+    {0xebe, 0x314e},\r
+    {0xebf, 0x314f},\r
+    {0xec0, 0x3150},\r
+    {0xec1, 0x3151},\r
+    {0xec2, 0x3152},\r
+    {0xec3, 0x3153},\r
+    {0xec4, 0x3154},\r
+    {0xec5, 0x3155},\r
+    {0xec6, 0x3156},\r
+    {0xec7, 0x3157},\r
+    {0xec8, 0x3158},\r
+    {0xec9, 0x3159},\r
+    {0xeca, 0x315a},\r
+    {0xecb, 0x315b},\r
+    {0xecc, 0x315c},\r
+    {0xecd, 0x315d},\r
+    {0xece, 0x315e},\r
+    {0xecf, 0x315f},\r
+    {0xed0, 0x3160},\r
+    {0xed1, 0x3161},\r
+    {0xed2, 0x3162},\r
+    {0xed3, 0x3163},\r
+    {0xed4, 0x11a8},\r
+    {0xed5, 0x11a9},\r
+    {0xed6, 0x11aa},\r
+    {0xed7, 0x11ab},\r
+    {0xed8, 0x11ac},\r
+    {0xed9, 0x11ad},\r
+    {0xeda, 0x11ae},\r
+    {0xedb, 0x11af},\r
+    {0xedc, 0x11b0},\r
+    {0xedd, 0x11b1},\r
+    {0xede, 0x11b2},\r
+    {0xedf, 0x11b3},\r
+    {0xee0, 0x11b4},\r
+    {0xee1, 0x11b5},\r
+    {0xee2, 0x11b6},\r
+    {0xee3, 0x11b7},\r
+    {0xee4, 0x11b8},\r
+    {0xee5, 0x11b9},\r
+    {0xee6, 0x11ba},\r
+    {0xee7, 0x11bb},\r
+    {0xee8, 0x11bc},\r
+    {0xee9, 0x11bd},\r
+    {0xeea, 0x11be},\r
+    {0xeeb, 0x11bf},\r
+    {0xeec, 0x11c0},\r
+    {0xeed, 0x11c1},\r
+    {0xeee, 0x11c2},\r
+    {0xeef, 0x316d},\r
+    {0xef0, 0x3171},\r
+    {0xef1, 0x3178},\r
+    {0xef2, 0x317f},\r
+    {0xef3, 0x3181},\r
+    {0xef4, 0x3184},\r
+    {0xef5, 0x3186},\r
+    {0xef6, 0x318d},\r
+    {0xef7, 0x318e},\r
+    {0xef8, 0x11eb},\r
+    {0xef9, 0x11f0},\r
+    {0xefa, 0x11f9},\r
+    {0xeff, 0x20a9},\r
+    {0x13a4, 0x20ac},\r
+    {0x13bc, 0x152},\r
+    {0x13bd, 0x153},\r
+    {0x13be, 0x178},\r
+    {0x20a0, 0x20a0},\r
+    {0x20a1, 0x20a1},\r
+    {0x20a2, 0x20a2},\r
+    {0x20a3, 0x20a3},\r
+    {0x20a4, 0x20a4},\r
+    {0x20a5, 0x20a5},\r
+    {0x20a6, 0x20a6},\r
+    {0x20a7, 0x20a7},\r
+    {0x20a8, 0x20a8},\r
+    {0x20aa, 0x20aa},\r
+    {0x20ab, 0x20ab},\r
+    {0x20ac, 0x20ac},\r
+};\r
+\r
+int keysym_to_unicode(int keysym)\r
+{\r
+    int i, j, k;\r
+\r
+    i = -1;\r
+    j = lenof(keysyms);\r
+\r
+    while (j - i >= 2) {\r
+       k = (j + i) / 2;\r
+       if (keysyms[k].keysym == keysym)\r
+           return keysyms[k].unicode;\r
+       else if (keysyms[k].keysym < keysym)\r
+           i = k;\r
+       else\r
+           j = k;\r
+    }\r
+    return -1;\r
+}\r
diff --git a/putty/UNIX/XPMPTCFG.C b/putty/UNIX/XPMPTCFG.C
new file mode 100644 (file)
index 0000000..af3d7fa
--- /dev/null
@@ -0,0 +1,150 @@
+/* XPM */\r
+static const char *const cfg_icon_0[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"16 16 9 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808000",\r
+"O c yellow",\r
+"+ c #808080",\r
+"@ c #C0C0C0",\r
+"# c gray100",\r
+"$ c None",\r
+/* pixels */\r
+"$$$  $$$$$$$$$$$",\r
+"$$ OO       $$$$",\r
+"$  +oO+###@+ $$$",\r
+" o #.oO.XX@+ $$$",\r
+" oO+.OO.XX@+ $$$",\r
+"$ oOOOO.XX@+ $$$",\r
+"$$ oooOO.X@+ $$$",\r
+"$$ +..oOO.@+ $$$",\r
+"$$ @@@+oOO++  $$",\r
+"$ +++++ oOO #+ $",\r
+" #######+oOO++ $",\r
+" #@@@@@++ oOO  $",\r
+" @++++++++ oOO $",\r
+"$           oOO ",\r
+"$$$$$$$$$$$$ oO ",\r
+"$$$$$$$$$$$$$  $"\r
+};\r
+\r
+/* XPM */\r
+static const char *const cfg_icon_1[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"32 32 9 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808000",\r
+"O c yellow",\r
+"+ c #808080",\r
+"@ c #C0C0C0",\r
+"# c gray100",\r
+"$ c None",\r
+/* pixels */\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$  $$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$ OO  $$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$ ooOO $$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$ ooOO               $$$$$$",\r
+"$$  $$$ oOO @@@@@@@@@@@@@+ $$$$$",\r
+"$ oO $$ oOOO @@@@@@@@@@@++ $$$$$",\r
+"$ oOO  oOOOO #########@+++ $$$$$",\r
+"$$ oOOOOOOO ..........@+++ $$$$$",\r
+"$$ ooOOOOOOO XXXXXXXXX@+++ $$$$$",\r
+"$$$ ooooooOOO XXXXXXXX@+++ $$$$$",\r
+"$$$$  oo ooOOO XXXXXXX@+++ $$$$$",\r
+"$$$$$$  . ooOOO XXXXXX@+++ $$$$$",\r
+"$$$$$$ #.X ooOOO XXXXX@+++ $$$$$",\r
+"$$$$$$ #.XX ooOOO XXXX@+++ $$$$$",\r
+"$$$$$$ #.XXX ooOOO XXX@+++ $$$$$",\r
+"$$$$$$ #.XXXX ooOOO XX@+++ $$$$$",\r
+"$$$$$$ ####### ooOOO #@+++   $$$",\r
+"$$$$$  #@@@@@@@ ooOOO +++ @#+ $$",\r
+"$$$$ @ @++++++++ ooOOO + @#++ $$",\r
+"$$$ @@            ooOOO @#+++ $$",\r
+"$$ ############### ooOOO @+++ $$",\r
+"$$ #@@@@@@@@@@@@@@@ ooOOO +++ $$",\r
+"$$ #@@@@@@@@@@@@@@@@ ooOOO + $$$",\r
+"$$ #@@@@@@@@@@@@+     ooOOO $$$$",\r
+"$$ @++++++++++++++++++ ooOOO $$$",\r
+"$$$                     ooOOO $$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$ ooO $$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$ o $$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"\r
+};\r
+\r
+/* XPM */\r
+static const char *const cfg_icon_2[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"48 48 9 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808000",\r
+"O c yellow",\r
+"+ c #808080",\r
+"@ c #C0C0C0",\r
+"# c gray100",\r
+"$ c None",\r
+/* pixels */\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$  $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$ OO   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$ oOOOO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$ ooOOO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$$ ooOOO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$$$ oOOO                      $$$$$$$$$$",\r
+"$$$  $$$$$$ oOOO @@@@@@@@@@@@@@@@@@@@+ $$$$$$$$$",\r
+"$$ oO $$$$$ oOOOO @@@@@@@@@@@@@@@@@@++ $$$$$$$$$",\r
+"$$ ooO $$$  oOOOO @@@@@@@@@@@@@@@@@+++ $$$$$$$$$",\r
+"$$$ oOO    OOOOO ################@++++ $$$$$$$$$",\r
+"$$$ ooOOOOOOOOOOO ++++++++++++++@+++++ $$$$$$$$$",\r
+"$$$ ooOOOOOOOOOOOO .............#+++++ $$$$$$$$$",\r
+"$$$$ oooOOOOoOOOOOO XXXXXXXXXXXX#+++++ $$$$$$$$$",\r
+"$$$$$ oooooooOOOOOOO XXXXXXXXXXX#+++++ $$$$$$$$$",\r
+"$$$$$$   oo ooOOOOOOO XXXXXXXXXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$  + ooOOOOOOO XXXXXXXXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+. ooOOOOOOO XXXXXXXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.X ooOOOOOOO XXXXXXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.XX ooOOOOOOO XXXXXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.XXX ooOOOOOOO XXXXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.XXXX ooOOOOOOO XXXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.XXXXX ooOOOOOOO XXX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.XXXXXX ooOOOOOOO XX#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.XXXXXXX ooOOOOOOO X#+++++ $$$$$$$$$",\r
+"$$$$$$$$$ #+.XXXXXXXX ooOOOOOOO #+++++ $$$$$$$$$",\r
+"$$$$$$$$  #@########## ooOOOOOOO +++++     $$$$$",\r
+"$$$$$$$ @ #@@@@@@@@@@@@ ooOOOOOOO +++ @@##+ $$$$",\r
+"$$$$$$ @@ #@@@@@@@@@@@@@ ooOOOOOOO + @@##++ $$$$",\r
+"$$$$$ @@@ @++++++++++++++ ooOOOOOOO @@##+++ $$$$",\r
+"$$$$ @@@@                  ooOOOOOOO ##++++ $$$$",\r
+"$$$ ####################### ooOOOOOOO @++++ $$$$",\r
+"$$$ ######################## ooOOOOOOO ++++ $$$$",\r
+"$$$ ##@@@@@@@@@@@@@@@@@@@@@@@ ooOOOOOOO +++ $$$$",\r
+"$$$ ##@@@@@@@@@@@@@@@@@@@@@@@@ ooOOOOOOO ++ $$$$",\r
+"$$$ ##@@@@@@@@@@@@@@@@@@@@@@@@@ ooOOOOOOO  $$$$$",\r
+"$$$ ##@@@@@@@@@@@@@@@@@@         ooOOOOOOO $$$$$",\r
+"$$$ @@+++++++++++++++++++++++++++ ooOOOOOOO $$$$",\r
+"$$$ @@++++++++++++++++++++++++++++ ooOOOOOOO $$$",\r
+"$$$$                                ooOOOOO $$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ooOOO $$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ooO $$$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o $$$$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"\r
+};\r
+\r
+const char *const *const cfg_icon[] = {\r
+    cfg_icon_0,\r
+    cfg_icon_1,\r
+    cfg_icon_2,\r
+};\r
+const int n_cfg_icon = 3;\r
diff --git a/putty/UNIX/XPMPTERM.C b/putty/UNIX/XPMPTERM.C
new file mode 100644 (file)
index 0000000..5cb379f
--- /dev/null
@@ -0,0 +1,143 @@
+/* XPM */\r
+static const char *const main_icon_0[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"16 16 6 1",\r
+"  c black",\r
+". c blue",\r
+"X c #808080",\r
+"o c #C0C0C0",\r
+"O c gray100",\r
+"+ c None",\r
+/* pixels */\r
+"++++++++++++++++",\r
+"+++         ++++",\r
+"++ OOOOOOOoX +++",\r
+"++ O......oX +++",\r
+"++ O......oX +++",\r
+"++ O......oX +++",\r
+"++ O......oX +++",\r
+"++ O......oX +++",\r
+"++ ooooooooX  ++",\r
+"+ XXXXXXXXXXOX +",\r
+" OOOOOOOOOOOoX +",\r
+" OoooooXXXXoXX +",\r
+" oXXXXXXXXXXX ++",\r
+"+            +++",\r
+"++++++++++++++++",\r
+"++++++++++++++++"\r
+};\r
+\r
+/* XPM */\r
+static const char *const main_icon_1[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"32 32 7 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808080",\r
+"O c #C0C0C0",\r
+"+ c gray100",\r
+"@ c None",\r
+/* pixels */\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@                 @@@@@@",\r
+"@@@@@@@@ OOOOOOOOOOOOOOOOo @@@@@",\r
+"@@@@@@@ OOOOOOOOOOOOOOOOoo @@@@@",\r
+"@@@@@@ +++++++++++++++Oooo @@@@@",\r
+"@@@@@@ +..............Oooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@",\r
+"@@@@@@ +++++++++++++++Oooo   @@@",\r
+"@@@@@  +OOOOOOOOOOOOOOooo O+o @@",\r
+"@@@@ O Ooooooooooooooooo O+oo @@",\r
+"@@@ OO                  O+ooo @@",\r
+"@@ ++++++++++++++++++++++Oooo @@",\r
+"@@ +OOOOOOOOOOOOOOOOOOOOOoooo @@",\r
+"@@ +OOOOOOOOOOOOOOOOOOOOOooo @@@",\r
+"@@ +OOOOOOOOOOOOo      oOoo @@@@",\r
+"@@ Ooooooooooooooooooooooo @@@@@",\r
+"@@@                       @@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"\r
+};\r
+\r
+/* XPM */\r
+static const char *const main_icon_2[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"48 48 7 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808080",\r
+"O c #C0C0C0",\r
+"+ c gray100",\r
+"@ c None",\r
+/* pixels */\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@                         @@@@@@@@@@",\r
+"@@@@@@@@@@@@ OOOOOOOOOOOOOOOOOOOOOOOOo @@@@@@@@@",\r
+"@@@@@@@@@@@ OOOOOOOOOOOOOOOOOOOOOOOOoo @@@@@@@@@",\r
+"@@@@@@@@@@ OOOOOOOOOOOOOOOOOOOOOOOOooo @@@@@@@@@",\r
+"@@@@@@@@@ +++++++++++++++++++++++Ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +oooooooooooooooooooooOooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o....................+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@",\r
+"@@@@@@@@  +O+++++++++++++++++++++ooooo     @@@@@",\r
+"@@@@@@@ O +OOOOOOOOOOOOOOOOOOOOOOoooo OO++o @@@@",\r
+"@@@@@@ OO +OOOOOOOOOOOOOOOOOOOOOOooo OO++oo @@@@",\r
+"@@@@@ OOO Ooooooooooooooooooooooooo OO++ooo @@@@",\r
+"@@@@ OOOO                          OO++oooo @@@@",\r
+"@@@ ++++++++++++++++++++++++++++++++++Ooooo @@@@",\r
+"@@@ +++++++++++++++++++++++++++++++++Oooooo @@@@",\r
+"@@@ ++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOoooooo @@@@",\r
+"@@@ ++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOoooooo @@@@",\r
+"@@@ ++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOooooo @@@@@",\r
+"@@@ ++OOOOOOOOOOOOOOOOOO          oOOoooo @@@@@@",\r
+"@@@ OOoooooooooooooooooooooooooooooooooo @@@@@@@",\r
+"@@@ OOooooooooooooooooooooooooooooooooo @@@@@@@@",\r
+"@@@@                                   @@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",\r
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"\r
+};\r
+\r
+const char *const *const main_icon[] = {\r
+    main_icon_0,\r
+    main_icon_1,\r
+    main_icon_2,\r
+};\r
+const int n_main_icon = 3;\r
diff --git a/putty/UNIX/XPMPUCFG.C b/putty/UNIX/XPMPUCFG.C
new file mode 100644 (file)
index 0000000..e6858e4
--- /dev/null
@@ -0,0 +1,150 @@
+/* XPM */\r
+static const char *const cfg_icon_0[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"16 16 9 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808000",\r
+"O c yellow",\r
+"+ c #808080",\r
+"@ c #C0C0C0",\r
+"# c gray100",\r
+"$ c None",\r
+/* pixels */\r
+"$$$  $$       $$",\r
+"$$ OO  #####@+ $",\r
+"$ $ oO #XX..@+ $",\r
+" o $ oO+X.O.@+ $",\r
+" oO  OO .O.X@+ $",\r
+"$ oOOOOoO++@@+ $",\r
+"$$ oooOOoOO +++ ",\r
+"$ # oooOO +++++ ",\r
+"$ #X..ooOO +++ $",\r
+"$ #X.O. oOO   $$",\r
+"$ #.O.X@ oOO $$$",\r
+"$ @++@@@+ oOO $$",\r
+"$ ++++++++ oOO $",\r
+" #####++++  oOO ",\r
+" @+++++++ $$ oO ",\r
+"$        $$$$  $"\r
+};\r
+\r
+/* XPM */\r
+static const char *const cfg_icon_1[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"32 32 9 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808000",\r
+"O c yellow",\r
+"+ c #808080",\r
+"@ c #C0C0C0",\r
+"# c gray100",\r
+"$ c None",\r
+/* pixels */\r
+"$$$$$$$$$$$$$$$$            $$$$",\r
+"$$$$$$  $$$$$$$ @@@@@@@@@@@+ $$$",\r
+"$$$$$ OO  $$$$ ##########@++ $$$",\r
+"$$$$$ ooOO $$$ #.........@++ $$$",\r
+"$$$$$$ ooOO $$ #.XXXXXXXX@++ $$$",\r
+"$$  $$$ oOO $$ #.XXXX  XX@++ $$$",\r
+"$ oO $$ oOOO $ #.XXX O XX@++ $$$",\r
+"$ oOO  oOOOO $ #.X  O XXX@++ $$$",\r
+"$$ oOOOOOOO $$ #. OO XXXX@++ $$$",\r
+"$$ ooOOOOOOO $ # OO XXXXX@++ $$$",\r
+"$$$ ooooooOOO   OO ######@++   $",\r
+"$$$$  oo ooOOO OO +++++++++ @#+ ",\r
+"$$$$$$  $ ooOOO            @#++ ",\r
+"$$$$$$$$$$ ooOOO OOOO ######@++ ",\r
+"$$$$$     O ooOOO O  @@@@@@@+++ ",\r
+"$$$$ @@@@@   ooOOO @@+    +@++ $",\r
+"$$$ ######### ooOOO +++++++++ $$",\r
+"$$$ #....... O ooOOO         $$$",\r
+"$$$ #.XXXXX OO  ooOOO $$$$$$$$$$",\r
+"$$$ #.XXXX OO @+ ooOOO $$$$$$$$$",\r
+"$$$ #.XXX O  X@++ ooOOO $$$$$$$$",\r
+"$$$ #.XX O XXX@++  ooOOO $$$$$$$",\r
+"$$$ #.XX  XXXX@++ $ ooOOO $$$$$$",\r
+"$$$ #.XXXXXXXX@++ $$ ooOOO $$$$$",\r
+"$$$ ##########@++   $ ooOOO $$$$",\r
+"$$  @+++++++++++ @#+ $ ooOOO $$$",\r
+"$ @             @#++ $$ ooOOO $$",\r
+" ################@++ $$$ ooO $$$",\r
+" #@@@@@@@@@@@@@@@+++ $$$$ o $$$$",\r
+" #@@@@@@@@+    +@++ $$$$$$ $$$$$",\r
+" @++++++++++++++++ $$$$$$$$$$$$$",\r
+"$                 $$$$$$$$$$$$$$"\r
+};\r
+\r
+/* XPM */\r
+static const char *const cfg_icon_2[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"48 48 9 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c #808000",\r
+"O c yellow",\r
+"+ c #808080",\r
+"@ c #C0C0C0",\r
+"# c gray100",\r
+"$ c None",\r
+/* pixels */\r
+"$$$$$$$$$$$$$$$$$$$$$$$$$                  $$$$$",\r
+"$$$$$$$$$$$$$$$$$$$$$$$$ @@@@@@@@@@@@@@@@@+ $$$$",\r
+"$$$$$$$$$  $$$$$$$$$$$$ @@@@@@@@@@@@@@@@@++ $$$$",\r
+"$$$$$$$$ OO   $$$$$$$$ ################@+++ $$$$",\r
+"$$$$$$$$ oOOOO $$$$$$$ #++++++++++++++@++++ $$$$",\r
+"$$$$$$$$$ ooOOO $$$$$$ #+.............#++++ $$$$",\r
+"$$$$$$$$$$ ooOOO $$$$$ #+.XXXXXXXXXXXX#++++ $$$$",\r
+"$$$$$$$$$$$ oOOO $$$$$ #+.XXXXXXXXXXXX#++++ $$$$",\r
+"$$$  $$$$$$ oOOO $$$$$ #+.XXXXXXX  XXX#++++ $$$$",\r
+"$$ oO $$$$$ oOOOO $$$$ #+.XXXXXX O XXX#++++ $$$$",\r
+"$$ ooO $$$$ oOOOO $$$$ #+.XXXXX O XXXX#++++ $$$$",\r
+"$$$ oOO    OOOOO $$$$$ #+.XXX  O XXXXX#++++ $$$$",\r
+"$$$ ooOOOOOOOOOOO $$$$ #+.XX OO XXXXXX#++++ $$$$",\r
+"$$$ ooOOOOOOOOOOOO $$$ #+.X OO XXXXXXX#++++ $$$$",\r
+"$$$$ oooOOOOoOOOOOO $$ #@  OO #########++++    $",\r
+"$$$$$ oooooooOOOOOOO   # OOO @@@@@@@@@@+++ @##+ ",\r
+"$$$$$$   oo ooOOOOOOO   OO  +++++++++++++ @##++ ",\r
+"$$$$$$$$$  $ ooOOOOOOO OO                @##+++ ",\r
+"$$$$$$$$$$$$$ ooOOOOOOO        ############@+++ ",\r
+"$$$$$$$$$$$$$$ ooOOOOOOO OOOOOO ##########@++++ ",\r
+"$$$$$$$$$$$$$$$ ooOOOOOOO OOO  @@+       @++++ $",\r
+"$$$$$$$$$$$$$$$$ ooOOOOOOO O ++++++++++++++++ $$",\r
+"$$$$$$$$$$$$$$$ O ooOOOOOOO ++++++++++++++++ $$$",\r
+"$$$$$$$$$$$$$$$$   ooOOOOOOO                $$$$",\r
+"$$$$$$$             ooOOOOOOO $$$$$$$$$$$$$$$$$$",\r
+"$$$$$$ @@@@@@@@@@@@  ooOOOOOOO $$$$$$$$$$$$$$$$$",\r
+"$$$$$ @@@@@@@@@@@@ OO ooOOOOOOO $$$$$$$$$$$$$$$$",\r
+"$$$$ ############ OO   ooOOOOOOO $$$$$$$$$$$$$$$",\r
+"$$$$ #++++++++++ OO @++ ooOOOOOOO $$$$$$$$$$$$$$",\r
+"$$$$ #+........ OO .#+++ ooOOOOOOO $$$$$$$$$$$$$",\r
+"$$$$ #+.XXXXXX O  XX#++++ ooOOOOOOO $$$$$$$$$$$$",\r
+"$$$$ #+.XXXXX O XXXX#++++  ooOOOOOOO $$$$$$$$$$$",\r
+"$$$$ #+.XXXX O XXXXX#++++ $ ooOOOOOOO $$$$$$$$$$",\r
+"$$$$ #+.XXXX  XXXXXX#++++ $$ ooOOOOOOO $$$$$$$$$",\r
+"$$$$ #+.XXXXXXXXXXXX#++++ $$$ ooOOOOOOO $$$$$$$$",\r
+"$$$$ #+.XXXXXXXXXXXX#++++ $$$$ ooOOOOOOO $$$$$$$",\r
+"$$$$ #+.XXXXXXXXXXXX#++++ $$$$$ ooOOOOOOO $$$$$$",\r
+"$$$$ #+.XXXXXXXXXXXX#++++ $$$$$$ ooOOOOOOO $$$$$",\r
+"$$$$ #@##############++++    $$$$ ooOOOOOOO $$$$",\r
+"$$$  #@@@@@@@@@@@@@@@+++ @##+ $$$$ ooOOOOOOO $$$",\r
+"$$ @ @+++++++++++++++++ @##++ $$$$$ ooOOOOO $$$$",\r
+"$ @@                   @##+++ $$$$$$ ooOOO $$$$$",\r
+" ########################@+++ $$$$$$$ ooO $$$$$$",\r
+" #######################@++++ $$$$$$$$ o $$$$$$$",\r
+" ##@@@@@@@@@@@@+       @++++ $$$$$$$$$$ $$$$$$$$",\r
+" @@++++++++++++++++++++++++ $$$$$$$$$$$$$$$$$$$$",\r
+" @@+++++++++++++++++++++++ $$$$$$$$$$$$$$$$$$$$$",\r
+"$                         $$$$$$$$$$$$$$$$$$$$$$"\r
+};\r
+\r
+const char *const *const cfg_icon[] = {\r
+    cfg_icon_0,\r
+    cfg_icon_1,\r
+    cfg_icon_2,\r
+};\r
+const int n_cfg_icon = 3;\r
diff --git a/putty/UNIX/XPMPUTTY.C b/putty/UNIX/XPMPUTTY.C
new file mode 100644 (file)
index 0000000..e51b011
--- /dev/null
@@ -0,0 +1,147 @@
+/* XPM */\r
+static const char *const main_icon_0[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"16 16 8 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c yellow",\r
+"O c #808080",\r
+"+ c #C0C0C0",\r
+"@ c gray100",\r
+"# c None",\r
+/* pixels */\r
+"#######       ##",\r
+"###### @@@@@+O #",\r
+"###### @XX..+O #",\r
+"###### @X.o.+O #",\r
+"###### O.o.X+O #",\r
+"###### ooOO++O #",\r
+"##    ooooo OOO ",\r
+"# @Oooooo OOOOO ",\r
+"# @X..oo  OOOO #",\r
+"# @X.o.OO     ##",\r
+"# @.o.X+O ######",\r
+"# +OO+++O ######",\r
+"# OOOOOOOO #####",\r
+" @@@@@OOOO #####",\r
+" +OOOOOOO ######",\r
+"#        #######"\r
+};\r
+\r
+/* XPM */\r
+static const char *const main_icon_1[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"32 32 8 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c yellow",\r
+"O c #808080",\r
+"+ c #C0C0C0",\r
+"@ c gray100",\r
+"# c None",\r
+/* pixels */\r
+"################            ####",\r
+"############### +++++++++++O ###",\r
+"############## @@@@@@@@@@+OO ###",\r
+"############## @.........+OO ###",\r
+"############## @.XXXXXXXX+OO ###",\r
+"############## @.XXXX  XX+OO ###",\r
+"############## @.XXX o XX+OO ###",\r
+"############## @.X  o XXX+OO ###",\r
+"############## @. oo XXXX+OO ###",\r
+"############## @ oo XXXXX+OO ###",\r
+"##############  oo @@@@@@+OO   #",\r
+"############# ooo OOOOOOOOO +@O ",\r
+"############ ooo           +@OO ",\r
+"##########  ooooooooo @@@@@@+OO ",\r
+"#####     ooooooooo  +++++++OOO ",\r
+"#### +++++     ooo ++O    O+OO #",\r
+"### @@@@@@@@@ ooo OOOOOOOOOOO ##",\r
+"### @....... oo              ###",\r
+"### @.XXXXX oo OO ##############",\r
+"### @.XXXX oo +OO ##############",\r
+"### @.XXX o  X+OO ##############",\r
+"### @.XX o XXX+OO ##############",\r
+"### @.XX  XXXX+OO ##############",\r
+"### @.XXXXXXXX+OO ##############",\r
+"### @@@@@@@@@@+OO   ############",\r
+"##  +OOOOOOOOOOO +@O ###########",\r
+"# +             +@OO ###########",\r
+" @@@@@@@@@@@@@@@@+OO ###########",\r
+" @+++++++++++++++OOO ###########",\r
+" @++++++++O    O+OO ############",\r
+" +OOOOOOOOOOOOOOOO #############",\r
+"#                 ##############"\r
+};\r
+\r
+/* XPM */\r
+static const char *const main_icon_2[] = {\r
+/* columns rows colors chars-per-pixel */\r
+"48 48 8 1",\r
+"  c black",\r
+". c navy",\r
+"X c blue",\r
+"o c yellow",\r
+"O c #808080",\r
+"+ c #C0C0C0",\r
+"@ c gray100",\r
+"# c None",\r
+/* pixels */\r
+"#########################                  #####",\r
+"######################## +++++++++++++++++O ####",\r
+"####################### +++++++++++++++++OO ####",\r
+"###################### @@@@@@@@@@@@@@@@+OOO ####",\r
+"###################### @OOOOOOOOOOOOOO+OOOO ####",\r
+"###################### @O.............@OOOO ####",\r
+"###################### @O.XXXXXXXXXXXX@OOOO ####",\r
+"###################### @O.XXXXXXXXXXXX@OOOO ####",\r
+"###################### @O.XXXXXXX  XXX@OOOO ####",\r
+"###################### @O.XXXXXX o XXX@OOOO ####",\r
+"###################### @O.XXXXX o XXXX@OOOO ####",\r
+"###################### @O.XXX  o XXXXX@OOOO ####",\r
+"###################### @O.XX oo XXXXXX@OOOO ####",\r
+"###################### @O.X oo XXXXXXX@OOOO ####",\r
+"###################### @+  oo @@@@@@@@@OOOO    #",\r
+"#####################  @ ooo ++++++++++OOO +@@O ",\r
+"#################### +  oo  OOOOOOOOOOOOO +@@OO ",\r
+"################### +  oo                +@@OOO ",\r
+"################## @ ooo       @@@@@@@@@@@@+OOO ",\r
+"##################  ooooooooooo @@@@@@@@@@+OOOO ",\r
+"################## oooooooooo  ++O       +OOOO #",\r
+"################  oooooooooo OOOOOOOOOOOOOOOO ##",\r
+"############### ooooooooooo OOOOOOOOOOOOOOOO ###",\r
+"################       ooo                  ####",\r
+"#######               oo  ######################",\r
+"###### ++++++++++++  oo O ######################",\r
+"##### ++++++++++++ ooo OO ######################",\r
+"#### @@@@@@@@@@@@ oo  OOO ######################",\r
+"#### @OOOOOOOOOO oo +OOOO ######################",\r
+"#### @O........ oo .@OOOO ######################",\r
+"#### @O.XXXXXX o  XX@OOOO ######################",\r
+"#### @O.XXXXX o XXXX@OOOO ######################",\r
+"#### @O.XXXX o XXXXX@OOOO ######################",\r
+"#### @O.XXXX  XXXXXX@OOOO ######################",\r
+"#### @O.XXXXXXXXXXXX@OOOO ######################",\r
+"#### @O.XXXXXXXXXXXX@OOOO ######################",\r
+"#### @O.XXXXXXXXXXXX@OOOO ######################",\r
+"#### @O.XXXXXXXXXXXX@OOOO ######################",\r
+"#### @+@@@@@@@@@@@@@@OOOO    ###################",\r
+"###  @+++++++++++++++OOO +@@O ##################",\r
+"## + +OOOOOOOOOOOOOOOOO +@@OO ##################",\r
+"# ++                   +@@OOO ##################",\r
+" @@@@@@@@@@@@@@@@@@@@@@@@+OOO ##################",\r
+" @@@@@@@@@@@@@@@@@@@@@@@+OOOO ##################",\r
+" @@++++++++++++O       +OOOO ###################",\r
+" ++OOOOOOOOOOOOOOOOOOOOOOOO ####################",\r
+" ++OOOOOOOOOOOOOOOOOOOOOOO #####################",\r
+"#                         ######################"\r
+};\r
+\r
+const char *const *const main_icon[] = {\r
+    main_icon_0,\r
+    main_icon_1,\r
+    main_icon_2,\r
+};\r
+const int n_main_icon = 3;\r
diff --git a/putty/VERSION.C b/putty/VERSION.C
new file mode 100644 (file)
index 0000000..ece99fb
--- /dev/null
@@ -0,0 +1,42 @@
+/*\r
+ * PuTTY version numbering\r
+ */\r
+\r
+#define STR1(x) #x\r
+#define STR(x) STR1(x)\r
+\r
+#if defined SNAPSHOT\r
+\r
+#if defined SVN_REV\r
+#define SNAPSHOT_TEXT STR(SNAPSHOT) ":r" STR(SVN_REV)\r
+#else\r
+#define SNAPSHOT_TEXT STR(SNAPSHOT)\r
+#endif\r
+\r
+char ver[] = "Development snapshot " SNAPSHOT_TEXT;\r
+char sshver[] = "PuTTY-Snapshot-" SNAPSHOT_TEXT;\r
+\r
+#undef SNAPSHOT_TEXT\r
+\r
+#elif defined RELEASE\r
+\r
+char ver[] = "Release " STR(RELEASE);\r
+char sshver[] = "PuTTY-Release-" STR(RELEASE);\r
+\r
+#elif defined SVN_REV\r
+\r
+char ver[] = "Custom build r" STR(SVN_REV) ", " __DATE__ " " __TIME__;\r
+char sshver[] = "PuTTY-Custom-r" STR(SVN_REV);\r
+\r
+#else\r
+\r
+char ver[] = "Unidentified build, " __DATE__ " " __TIME__;\r
+char sshver[] = "PuTTY-Local: " __DATE__ " " __TIME__;\r
+\r
+#endif\r
+\r
+/*\r
+ * SSH local version string MUST be under 40 characters. Here's a\r
+ * compile time assertion to verify this.\r
+ */\r
+enum { vorpal_sword = 1 / (sizeof(sshver) <= 40) };\r
diff --git a/putty/WCWIDTH.C b/putty/WCWIDTH.C
new file mode 100644 (file)
index 0000000..bcd153d
--- /dev/null
@@ -0,0 +1,303 @@
+/*\r
+ * This is an implementation of wcwidth() and wcswidth() (defined in\r
+ * IEEE Std 1002.1-2001) for Unicode.\r
+ *\r
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html\r
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html\r
+ *\r
+ * In fixed-width output devices, Latin characters all occupy a single\r
+ * "cell" position of equal width, whereas ideographic CJK characters\r
+ * occupy two such cells. Interoperability between terminal-line\r
+ * applications and (teletype-style) character terminals using the\r
+ * UTF-8 encoding requires agreement on which character should advance\r
+ * the cursor by how many cell positions. No established formal\r
+ * standards exist at present on which Unicode character shall occupy\r
+ * how many cell positions on character terminals. These routines are\r
+ * a first attempt of defining such behavior based on simple rules\r
+ * applied to data provided by the Unicode Consortium.\r
+ *\r
+ * For some graphical characters, the Unicode standard explicitly\r
+ * defines a character-cell width via the definition of the East Asian\r
+ * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.\r
+ * In all these cases, there is no ambiguity about which width a\r
+ * terminal shall use. For characters in the East Asian Ambiguous (A)\r
+ * class, the width choice depends purely on a preference of backward\r
+ * compatibility with either historic CJK or Western practice.\r
+ * Choosing single-width for these characters is easy to justify as\r
+ * the appropriate long-term solution, as the CJK practice of\r
+ * displaying these characters as double-width comes from historic\r
+ * implementation simplicity (8-bit encoded characters were displayed\r
+ * single-width and 16-bit ones double-width, even for Greek,\r
+ * Cyrillic, etc.) and not any typographic considerations.\r
+ *\r
+ * Much less clear is the choice of width for the Not East Asian\r
+ * (Neutral) class. Existing practice does not dictate a width for any\r
+ * of these characters. It would nevertheless make sense\r
+ * typographically to allocate two character cells to characters such\r
+ * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be\r
+ * represented adequately with a single-width glyph. The following\r
+ * routines at present merely assign a single-cell width to all\r
+ * neutral characters, in the interest of simplicity. This is not\r
+ * entirely satisfactory and should be reconsidered before\r
+ * establishing a formal standard in this area. At the moment, the\r
+ * decision which Not East Asian (Neutral) characters should be\r
+ * represented by double-width glyphs cannot yet be answered by\r
+ * applying a simple rule from the Unicode database content. Setting\r
+ * up a proper standard for the behavior of UTF-8 character terminals\r
+ * will require a careful analysis not only of each Unicode character,\r
+ * but also of each presentation form, something the author of these\r
+ * routines has avoided to do so far.\r
+ *\r
+ * http://www.unicode.org/unicode/reports/tr11/\r
+ *\r
+ * Markus Kuhn -- 2003-05-20 (Unicode 4.0)\r
+ *\r
+ * Permission to use, copy, modify, and distribute this software\r
+ * for any purpose and without fee is hereby granted. The author\r
+ * disclaims all warranties with regard to this software.\r
+ *\r
+ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c\r
+ */\r
+\r
+#include <wchar.h>\r
+\r
+#include "putty.h" /* for prototypes */\r
+\r
+struct interval {\r
+  int first;\r
+  int last;\r
+};\r
+\r
+/* auxiliary function for binary search in interval table */\r
+static int bisearch(wchar_t ucs, const struct interval *table, int max) {\r
+  int min = 0;\r
+  int mid;\r
+\r
+  if (ucs < table[0].first || ucs > table[max].last)\r
+    return 0;\r
+  while (max >= min) {\r
+    mid = (min + max) / 2;\r
+    if (ucs > table[mid].last)\r
+      min = mid + 1;\r
+    else if (ucs < table[mid].first)\r
+      max = mid - 1;\r
+    else\r
+      return 1;\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+\r
+/* The following two functions define the column width of an ISO 10646\r
+ * character as follows:\r
+ *\r
+ *    - The null character (U+0000) has a column width of 0.\r
+ *\r
+ *    - Other C0/C1 control characters and DEL will lead to a return\r
+ *      value of -1.\r
+ *\r
+ *    - Non-spacing and enclosing combining characters (general\r
+ *      category code Mn or Me in the Unicode database) have a\r
+ *      column width of 0.\r
+ *\r
+ *    - SOFT HYPHEN (U+00AD) has a column width of 1.\r
+ *\r
+ *    - Other format characters (general category code Cf in the Unicode\r
+ *      database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.\r
+ *\r
+ *    - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)\r
+ *      have a column width of 0.\r
+ *\r
+ *    - Spacing characters in the East Asian Wide (W) or East Asian\r
+ *      Full-width (F) category as defined in Unicode Technical\r
+ *      Report #11 have a column width of 2.\r
+ *\r
+ *    - All remaining characters (including all printable\r
+ *      ISO 8859-1 and WGL4 characters, Unicode control characters,\r
+ *      etc.) have a column width of 1.\r
+ *\r
+ * This implementation assumes that wchar_t characters are encoded\r
+ * in ISO 10646.\r
+ */\r
+\r
+int mk_wcwidth(wchar_t ucs)\r
+{\r
+  /* sorted list of non-overlapping intervals of non-spacing characters */\r
+  /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */\r
+  static const struct interval combining[] = {\r
+    { 0x0300, 0x0357 }, { 0x035D, 0x036F }, { 0x0483, 0x0486 },\r
+    { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 },\r
+    { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },\r
+    { 0x05C4, 0x05C4 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 },\r
+    { 0x064B, 0x0658 }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 },\r
+    { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F },\r
+    { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 },\r
+    { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 },\r
+    { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 },\r
+    { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 },\r
+    { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 },\r
+    { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 },\r
+    { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 },\r
+    { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 },\r
+    { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 },\r
+    { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 },\r
+    { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 },\r
+    { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 },\r
+    { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 },\r
+    { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 },\r
+    { 0x0CCC, 0x0CCD }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },\r
+    { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },\r
+    { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },\r
+    { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },\r
+    { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },\r
+    { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },\r
+    { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },\r
+    { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },\r
+    { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },\r
+    { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x1712, 0x1714 },\r
+    { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 },\r
+    { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 },\r
+    { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D },\r
+    { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 },\r
+    { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x200B, 0x200F },\r
+    { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F },\r
+    { 0x20D0, 0x20EA }, { 0x302A, 0x302F }, { 0x3099, 0x309A },\r
+    { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 },\r
+    { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x1D167, 0x1D169 },\r
+    { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },\r
+    { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF }\r
+  };\r
+\r
+  /* test for 8-bit control characters */\r
+  if (ucs == 0)\r
+    return 0;\r
+  if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))\r
+    return -1;\r
+\r
+  /* binary search in table of non-spacing characters */\r
+  if (bisearch(ucs, combining,\r
+              sizeof(combining) / sizeof(struct interval) - 1))\r
+    return 0;\r
+\r
+  /* if we arrive here, ucs is not a combining or C0/C1 control character */\r
+\r
+  return 1 + \r
+    (ucs >= 0x1100 &&\r
+     (ucs <= 0x115f ||                    /* Hangul Jamo init. consonants */\r
+      ucs == 0x2329 || ucs == 0x232a ||\r
+      (ucs >= 0x2e80 && ucs <= 0xa4cf &&\r
+       ucs != 0x303f) ||                  /* CJK ... Yi */\r
+      (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */\r
+      (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */\r
+      (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */\r
+      (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */\r
+      (ucs >= 0xffe0 && ucs <= 0xffe6) ||\r
+      (ucs >= 0x20000 && ucs <= 0x2fffd) ||\r
+      (ucs >= 0x30000 && ucs <= 0x3fffd)));\r
+}\r
+\r
+\r
+int mk_wcswidth(const wchar_t *pwcs, size_t n)\r
+{\r
+  int w, width = 0;\r
+\r
+  for (;*pwcs && n-- > 0; pwcs++)\r
+    if ((w = mk_wcwidth(*pwcs)) < 0)\r
+      return -1;\r
+    else\r
+      width += w;\r
+\r
+  return width;\r
+}\r
+\r
+\r
+/*\r
+ * The following functions are the same as mk_wcwidth() and\r
+ * mk_wcwidth_cjk(), except that spacing characters in the East Asian\r
+ * Ambiguous (A) category as defined in Unicode Technical Report #11\r
+ * have a column width of 2. This variant might be useful for users of\r
+ * CJK legacy encodings who want to migrate to UCS without changing\r
+ * the traditional terminal character-width behaviour. It is not\r
+ * otherwise recommended for general use.\r
+ */\r
+int mk_wcwidth_cjk(wchar_t ucs)\r
+{\r
+  /* sorted list of non-overlapping intervals of East Asian Ambiguous\r
+   * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */\r
+  static const struct interval ambiguous[] = {\r
+    { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },\r
+    { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },\r
+    { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },\r
+    { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },\r
+    { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },\r
+    { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },\r
+    { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },\r
+    { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },\r
+    { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },\r
+    { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },\r
+    { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },\r
+    { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },\r
+    { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },\r
+    { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },\r
+    { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },\r
+    { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },\r
+    { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },\r
+    { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },\r
+    { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },\r
+    { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },\r
+    { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },\r
+    { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },\r
+    { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },\r
+    { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },\r
+    { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },\r
+    { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },\r
+    { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },\r
+    { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },\r
+    { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },\r
+    { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },\r
+    { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },\r
+    { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },\r
+    { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },\r
+    { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },\r
+    { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },\r
+    { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },\r
+    { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },\r
+    { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },\r
+    { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },\r
+    { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },\r
+    { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },\r
+    { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },\r
+    { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },\r
+    { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },\r
+    { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },\r
+    { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },\r
+    { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },\r
+    { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },\r
+    { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },\r
+    { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },\r
+    { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },\r
+    { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }\r
+  };\r
+\r
+  /* binary search in table of non-spacing characters */\r
+  if (bisearch(ucs, ambiguous,\r
+              sizeof(ambiguous) / sizeof(struct interval) - 1))\r
+    return 2;\r
+\r
+  return mk_wcwidth(ucs);\r
+}\r
+\r
+\r
+int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)\r
+{\r
+  int w, width = 0;\r
+\r
+  for (;*pwcs && n-- > 0; pwcs++)\r
+    if ((w = mk_wcwidth_cjk(*pwcs)) < 0)\r
+      return -1;\r
+    else\r
+      width += w;\r
+\r
+  return width;\r
+}\r
diff --git a/putty/WILDCARD.C b/putty/WILDCARD.C
new file mode 100644 (file)
index 0000000..75a7573
--- /dev/null
@@ -0,0 +1,472 @@
+/*\r
+ * Wildcard matching engine for use with SFTP-based file transfer\r
+ * programs (PSFTP, new-look PSCP): since SFTP has no notion of\r
+ * getting the remote side to do globbing (and rightly so) we have\r
+ * to do it locally, by retrieving all the filenames in a directory\r
+ * and checking each against the wildcard pattern.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include "putty.h"\r
+\r
+/*\r
+ * Definition of wildcard syntax:\r
+ * \r
+ *  - * matches any sequence of characters, including zero.\r
+ *  - ? matches exactly one character which can be anything.\r
+ *  - [abc] matches exactly one character which is a, b or c.\r
+ *  - [a-f] matches anything from a through f.\r
+ *  - [^a-f] matches anything _except_ a through f.\r
+ *  - [-_] matches - or _; [^-_] matches anything else. (The - is\r
+ *    non-special if it occurs immediately after the opening\r
+ *    bracket or ^.)\r
+ *  - [a^] matches an a or a ^. (The ^ is non-special if it does\r
+ *    _not_ occur immediately after the opening bracket.)\r
+ *  - \*, \?, \[, \], \\ match the single characters *, ?, [, ], \.\r
+ *  - All other characters are non-special and match themselves.\r
+ */\r
+\r
+/*\r
+ * Some notes on differences from POSIX globs (IEEE Std 1003.1, 2003 ed.):\r
+ *  - backslashes act as escapes even within [] bracket expressions\r
+ *  - does not support [!...] for non-matching list (POSIX are weird);\r
+ *    NB POSIX allows [^...] as well via "A bracket expression starting\r
+ *    with an unquoted circumflex character produces unspecified\r
+ *    results". If we wanted to allow [!...] we might want to define\r
+ *    [^!] as having its literal meaning (match '^' or '!').\r
+ *  - none of the scary [[:class:]] stuff, etc\r
+ */\r
+\r
+/*\r
+ * The wildcard matching technique we use is very simple and\r
+ * potentially O(N^2) in running time, but I don't anticipate it\r
+ * being that bad in reality (particularly since N will be the size\r
+ * of a filename, which isn't all that much). Perhaps one day, once\r
+ * PuTTY has grown a regexp matcher for some other reason, I might\r
+ * come back and reimplement wildcards by translating them into\r
+ * regexps or directly into NFAs; but for the moment, in the\r
+ * absence of any other need for the NFA->DFA translation engine,\r
+ * anything more than the simplest possible wildcard matcher is\r
+ * vast code-size overkill.\r
+ * \r
+ * Essentially, these wildcards are much simpler than regexps in\r
+ * that they consist of a sequence of rigid fragments (? and [...]\r
+ * can never match more or less than one character) separated by\r
+ * asterisks. It is therefore extremely simple to look at a rigid\r
+ * fragment and determine whether or not it begins at a particular\r
+ * point in the test string; so we can search along the string\r
+ * until we find each fragment, then search for the next. As long\r
+ * as we find each fragment in the _first_ place it occurs, there\r
+ * will never be a danger of having to backpedal and try to find it\r
+ * again somewhere else.\r
+ */\r
+\r
+enum {\r
+    WC_TRAILINGBACKSLASH = 1,\r
+    WC_UNCLOSEDCLASS,\r
+    WC_INVALIDRANGE\r
+};\r
+\r
+/*\r
+ * Error reporting is done by returning various negative values\r
+ * from the wildcard routines. Passing any such value to wc_error\r
+ * will give a human-readable message.\r
+ */\r
+const char *wc_error(int value)\r
+{\r
+    value = abs(value);\r
+    switch (value) {\r
+      case WC_TRAILINGBACKSLASH:\r
+       return "'\' occurred at end of string (expected another character)";\r
+      case WC_UNCLOSEDCLASS:\r
+       return "expected ']' to close character class";\r
+      case WC_INVALIDRANGE:\r
+       return "character range was not terminated (']' just after '-')";\r
+    }\r
+    return "INTERNAL ERROR: unrecognised wildcard error number";\r
+}\r
+\r
+/*\r
+ * This is the routine that tests a target string to see if an\r
+ * initial substring of it matches a fragment. If successful, it\r
+ * returns 1, and advances both `fragment' and `target' past the\r
+ * fragment and matching substring respectively. If unsuccessful it\r
+ * returns zero. If the wildcard fragment suffers a syntax error,\r
+ * it returns <0 and the precise value indexes into wc_error.\r
+ */\r
+static int wc_match_fragment(const char **fragment, const char **target)\r
+{\r
+    const char *f, *t;\r
+\r
+    f = *fragment;\r
+    t = *target;\r
+    /*\r
+     * The fragment terminates at either the end of the string, or\r
+     * the first (unescaped) *.\r
+     */\r
+    while (*f && *f != '*' && *t) {\r
+       /*\r
+        * Extract one character from t, and one character's worth\r
+        * of pattern from f, and step along both. Return 0 if they\r
+        * fail to match.\r
+        */\r
+       if (*f == '\\') {\r
+           /*\r
+            * Backslash, which means f[1] is to be treated as a\r
+            * literal character no matter what it is. It may not\r
+            * be the end of the string.\r
+            */\r
+           if (!f[1])\r
+               return -WC_TRAILINGBACKSLASH;   /* error */\r
+           if (f[1] != *t)\r
+               return 0;              /* failed to match */\r
+           f += 2;\r
+       } else if (*f == '?') {\r
+           /*\r
+            * Question mark matches anything.\r
+            */\r
+           f++;\r
+       } else if (*f == '[') {\r
+           int invert = 0;\r
+           int matched = 0;\r
+           /*\r
+            * Open bracket introduces a character class.\r
+            */\r
+           f++;\r
+           if (*f == '^') {\r
+               invert = 1;\r
+               f++;\r
+           }\r
+           while (*f != ']') {\r
+               if (*f == '\\')\r
+                   f++;               /* backslashes still work */\r
+               if (!*f)\r
+                   return -WC_UNCLOSEDCLASS;   /* error again */\r
+               if (f[1] == '-') {\r
+                   int lower, upper, ourchr;\r
+                   lower = (unsigned char) *f++;\r
+                   f++;               /* eat the minus */\r
+                   if (*f == ']')\r
+                       return -WC_INVALIDRANGE;   /* different error! */\r
+                   if (*f == '\\')\r
+                       f++;           /* backslashes _still_ work */\r
+                   if (!*f)\r
+                       return -WC_UNCLOSEDCLASS;   /* error again */\r
+                   upper = (unsigned char) *f++;\r
+                   ourchr = (unsigned char) *t;\r
+                   if (lower > upper) {\r
+                       int t = lower; lower = upper; upper = t;\r
+                   }\r
+                   if (ourchr >= lower && ourchr <= upper)\r
+                       matched = 1;\r
+               } else {\r
+                   matched |= (*t == *f++);\r
+               }\r
+           }\r
+           if (invert == matched)\r
+               return 0;              /* failed to match character class */\r
+           f++;                       /* eat the ] */\r
+       } else {\r
+           /*\r
+            * Non-special character matches itself.\r
+            */\r
+           if (*f != *t)\r
+               return 0;\r
+           f++;\r
+       }\r
+       /*\r
+        * Now we've done that, increment t past the character we\r
+        * matched.\r
+        */\r
+       t++;\r
+    }\r
+    if (!*f || *f == '*') {\r
+       /*\r
+        * We have reached the end of f without finding a mismatch;\r
+        * so we're done. Update the caller pointers and return 1.\r
+        */\r
+       *fragment = f;\r
+       *target = t;\r
+       return 1;\r
+    }\r
+    /*\r
+     * Otherwise, we must have reached the end of t before we\r
+     * reached the end of f; so we've failed. Return 0. \r
+     */\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * This is the real wildcard matching routine. It returns 1 for a\r
+ * successful match, 0 for an unsuccessful match, and <0 for a\r
+ * syntax error in the wildcard.\r
+ */\r
+int wc_match(const char *wildcard, const char *target)\r
+{\r
+    int ret;\r
+\r
+    /*\r
+     * Every time we see a '*' _followed_ by a fragment, we just\r
+     * search along the string for a location at which the fragment\r
+     * matches. The only special case is when we see a fragment\r
+     * right at the start, in which case we just call the matching\r
+     * routine once and give up if it fails.\r
+     */\r
+    if (*wildcard != '*') {\r
+       ret = wc_match_fragment(&wildcard, &target);\r
+       if (ret <= 0)\r
+           return ret;                /* pass back failure or error alike */\r
+    }\r
+\r
+    while (*wildcard) {\r
+       assert(*wildcard == '*');\r
+       while (*wildcard == '*')\r
+           wildcard++;\r
+\r
+       /*\r
+        * It's possible we've just hit the end of the wildcard\r
+        * after seeing a *, in which case there's no need to\r
+        * bother searching any more because we've won.\r
+        */\r
+       if (!*wildcard)\r
+           return 1;\r
+\r
+       /*\r
+        * Now `wildcard' points at the next fragment. So we\r
+        * attempt to match it against `target', and if that fails\r
+        * we increment `target' and try again, and so on. When we\r
+        * find we're about to try matching against the empty\r
+        * string, we give up and return 0.\r
+        */\r
+       ret = 0;\r
+       while (*target) {\r
+           const char *save_w = wildcard, *save_t = target;\r
+\r
+           ret = wc_match_fragment(&wildcard, &target);\r
+\r
+           if (ret < 0)\r
+               return ret;            /* syntax error */\r
+\r
+           if (ret > 0 && !*wildcard && *target) {\r
+               /*\r
+                * Final special case - literally.\r
+                * \r
+                * This situation arises when we are matching a\r
+                * _terminal_ fragment of the wildcard (that is,\r
+                * there is nothing after it, e.g. "*a"), and it\r
+                * has matched _too early_. For example, matching\r
+                * "*a" against "parka" will match the "a" fragment\r
+                * against the _first_ a, and then (if it weren't\r
+                * for this special case) matching would fail\r
+                * because we're at the end of the wildcard but not\r
+                * at the end of the target string.\r
+                * \r
+                * In this case what we must do is measure the\r
+                * length of the fragment in the target (which is\r
+                * why we saved `target'), jump straight to that\r
+                * distance from the end of the string using\r
+                * strlen, and match the same fragment again there\r
+                * (which is why we saved `wildcard'). Then we\r
+                * return whatever that operation returns.\r
+                */\r
+               target = save_t + strlen(save_t) - (target - save_t);\r
+               wildcard = save_w;\r
+               return wc_match_fragment(&wildcard, &target);\r
+           }\r
+\r
+           if (ret > 0)\r
+               break;\r
+           target++;\r
+       }\r
+       if (ret > 0)\r
+           continue;\r
+       return 0;\r
+    }\r
+\r
+    /*\r
+     * If we reach here, it must be because we successfully matched\r
+     * a fragment and then found ourselves right at the end of the\r
+     * wildcard. Hence, we return 1 if and only if we are also\r
+     * right at the end of the target.\r
+     */\r
+    return (*target ? 0 : 1);\r
+}\r
+\r
+/*\r
+ * Another utility routine that translates a non-wildcard string\r
+ * into its raw equivalent by removing any escaping backslashes.\r
+ * Expects a target string buffer of anything up to the length of\r
+ * the original wildcard. You can also pass NULL as the output\r
+ * buffer if you're only interested in the return value.\r
+ * \r
+ * Returns 1 on success, or 0 if a wildcard character was\r
+ * encountered. In the latter case the output string MAY not be\r
+ * zero-terminated and you should not use it for anything!\r
+ */\r
+int wc_unescape(char *output, const char *wildcard)\r
+{\r
+    while (*wildcard) {\r
+       if (*wildcard == '\\') {\r
+           wildcard++;\r
+           /* We are lenient about trailing backslashes in non-wildcards. */\r
+           if (*wildcard) {\r
+               if (output)\r
+                   *output++ = *wildcard;\r
+               wildcard++;\r
+           }\r
+       } else if (*wildcard == '*' || *wildcard == '?' ||\r
+                  *wildcard == '[' || *wildcard == ']') {\r
+           return 0;                  /* it's a wildcard! */\r
+       } else {\r
+           if (output)\r
+               *output++ = *wildcard;\r
+           wildcard++;\r
+       }\r
+    }\r
+    *output = '\0';\r
+    return 1;                         /* it's clean */\r
+}\r
+\r
+#ifdef TESTMODE\r
+\r
+struct test {\r
+    const char *wildcard;\r
+    const char *target;\r
+    int expected_result;\r
+};\r
+\r
+const struct test fragment_tests[] = {\r
+    /*\r
+     * We exhaustively unit-test the fragment matching routine\r
+     * itself, which should save us the need to test all its\r
+     * intricacies during the full wildcard tests.\r
+     */\r
+    {"abc", "abc", 1},\r
+    {"abc", "abd", 0},\r
+    {"abc", "abcd", 1},\r
+    {"abcd", "abc", 0},\r
+    {"ab[cd]", "abc", 1},\r
+    {"ab[cd]", "abd", 1},\r
+    {"ab[cd]", "abe", 0},\r
+    {"ab[^cd]", "abc", 0},\r
+    {"ab[^cd]", "abd", 0},\r
+    {"ab[^cd]", "abe", 1},\r
+    {"ab\\", "abc", -WC_TRAILINGBACKSLASH},\r
+    {"ab\\*", "ab*", 1},\r
+    {"ab\\?", "ab*", 0},\r
+    {"ab?", "abc", 1},\r
+    {"ab?", "ab", 0},\r
+    {"ab[", "abc", -WC_UNCLOSEDCLASS},\r
+    {"ab[c-", "abb", -WC_UNCLOSEDCLASS},\r
+    {"ab[c-]", "abb", -WC_INVALIDRANGE},\r
+    {"ab[c-e]", "abb", 0},\r
+    {"ab[c-e]", "abc", 1},\r
+    {"ab[c-e]", "abd", 1},\r
+    {"ab[c-e]", "abe", 1},\r
+    {"ab[c-e]", "abf", 0},\r
+    {"ab[e-c]", "abb", 0},\r
+    {"ab[e-c]", "abc", 1},\r
+    {"ab[e-c]", "abd", 1},\r
+    {"ab[e-c]", "abe", 1},\r
+    {"ab[e-c]", "abf", 0},\r
+    {"ab[^c-e]", "abb", 1},\r
+    {"ab[^c-e]", "abc", 0},\r
+    {"ab[^c-e]", "abd", 0},\r
+    {"ab[^c-e]", "abe", 0},\r
+    {"ab[^c-e]", "abf", 1},\r
+    {"ab[^e-c]", "abb", 1},\r
+    {"ab[^e-c]", "abc", 0},\r
+    {"ab[^e-c]", "abd", 0},\r
+    {"ab[^e-c]", "abe", 0},\r
+    {"ab[^e-c]", "abf", 1},\r
+    {"ab[a^]", "aba", 1},\r
+    {"ab[a^]", "ab^", 1},\r
+    {"ab[a^]", "abb", 0},\r
+    {"ab[^a^]", "aba", 0},\r
+    {"ab[^a^]", "ab^", 0},\r
+    {"ab[^a^]", "abb", 1},\r
+    {"ab[-c]", "ab-", 1},\r
+    {"ab[-c]", "abc", 1},\r
+    {"ab[-c]", "abd", 0},\r
+    {"ab[^-c]", "ab-", 0},\r
+    {"ab[^-c]", "abc", 0},\r
+    {"ab[^-c]", "abd", 1},\r
+    {"ab[\\[-\\]]", "abZ", 0},\r
+    {"ab[\\[-\\]]", "ab[", 1},\r
+    {"ab[\\[-\\]]", "ab\\", 1},\r
+    {"ab[\\[-\\]]", "ab]", 1},\r
+    {"ab[\\[-\\]]", "ab^", 0},\r
+    {"ab[^\\[-\\]]", "abZ", 1},\r
+    {"ab[^\\[-\\]]", "ab[", 0},\r
+    {"ab[^\\[-\\]]", "ab\\", 0},\r
+    {"ab[^\\[-\\]]", "ab]", 0},\r
+    {"ab[^\\[-\\]]", "ab^", 1},\r
+    {"ab[a-fA-F]", "aba", 1},\r
+    {"ab[a-fA-F]", "abF", 1},\r
+    {"ab[a-fA-F]", "abZ", 0},\r
+};\r
+\r
+const struct test full_tests[] = {\r
+    {"a", "argh", 0},\r
+    {"a", "ba", 0},\r
+    {"a", "a", 1},\r
+    {"a*", "aardvark", 1},\r
+    {"a*", "badger", 0},\r
+    {"*a", "park", 0},\r
+    {"*a", "pArka", 1},\r
+    {"*a", "parka", 1},\r
+    {"*a*", "park", 1},\r
+    {"*a*", "perk", 0},\r
+    {"?b*r?", "abracadabra", 1},\r
+    {"?b*r?", "abracadabr", 0},\r
+    {"?b*r?", "abracadabzr", 0},\r
+};\r
+\r
+int main(void)\r
+{\r
+    int i;\r
+    int fails, passes;\r
+\r
+    fails = passes = 0;\r
+\r
+    for (i = 0; i < sizeof(fragment_tests)/sizeof(*fragment_tests); i++) {\r
+       const char *f, *t;\r
+       int eret, aret;\r
+       f = fragment_tests[i].wildcard;\r
+       t = fragment_tests[i].target;\r
+       eret = fragment_tests[i].expected_result;\r
+       aret = wc_match_fragment(&f, &t);\r
+       if (aret != eret) {\r
+           printf("failed test: /%s/ against /%s/ returned %d not %d\n",\r
+                  fragment_tests[i].wildcard, fragment_tests[i].target,\r
+                  aret, eret);\r
+           fails++;\r
+       } else\r
+           passes++;\r
+    }\r
+\r
+    for (i = 0; i < sizeof(full_tests)/sizeof(*full_tests); i++) {\r
+       const char *f, *t;\r
+       int eret, aret;\r
+       f = full_tests[i].wildcard;\r
+       t = full_tests[i].target;\r
+       eret = full_tests[i].expected_result;\r
+       aret = wc_match(f, t);\r
+       if (aret != eret) {\r
+           printf("failed test: /%s/ against /%s/ returned %d not %d\n",\r
+                  full_tests[i].wildcard, full_tests[i].target,\r
+                  aret, eret);\r
+           fails++;\r
+       } else\r
+           passes++;\r
+    }\r
+\r
+    printf("passed %d, failed %d\n", passes, fails);\r
+\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV b/putty/WINDOWS/DEVCPP/PAGEANT/PAGEANT.DEV
new file mode 100644 (file)
index 0000000..215755a
--- /dev/null
@@ -0,0 +1,401 @@
+# DEV-C++ 5 Project File - pageant.dev\r\r
+# ** DO NOT EDIT **\r\r
+\r\r
+[Project]\r\r
+FileName=pageant.dev\r\r
+Name=pageant\r\r
+Ver=1\r\r
+IsCpp=1\r\r
+Type=0\r\r
+Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx\r\r
+Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_\r\r
+Libs=\r\r
+UnitCount=35\r\r
+Folders="Header Files","Resource Files","Source Files"\r\r
+ObjFiles=\r\r
+PrivateResource=pageant_private.rc\r\r
+ResourceIncludes=..\..\..\WINDOWS\r\r
+MakeIncludes=\r\r
+Icon=\r\r
+ExeOutput=\r\r
+ObjectOutput=\r\r
+OverrideOutput=0\r\r
+OverrideOutputName=pageant.exe\r\r
+HostApplication=\r\r
+CommandLine=\r\r
+UseCustomMakefile=0\r\r
+CustomMakefile=\r\r
+IncludeVersionInfo=0\r\r
+SupportXPThemes=0\r\r
+CompilerSet=0\r\r
+CompilerSettings=0000000000000000000000\r\r
+\r\r
+[Unit1]\r\r
+FileName=..\..\..\misc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit2]\r\r
+FileName=..\..\..\sshaes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit3]\r\r
+FileName=..\..\..\sshbn.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit4]\r\r
+FileName=..\..\..\sshdes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit5]\r\r
+FileName=..\..\..\sshdss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit6]\r\r
+FileName=..\..\..\sshmd5.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit7]\r\r
+FileName=..\..\..\sshpubk.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit8]\r\r
+FileName=..\..\..\sshrsa.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit9]\r\r
+FileName=..\..\..\sshsh256.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit10]\r\r
+FileName=..\..\..\sshsh512.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit11]\r\r
+FileName=..\..\..\sshsha.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit12]\r\r
+FileName=..\..\..\tree234.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit13]\r\r
+FileName=..\..\..\version.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit14]\r\r
+FileName=..\..\..\windows\winhelp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit15]\r\r
+FileName=..\..\..\windows\winmisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit16]\r\r
+FileName=..\..\..\windows\winpgnt.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit17]\r\r
+FileName=..\..\..\windows\winpgntc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit18]\r\r
+FileName=..\..\..\windows\winutils.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit19]\r\r
+FileName=..\..\..\charset\charset.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit20]\r\r
+FileName=..\..\..\int64.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit21]\r\r
+FileName=..\..\..\macosx\osx.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit22]\r\r
+FileName=..\..\..\misc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit23]\r\r
+FileName=..\..\..\network.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit24]\r\r
+FileName=..\..\..\putty.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit25]\r\r
+FileName=..\..\..\puttymem.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit26]\r\r
+FileName=..\..\..\puttyps.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit27]\r\r
+FileName=..\..\..\ssh.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit28]\r\r
+FileName=..\..\..\tree234.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit29]\r\r
+FileName=..\..\..\unix\unix.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit30]\r\r
+FileName=..\..\..\windows\rcstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit31]\r\r
+FileName=..\..\..\windows\winhelp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit32]\r\r
+FileName=..\..\..\windows\winstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit33]\r\r
+FileName=..\..\..\windows\pageant.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit34]\r\r
+FileName=..\..\..\windows\pageant.rc\r\r
+Folder=Resource Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit35]\r\r
+FileName=..\..\..\windows\pageants.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[VersionInfo]\r\r
+Major=0\r\r
+Minor=0\r\r
+Release=1\r\r
+Build=1\r\r
+LanguageID=1033\r\r
+CharsetID=1252\r\r
+CompanyName=\r\r
+FileVersion=0.1\r\r
+FileDescription=\r\r
+InternalName=\r\r
+LegalCopyright=\r\r
+LegalTrademarks=\r\r
+OriginalFilename=pageant.exe\r\r
+ProductName=pageant\r\r
+ProductVersion=0.1\r\r
+AutoIncBuildNr=0\r\r
diff --git a/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV b/putty/WINDOWS/DEVCPP/PLINK/PLINK.DEV
new file mode 100644 (file)
index 0000000..a7f5a5b
--- /dev/null
@@ -0,0 +1,811 @@
+# DEV-C++ 5 Project File - plink.dev\r\r
+# ** DO NOT EDIT **\r\r
+\r\r
+[Project]\r\r
+FileName=plink.dev\r\r
+Name=plink\r\r
+Ver=1\r\r
+IsCpp=1\r\r
+Type=1\r\r
+Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx\r\r
+Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_\r\r
+Libs=\r\r
+UnitCount=76\r\r
+Folders="Header Files","Resource Files","Source Files"\r\r
+ObjFiles=\r\r
+PrivateResource=plink_private.rc\r\r
+ResourceIncludes=..\..\..\WINDOWS\r\r
+MakeIncludes=\r\r
+Icon=\r\r
+ExeOutput=\r\r
+ObjectOutput=\r\r
+OverrideOutput=0\r\r
+OverrideOutputName=plink.exe\r\r
+HostApplication=\r\r
+CommandLine=\r\r
+UseCustomMakefile=0\r\r
+CustomMakefile=\r\r
+IncludeVersionInfo=0\r\r
+SupportXPThemes=0\r\r
+CompilerSet=0\r\r
+CompilerSettings=0000000000000000000000\r\r
+\r\r
+[Unit1]\r\r
+FileName=..\..\..\be_all_s.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit2]\r\r
+FileName=..\..\..\cmdline.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit3]\r\r
+FileName=..\..\..\cproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit4]\r\r
+FileName=..\..\..\ldisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit5]\r\r
+FileName=..\..\..\logging.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit6]\r\r
+FileName=..\..\..\misc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit7]\r\r
+FileName=..\..\..\pgssapi.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit8]\r\r
+FileName=..\..\..\pinger.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit9]\r\r
+FileName=..\..\..\portfwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit10]\r\r
+FileName=..\..\..\proxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit11]\r\r
+FileName=..\..\..\raw.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit12]\r\r
+FileName=..\..\..\rlogin.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit13]\r\r
+FileName=..\..\..\settings.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit14]\r\r
+FileName=..\..\..\ssh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit15]\r\r
+FileName=..\..\..\sshaes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit16]\r\r
+FileName=..\..\..\ssharcf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit17]\r\r
+FileName=..\..\..\sshblowf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit18]\r\r
+FileName=..\..\..\sshbn.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit19]\r\r
+FileName=..\..\..\sshcrc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit20]\r\r
+FileName=..\..\..\sshcrcda.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit21]\r\r
+FileName=..\..\..\sshdes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit22]\r\r
+FileName=..\..\..\sshdh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit23]\r\r
+FileName=..\..\..\sshdss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit24]\r\r
+FileName=..\..\..\sshgssc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit25]\r\r
+FileName=..\..\..\sshmd5.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit26]\r\r
+FileName=..\..\..\sshpubk.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit27]\r\r
+FileName=..\..\..\sshrand.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit28]\r\r
+FileName=..\..\..\sshrsa.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit29]\r\r
+FileName=..\..\..\sshsh256.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit30]\r\r
+FileName=..\..\..\sshsh512.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit31]\r\r
+FileName=..\..\..\sshsha.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit32]\r\r
+FileName=..\..\..\sshzlib.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit33]\r\r
+FileName=..\..\..\telnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit34]\r\r
+FileName=..\..\..\timing.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit35]\r\r
+FileName=..\..\..\tree234.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit36]\r\r
+FileName=..\..\..\version.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit37]\r\r
+FileName=..\..\..\wildcard.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit38]\r\r
+FileName=..\..\..\windows\wincons.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit39]\r\r
+FileName=..\..\..\windows\windefs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit40]\r\r
+FileName=..\..\..\windows\wingss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit41]\r\r
+FileName=..\..\..\windows\winhandl.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit42]\r\r
+FileName=..\..\..\windows\winmisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit43]\r\r
+FileName=..\..\..\windows\winnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit44]\r\r
+FileName=..\..\..\windows\winnoise.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit45]\r\r
+FileName=..\..\..\windows\winnojmp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit46]\r\r
+FileName=..\..\..\windows\winpgntc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit47]\r\r
+FileName=..\..\..\windows\winplink.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit48]\r\r
+FileName=..\..\..\windows\winproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit49]\r\r
+FileName=..\..\..\windows\winser.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit50]\r\r
+FileName=..\..\..\windows\winstore.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit51]\r\r
+FileName=..\..\..\windows\wintime.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit52]\r\r
+FileName=..\..\..\windows\winx11.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit53]\r\r
+FileName=..\..\..\x11fwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit54]\r\r
+FileName=..\..\..\charset\charset.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit55]\r\r
+FileName=..\..\..\int64.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit56]\r\r
+FileName=..\..\..\ldisc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit57]\r\r
+FileName=..\..\..\macosx\osx.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit58]\r\r
+FileName=..\..\..\misc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit59]\r\r
+FileName=..\..\..\network.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit60]\r\r
+FileName=..\..\..\pgssapi.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit61]\r\r
+FileName=..\..\..\proxy.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit62]\r\r
+FileName=..\..\..\putty.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit63]\r\r
+FileName=..\..\..\puttymem.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit64]\r\r
+FileName=..\..\..\puttyps.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit65]\r\r
+FileName=..\..\..\ssh.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit66]\r\r
+FileName=..\..\..\sshgss.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit67]\r\r
+FileName=..\..\..\sshgssc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit68]\r\r
+FileName=..\..\..\storage.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit69]\r\r
+FileName=..\..\..\terminal.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit70]\r\r
+FileName=..\..\..\tree234.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit71]\r\r
+FileName=..\..\..\unix\unix.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit72]\r\r
+FileName=..\..\..\windows\rcstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit73]\r\r
+FileName=..\..\..\windows\winhelp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit74]\r\r
+FileName=..\..\..\windows\winstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit75]\r\r
+FileName=..\..\..\windows\plink.rc\r\r
+Folder=Resource Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit76]\r\r
+FileName=..\..\..\windows\putty.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[VersionInfo]\r\r
+Major=0\r\r
+Minor=0\r\r
+Release=1\r\r
+Build=1\r\r
+LanguageID=1033\r\r
+CharsetID=1252\r\r
+CompanyName=\r\r
+FileVersion=0.1\r\r
+FileDescription=\r\r
+InternalName=\r\r
+LegalCopyright=\r\r
+LegalTrademarks=\r\r
+OriginalFilename=plink.exe\r\r
+ProductName=plink\r\r
+ProductVersion=0.1\r\r
+AutoIncBuildNr=0\r\r
diff --git a/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV b/putty/WINDOWS/DEVCPP/PSCP/PSCP.DEV
new file mode 100644 (file)
index 0000000..8ae4c6a
--- /dev/null
@@ -0,0 +1,781 @@
+# DEV-C++ 5 Project File - pscp.dev\r\r
+# ** DO NOT EDIT **\r\r
+\r\r
+[Project]\r\r
+FileName=pscp.dev\r\r
+Name=pscp\r\r
+Ver=1\r\r
+IsCpp=1\r\r
+Type=1\r\r
+Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx\r\r
+Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_\r\r
+Libs=\r\r
+UnitCount=73\r\r
+Folders="Header Files","Resource Files","Source Files"\r\r
+ObjFiles=\r\r
+PrivateResource=pscp_private.rc\r\r
+ResourceIncludes=..\..\..\WINDOWS\r\r
+MakeIncludes=\r\r
+Icon=\r\r
+ExeOutput=\r\r
+ObjectOutput=\r\r
+OverrideOutput=0\r\r
+OverrideOutputName=pscp.exe\r\r
+HostApplication=\r\r
+CommandLine=\r\r
+UseCustomMakefile=0\r\r
+CustomMakefile=\r\r
+IncludeVersionInfo=0\r\r
+SupportXPThemes=0\r\r
+CompilerSet=0\r\r
+CompilerSettings=0000000000000000000000\r\r
+\r\r
+[Unit1]\r\r
+FileName=..\..\..\be_none.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit2]\r\r
+FileName=..\..\..\cmdline.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit3]\r\r
+FileName=..\..\..\cproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit4]\r\r
+FileName=..\..\..\int64.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit5]\r\r
+FileName=..\..\..\logging.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit6]\r\r
+FileName=..\..\..\misc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit7]\r\r
+FileName=..\..\..\pgssapi.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit8]\r\r
+FileName=..\..\..\pinger.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit9]\r\r
+FileName=..\..\..\portfwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit10]\r\r
+FileName=..\..\..\proxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit11]\r\r
+FileName=..\..\..\pscp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit12]\r\r
+FileName=..\..\..\settings.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit13]\r\r
+FileName=..\..\..\sftp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit14]\r\r
+FileName=..\..\..\ssh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit15]\r\r
+FileName=..\..\..\sshaes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit16]\r\r
+FileName=..\..\..\ssharcf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit17]\r\r
+FileName=..\..\..\sshblowf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit18]\r\r
+FileName=..\..\..\sshbn.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit19]\r\r
+FileName=..\..\..\sshcrc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit20]\r\r
+FileName=..\..\..\sshcrcda.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit21]\r\r
+FileName=..\..\..\sshdes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit22]\r\r
+FileName=..\..\..\sshdh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit23]\r\r
+FileName=..\..\..\sshdss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit24]\r\r
+FileName=..\..\..\sshgssc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit25]\r\r
+FileName=..\..\..\sshmd5.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit26]\r\r
+FileName=..\..\..\sshpubk.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit27]\r\r
+FileName=..\..\..\sshrand.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit28]\r\r
+FileName=..\..\..\sshrsa.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit29]\r\r
+FileName=..\..\..\sshsh256.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit30]\r\r
+FileName=..\..\..\sshsh512.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit31]\r\r
+FileName=..\..\..\sshsha.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit32]\r\r
+FileName=..\..\..\sshzlib.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit33]\r\r
+FileName=..\..\..\timing.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit34]\r\r
+FileName=..\..\..\tree234.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit35]\r\r
+FileName=..\..\..\version.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit36]\r\r
+FileName=..\..\..\wildcard.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit37]\r\r
+FileName=..\..\..\windows\wincons.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit38]\r\r
+FileName=..\..\..\windows\windefs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit39]\r\r
+FileName=..\..\..\windows\wingss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit40]\r\r
+FileName=..\..\..\windows\winhandl.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit41]\r\r
+FileName=..\..\..\windows\winmisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit42]\r\r
+FileName=..\..\..\windows\winnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit43]\r\r
+FileName=..\..\..\windows\winnoise.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit44]\r\r
+FileName=..\..\..\windows\winnojmp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit45]\r\r
+FileName=..\..\..\windows\winpgntc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit46]\r\r
+FileName=..\..\..\windows\winproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit47]\r\r
+FileName=..\..\..\windows\winsftp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit48]\r\r
+FileName=..\..\..\windows\winstore.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit49]\r\r
+FileName=..\..\..\windows\wintime.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit50]\r\r
+FileName=..\..\..\x11fwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit51]\r\r
+FileName=..\..\..\charset\charset.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit52]\r\r
+FileName=..\..\..\int64.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit53]\r\r
+FileName=..\..\..\macosx\osx.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit54]\r\r
+FileName=..\..\..\misc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit55]\r\r
+FileName=..\..\..\network.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit56]\r\r
+FileName=..\..\..\pgssapi.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit57]\r\r
+FileName=..\..\..\proxy.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit58]\r\r
+FileName=..\..\..\psftp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit59]\r\r
+FileName=..\..\..\putty.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit60]\r\r
+FileName=..\..\..\puttymem.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit61]\r\r
+FileName=..\..\..\puttyps.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit62]\r\r
+FileName=..\..\..\sftp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit63]\r\r
+FileName=..\..\..\ssh.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit64]\r\r
+FileName=..\..\..\sshgss.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit65]\r\r
+FileName=..\..\..\sshgssc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit66]\r\r
+FileName=..\..\..\storage.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit67]\r\r
+FileName=..\..\..\tree234.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit68]\r\r
+FileName=..\..\..\unix\unix.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit69]\r\r
+FileName=..\..\..\windows\rcstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit70]\r\r
+FileName=..\..\..\windows\winhelp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit71]\r\r
+FileName=..\..\..\windows\winstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit72]\r\r
+FileName=..\..\..\windows\pscp.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit73]\r\r
+FileName=..\..\..\windows\pscp.rc\r\r
+Folder=Resource Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[VersionInfo]\r\r
+Major=0\r\r
+Minor=0\r\r
+Release=1\r\r
+Build=1\r\r
+LanguageID=1033\r\r
+CharsetID=1252\r\r
+CompanyName=\r\r
+FileVersion=0.1\r\r
+FileDescription=\r\r
+InternalName=\r\r
+LegalCopyright=\r\r
+LegalTrademarks=\r\r
+OriginalFilename=pscp.exe\r\r
+ProductName=pscp\r\r
+ProductVersion=0.1\r\r
+AutoIncBuildNr=0\r\r
diff --git a/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV b/putty/WINDOWS/DEVCPP/PSFTP/PSFTP.DEV
new file mode 100644 (file)
index 0000000..a4ab2d3
--- /dev/null
@@ -0,0 +1,781 @@
+# DEV-C++ 5 Project File - psftp.dev\r\r
+# ** DO NOT EDIT **\r\r
+\r\r
+[Project]\r\r
+FileName=psftp.dev\r\r
+Name=psftp\r\r
+Ver=1\r\r
+IsCpp=1\r\r
+Type=1\r\r
+Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx\r\r
+Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_\r\r
+Libs=\r\r
+UnitCount=73\r\r
+Folders="Header Files","Resource Files","Source Files"\r\r
+ObjFiles=\r\r
+PrivateResource=psftp_private.rc\r\r
+ResourceIncludes=..\..\..\WINDOWS\r\r
+MakeIncludes=\r\r
+Icon=\r\r
+ExeOutput=\r\r
+ObjectOutput=\r\r
+OverrideOutput=0\r\r
+OverrideOutputName=psftp.exe\r\r
+HostApplication=\r\r
+CommandLine=\r\r
+UseCustomMakefile=0\r\r
+CustomMakefile=\r\r
+IncludeVersionInfo=0\r\r
+SupportXPThemes=0\r\r
+CompilerSet=0\r\r
+CompilerSettings=0000000000000000000000\r\r
+\r\r
+[Unit1]\r\r
+FileName=..\..\..\be_none.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit2]\r\r
+FileName=..\..\..\cmdline.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit3]\r\r
+FileName=..\..\..\cproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit4]\r\r
+FileName=..\..\..\int64.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit5]\r\r
+FileName=..\..\..\logging.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit6]\r\r
+FileName=..\..\..\misc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit7]\r\r
+FileName=..\..\..\pgssapi.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit8]\r\r
+FileName=..\..\..\pinger.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit9]\r\r
+FileName=..\..\..\portfwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit10]\r\r
+FileName=..\..\..\proxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit11]\r\r
+FileName=..\..\..\psftp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit12]\r\r
+FileName=..\..\..\settings.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit13]\r\r
+FileName=..\..\..\sftp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit14]\r\r
+FileName=..\..\..\ssh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit15]\r\r
+FileName=..\..\..\sshaes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit16]\r\r
+FileName=..\..\..\ssharcf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit17]\r\r
+FileName=..\..\..\sshblowf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit18]\r\r
+FileName=..\..\..\sshbn.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit19]\r\r
+FileName=..\..\..\sshcrc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit20]\r\r
+FileName=..\..\..\sshcrcda.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit21]\r\r
+FileName=..\..\..\sshdes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit22]\r\r
+FileName=..\..\..\sshdh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit23]\r\r
+FileName=..\..\..\sshdss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit24]\r\r
+FileName=..\..\..\sshgssc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit25]\r\r
+FileName=..\..\..\sshmd5.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit26]\r\r
+FileName=..\..\..\sshpubk.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit27]\r\r
+FileName=..\..\..\sshrand.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit28]\r\r
+FileName=..\..\..\sshrsa.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit29]\r\r
+FileName=..\..\..\sshsh256.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit30]\r\r
+FileName=..\..\..\sshsh512.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit31]\r\r
+FileName=..\..\..\sshsha.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit32]\r\r
+FileName=..\..\..\sshzlib.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit33]\r\r
+FileName=..\..\..\timing.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit34]\r\r
+FileName=..\..\..\tree234.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit35]\r\r
+FileName=..\..\..\version.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit36]\r\r
+FileName=..\..\..\wildcard.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit37]\r\r
+FileName=..\..\..\windows\wincons.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit38]\r\r
+FileName=..\..\..\windows\windefs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit39]\r\r
+FileName=..\..\..\windows\wingss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit40]\r\r
+FileName=..\..\..\windows\winhandl.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit41]\r\r
+FileName=..\..\..\windows\winmisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit42]\r\r
+FileName=..\..\..\windows\winnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit43]\r\r
+FileName=..\..\..\windows\winnoise.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit44]\r\r
+FileName=..\..\..\windows\winnojmp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit45]\r\r
+FileName=..\..\..\windows\winpgntc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit46]\r\r
+FileName=..\..\..\windows\winproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit47]\r\r
+FileName=..\..\..\windows\winsftp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit48]\r\r
+FileName=..\..\..\windows\winstore.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit49]\r\r
+FileName=..\..\..\windows\wintime.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit50]\r\r
+FileName=..\..\..\x11fwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit51]\r\r
+FileName=..\..\..\charset\charset.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit52]\r\r
+FileName=..\..\..\int64.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit53]\r\r
+FileName=..\..\..\macosx\osx.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit54]\r\r
+FileName=..\..\..\misc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit55]\r\r
+FileName=..\..\..\network.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit56]\r\r
+FileName=..\..\..\pgssapi.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit57]\r\r
+FileName=..\..\..\proxy.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit58]\r\r
+FileName=..\..\..\psftp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit59]\r\r
+FileName=..\..\..\putty.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit60]\r\r
+FileName=..\..\..\puttymem.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit61]\r\r
+FileName=..\..\..\puttyps.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit62]\r\r
+FileName=..\..\..\sftp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit63]\r\r
+FileName=..\..\..\ssh.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit64]\r\r
+FileName=..\..\..\sshgss.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit65]\r\r
+FileName=..\..\..\sshgssc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit66]\r\r
+FileName=..\..\..\storage.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit67]\r\r
+FileName=..\..\..\tree234.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit68]\r\r
+FileName=..\..\..\unix\unix.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit69]\r\r
+FileName=..\..\..\windows\rcstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit70]\r\r
+FileName=..\..\..\windows\winhelp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit71]\r\r
+FileName=..\..\..\windows\winstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit72]\r\r
+FileName=..\..\..\windows\pscp.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit73]\r\r
+FileName=..\..\..\windows\psftp.rc\r\r
+Folder=Resource Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[VersionInfo]\r\r
+Major=0\r\r
+Minor=0\r\r
+Release=1\r\r
+Build=1\r\r
+LanguageID=1033\r\r
+CharsetID=1252\r\r
+CompanyName=\r\r
+FileVersion=0.1\r\r
+FileDescription=\r\r
+InternalName=\r\r
+LegalCopyright=\r\r
+LegalTrademarks=\r\r
+OriginalFilename=psftp.exe\r\r
+ProductName=psftp\r\r
+ProductVersion=0.1\r\r
+AutoIncBuildNr=0\r\r
diff --git a/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV b/putty/WINDOWS/DEVCPP/PUTTY/PUTTY.DEV
new file mode 100644 (file)
index 0000000..a8cad26
--- /dev/null
@@ -0,0 +1,981 @@
+# DEV-C++ 5 Project File - putty.dev\r\r
+# ** DO NOT EDIT **\r\r
+\r\r
+[Project]\r\r
+FileName=putty.dev\r\r
+Name=putty\r\r
+Ver=1\r\r
+IsCpp=1\r\r
+Type=0\r\r
+Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx\r\r
+Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_\r\r
+Libs=\r\r
+UnitCount=93\r\r
+Folders="Header Files","Resource Files","Source Files"\r\r
+ObjFiles=\r\r
+PrivateResource=putty_private.rc\r\r
+ResourceIncludes=..\..\..\WINDOWS\r\r
+MakeIncludes=\r\r
+Icon=\r\r
+ExeOutput=\r\r
+ObjectOutput=\r\r
+OverrideOutput=0\r\r
+OverrideOutputName=putty.exe\r\r
+HostApplication=\r\r
+CommandLine=\r\r
+UseCustomMakefile=0\r\r
+CustomMakefile=\r\r
+IncludeVersionInfo=0\r\r
+SupportXPThemes=0\r\r
+CompilerSet=0\r\r
+CompilerSettings=0000000000000000000000\r\r
+\r\r
+[Unit1]\r\r
+FileName=..\..\..\be_all_s.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit2]\r\r
+FileName=..\..\..\cmdline.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit3]\r\r
+FileName=..\..\..\config.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit4]\r\r
+FileName=..\..\..\cproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit5]\r\r
+FileName=..\..\..\dialog.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit6]\r\r
+FileName=..\..\..\ldisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit7]\r\r
+FileName=..\..\..\ldiscucs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit8]\r\r
+FileName=..\..\..\logging.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit9]\r\r
+FileName=..\..\..\minibidi.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit10]\r\r
+FileName=..\..\..\misc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit11]\r\r
+FileName=..\..\..\pgssapi.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit12]\r\r
+FileName=..\..\..\pinger.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit13]\r\r
+FileName=..\..\..\portfwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit14]\r\r
+FileName=..\..\..\proxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit15]\r\r
+FileName=..\..\..\raw.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit16]\r\r
+FileName=..\..\..\rlogin.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit17]\r\r
+FileName=..\..\..\sercfg.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit18]\r\r
+FileName=..\..\..\settings.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit19]\r\r
+FileName=..\..\..\ssh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit20]\r\r
+FileName=..\..\..\sshaes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit21]\r\r
+FileName=..\..\..\ssharcf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit22]\r\r
+FileName=..\..\..\sshblowf.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit23]\r\r
+FileName=..\..\..\sshbn.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit24]\r\r
+FileName=..\..\..\sshcrc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit25]\r\r
+FileName=..\..\..\sshcrcda.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit26]\r\r
+FileName=..\..\..\sshdes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit27]\r\r
+FileName=..\..\..\sshdh.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit28]\r\r
+FileName=..\..\..\sshdss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit29]\r\r
+FileName=..\..\..\sshgssc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit30]\r\r
+FileName=..\..\..\sshmd5.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit31]\r\r
+FileName=..\..\..\sshpubk.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit32]\r\r
+FileName=..\..\..\sshrand.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit33]\r\r
+FileName=..\..\..\sshrsa.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit34]\r\r
+FileName=..\..\..\sshsh256.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit35]\r\r
+FileName=..\..\..\sshsh512.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit36]\r\r
+FileName=..\..\..\sshsha.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit37]\r\r
+FileName=..\..\..\sshzlib.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit38]\r\r
+FileName=..\..\..\telnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit39]\r\r
+FileName=..\..\..\terminal.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit40]\r\r
+FileName=..\..\..\timing.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit41]\r\r
+FileName=..\..\..\tree234.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit42]\r\r
+FileName=..\..\..\version.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit43]\r\r
+FileName=..\..\..\wcwidth.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit44]\r\r
+FileName=..\..\..\wildcard.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit45]\r\r
+FileName=..\..\..\windows\sizetip.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit46]\r\r
+FileName=..\..\..\windows\wincfg.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit47]\r\r
+FileName=..\..\..\windows\winctrls.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit48]\r\r
+FileName=..\..\..\windows\windefs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit49]\r\r
+FileName=..\..\..\windows\windlg.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit50]\r\r
+FileName=..\..\..\windows\window.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit51]\r\r
+FileName=..\..\..\windows\wingss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit52]\r\r
+FileName=..\..\..\windows\winhandl.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit53]\r\r
+FileName=..\..\..\windows\winhelp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit54]\r\r
+FileName=..\..\..\windows\winjump.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit55]\r\r
+FileName=..\..\..\windows\winmisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit56]\r\r
+FileName=..\..\..\windows\winnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit57]\r\r
+FileName=..\..\..\windows\winnoise.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit58]\r\r
+FileName=..\..\..\windows\winpgntc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit59]\r\r
+FileName=..\..\..\windows\winprint.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit60]\r\r
+FileName=..\..\..\windows\winproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit61]\r\r
+FileName=..\..\..\windows\winser.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit62]\r\r
+FileName=..\..\..\windows\winstore.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit63]\r\r
+FileName=..\..\..\windows\wintime.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit64]\r\r
+FileName=..\..\..\windows\winucs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit65]\r\r
+FileName=..\..\..\windows\winutils.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit66]\r\r
+FileName=..\..\..\windows\winx11.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit67]\r\r
+FileName=..\..\..\x11fwd.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit68]\r\r
+FileName=..\..\..\charset\charset.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit69]\r\r
+FileName=..\..\..\dialog.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit70]\r\r
+FileName=..\..\..\int64.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit71]\r\r
+FileName=..\..\..\ldisc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit72]\r\r
+FileName=..\..\..\macosx\osx.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit73]\r\r
+FileName=..\..\..\misc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit74]\r\r
+FileName=..\..\..\network.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit75]\r\r
+FileName=..\..\..\pgssapi.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit76]\r\r
+FileName=..\..\..\proxy.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit77]\r\r
+FileName=..\..\..\putty.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit78]\r\r
+FileName=..\..\..\puttymem.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit79]\r\r
+FileName=..\..\..\puttyps.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit80]\r\r
+FileName=..\..\..\ssh.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit81]\r\r
+FileName=..\..\..\sshgss.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit82]\r\r
+FileName=..\..\..\sshgssc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit83]\r\r
+FileName=..\..\..\storage.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit84]\r\r
+FileName=..\..\..\terminal.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit85]\r\r
+FileName=..\..\..\tree234.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit86]\r\r
+FileName=..\..\..\unix\unix.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit87]\r\r
+FileName=..\..\..\windows\rcstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit88]\r\r
+FileName=..\..\..\windows\win_res.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit89]\r\r
+FileName=..\..\..\windows\winhelp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit90]\r\r
+FileName=..\..\..\windows\winstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit91]\r\r
+FileName=..\..\..\windows\putty.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit92]\r\r
+FileName=..\..\..\windows\putty.rc\r\r
+Folder=Resource Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit93]\r\r
+FileName=..\..\..\windows\puttycfg.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[VersionInfo]\r\r
+Major=0\r\r
+Minor=0\r\r
+Release=1\r\r
+Build=1\r\r
+LanguageID=1033\r\r
+CharsetID=1252\r\r
+CompanyName=\r\r
+FileVersion=0.1\r\r
+FileDescription=\r\r
+InternalName=\r\r
+LegalCopyright=\r\r
+LegalTrademarks=\r\r
+OriginalFilename=putty.exe\r\r
+ProductName=putty\r\r
+ProductVersion=0.1\r\r
+AutoIncBuildNr=0\r\r
diff --git a/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV b/putty/WINDOWS/DEVCPP/PUTTYGEN/PUTTYGEN.DEV
new file mode 100644 (file)
index 0000000..7d1ec3c
--- /dev/null
@@ -0,0 +1,511 @@
+# DEV-C++ 5 Project File - puttygen.dev\r\r
+# ** DO NOT EDIT **\r\r
+\r\r
+[Project]\r\r
+FileName=puttygen.dev\r\r
+Name=puttygen\r\r
+Ver=1\r\r
+IsCpp=1\r\r
+Type=0\r\r
+Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx\r\r
+Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_\r\r
+Libs=\r\r
+UnitCount=46\r\r
+Folders="Header Files","Resource Files","Source Files"\r\r
+ObjFiles=\r\r
+PrivateResource=puttygen_private.rc\r\r
+ResourceIncludes=..\..\..\WINDOWS\r\r
+MakeIncludes=\r\r
+Icon=\r\r
+ExeOutput=\r\r
+ObjectOutput=\r\r
+OverrideOutput=0\r\r
+OverrideOutputName=puttygen.exe\r\r
+HostApplication=\r\r
+CommandLine=\r\r
+UseCustomMakefile=0\r\r
+CustomMakefile=\r\r
+IncludeVersionInfo=0\r\r
+SupportXPThemes=0\r\r
+CompilerSet=0\r\r
+CompilerSettings=0000000000000000000000\r\r
+\r\r
+[Unit1]\r\r
+FileName=..\..\..\import.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit2]\r\r
+FileName=..\..\..\misc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit3]\r\r
+FileName=..\..\..\notiming.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit4]\r\r
+FileName=..\..\..\sshaes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit5]\r\r
+FileName=..\..\..\sshbn.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit6]\r\r
+FileName=..\..\..\sshdes.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit7]\r\r
+FileName=..\..\..\sshdss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit8]\r\r
+FileName=..\..\..\sshdssg.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit9]\r\r
+FileName=..\..\..\sshmd5.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit10]\r\r
+FileName=..\..\..\sshprime.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit11]\r\r
+FileName=..\..\..\sshpubk.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit12]\r\r
+FileName=..\..\..\sshrand.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit13]\r\r
+FileName=..\..\..\sshrsa.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit14]\r\r
+FileName=..\..\..\sshrsag.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit15]\r\r
+FileName=..\..\..\sshsh256.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit16]\r\r
+FileName=..\..\..\sshsh512.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit17]\r\r
+FileName=..\..\..\sshsha.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit18]\r\r
+FileName=..\..\..\tree234.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit19]\r\r
+FileName=..\..\..\version.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit20]\r\r
+FileName=..\..\..\windows\winctrls.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit21]\r\r
+FileName=..\..\..\windows\winhelp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit22]\r\r
+FileName=..\..\..\windows\winmisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit23]\r\r
+FileName=..\..\..\windows\winnoise.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit24]\r\r
+FileName=..\..\..\windows\winnojmp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit25]\r\r
+FileName=..\..\..\windows\winpgen.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit26]\r\r
+FileName=..\..\..\windows\winstore.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit27]\r\r
+FileName=..\..\..\windows\wintime.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit28]\r\r
+FileName=..\..\..\windows\winutils.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit29]\r\r
+FileName=..\..\..\charset\charset.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit30]\r\r
+FileName=..\..\..\dialog.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit31]\r\r
+FileName=..\..\..\int64.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit32]\r\r
+FileName=..\..\..\macosx\osx.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit33]\r\r
+FileName=..\..\..\misc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit34]\r\r
+FileName=..\..\..\network.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit35]\r\r
+FileName=..\..\..\putty.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit36]\r\r
+FileName=..\..\..\puttymem.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit37]\r\r
+FileName=..\..\..\puttyps.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit38]\r\r
+FileName=..\..\..\ssh.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit39]\r\r
+FileName=..\..\..\storage.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit40]\r\r
+FileName=..\..\..\tree234.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit41]\r\r
+FileName=..\..\..\unix\unix.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit42]\r\r
+FileName=..\..\..\windows\rcstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit43]\r\r
+FileName=..\..\..\windows\winhelp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit44]\r\r
+FileName=..\..\..\windows\winstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit45]\r\r
+FileName=..\..\..\windows\puttygen.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit46]\r\r
+FileName=..\..\..\windows\puttygen.rc\r\r
+Folder=Resource Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[VersionInfo]\r\r
+Major=0\r\r
+Minor=0\r\r
+Release=1\r\r
+Build=1\r\r
+LanguageID=1033\r\r
+CharsetID=1252\r\r
+CompanyName=\r\r
+FileVersion=0.1\r\r
+FileDescription=\r\r
+InternalName=\r\r
+LegalCopyright=\r\r
+LegalTrademarks=\r\r
+OriginalFilename=puttygen.exe\r\r
+ProductName=puttygen\r\r
+ProductVersion=0.1\r\r
+AutoIncBuildNr=0\r\r
diff --git a/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV b/putty/WINDOWS/DEVCPP/PUTTYTEL/PUTTYTEL.DEV
new file mode 100644 (file)
index 0000000..6da9f45
--- /dev/null
@@ -0,0 +1,691 @@
+# DEV-C++ 5 Project File - puttytel.dev\r\r
+# ** DO NOT EDIT **\r\r
+\r\r
+[Project]\r\r
+FileName=puttytel.dev\r\r
+Name=puttytel\r\r
+Ver=1\r\r
+IsCpp=1\r\r
+Type=0\r\r
+Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_\r\r
+Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix;..\..\..\macosx\r\r
+Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_\r\r
+Libs=\r\r
+UnitCount=64\r\r
+Folders="Header Files","Resource Files","Source Files"\r\r
+ObjFiles=\r\r
+PrivateResource=puttytel_private.rc\r\r
+ResourceIncludes=..\..\..\WINDOWS\r\r
+MakeIncludes=\r\r
+Icon=\r\r
+ExeOutput=\r\r
+ObjectOutput=\r\r
+OverrideOutput=0\r\r
+OverrideOutputName=puttytel.exe\r\r
+HostApplication=\r\r
+CommandLine=\r\r
+UseCustomMakefile=0\r\r
+CustomMakefile=\r\r
+IncludeVersionInfo=0\r\r
+SupportXPThemes=0\r\r
+CompilerSet=0\r\r
+CompilerSettings=0000000000000000000000\r\r
+\r\r
+[Unit1]\r\r
+FileName=..\..\..\be_nos_s.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit2]\r\r
+FileName=..\..\..\cmdline.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit3]\r\r
+FileName=..\..\..\config.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit4]\r\r
+FileName=..\..\..\dialog.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit5]\r\r
+FileName=..\..\..\ldisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit6]\r\r
+FileName=..\..\..\ldiscucs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit7]\r\r
+FileName=..\..\..\logging.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit8]\r\r
+FileName=..\..\..\minibidi.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit9]\r\r
+FileName=..\..\..\misc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit10]\r\r
+FileName=..\..\..\nocproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit11]\r\r
+FileName=..\..\..\nogss.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit12]\r\r
+FileName=..\..\..\pinger.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit13]\r\r
+FileName=..\..\..\proxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit14]\r\r
+FileName=..\..\..\raw.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit15]\r\r
+FileName=..\..\..\rlogin.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit16]\r\r
+FileName=..\..\..\sercfg.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit17]\r\r
+FileName=..\..\..\settings.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit18]\r\r
+FileName=..\..\..\telnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit19]\r\r
+FileName=..\..\..\terminal.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit20]\r\r
+FileName=..\..\..\timing.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit21]\r\r
+FileName=..\..\..\tree234.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit22]\r\r
+FileName=..\..\..\version.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit23]\r\r
+FileName=..\..\..\wcwidth.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit24]\r\r
+FileName=..\..\..\windows\sizetip.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit25]\r\r
+FileName=..\..\..\windows\wincfg.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit26]\r\r
+FileName=..\..\..\windows\winctrls.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit27]\r\r
+FileName=..\..\..\windows\windefs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit28]\r\r
+FileName=..\..\..\windows\windlg.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit29]\r\r
+FileName=..\..\..\windows\window.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit30]\r\r
+FileName=..\..\..\windows\winhandl.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit31]\r\r
+FileName=..\..\..\windows\winhelp.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit32]\r\r
+FileName=..\..\..\windows\winjump.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit33]\r\r
+FileName=..\..\..\windows\winmisc.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit34]\r\r
+FileName=..\..\..\windows\winnet.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit35]\r\r
+FileName=..\..\..\windows\winprint.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit36]\r\r
+FileName=..\..\..\windows\winproxy.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit37]\r\r
+FileName=..\..\..\windows\winser.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit38]\r\r
+FileName=..\..\..\windows\winstore.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit39]\r\r
+FileName=..\..\..\windows\wintime.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit40]\r\r
+FileName=..\..\..\windows\winucs.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit41]\r\r
+FileName=..\..\..\windows\winutils.c\r\r
+Folder=Source Files\r\r
+Compile=1\r\r
+CompileCpp=0\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit42]\r\r
+FileName=..\..\..\charset\charset.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit43]\r\r
+FileName=..\..\..\dialog.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit44]\r\r
+FileName=..\..\..\int64.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit45]\r\r
+FileName=..\..\..\ldisc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit46]\r\r
+FileName=..\..\..\macosx\osx.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit47]\r\r
+FileName=..\..\..\misc.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit48]\r\r
+FileName=..\..\..\network.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit49]\r\r
+FileName=..\..\..\proxy.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit50]\r\r
+FileName=..\..\..\putty.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit51]\r\r
+FileName=..\..\..\puttymem.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit52]\r\r
+FileName=..\..\..\puttyps.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit53]\r\r
+FileName=..\..\..\ssh.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit54]\r\r
+FileName=..\..\..\storage.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit55]\r\r
+FileName=..\..\..\terminal.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit56]\r\r
+FileName=..\..\..\tree234.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit57]\r\r
+FileName=..\..\..\unix\unix.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit58]\r\r
+FileName=..\..\..\windows\rcstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit59]\r\r
+FileName=..\..\..\windows\win_res.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit60]\r\r
+FileName=..\..\..\windows\winhelp.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit61]\r\r
+FileName=..\..\..\windows\winstuff.h\r\r
+Folder=Header Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=1\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit62]\r\r
+FileName=..\..\..\windows\putty.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit63]\r\r
+FileName=..\..\..\windows\puttycfg.ico\r\r
+Folder=Resource Files\r\r
+Compile=0\r\r
+CompileCpp=0\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[Unit64]\r\r
+FileName=..\..\..\windows\puttytel.rc\r\r
+Folder=Resource Files\r\r
+Compile=1\r\r
+CompileCpp=1\r\r
+Link=0\r\r
+Priority=1000\r\r
+OverrideBuildCmd=0\r\r
+BuildCmd=\r\r
+\r\r
+[VersionInfo]\r\r
+Major=0\r\r
+Minor=0\r\r
+Release=1\r\r
+Build=1\r\r
+LanguageID=1033\r\r
+CharsetID=1252\r\r
+CompanyName=\r\r
+FileVersion=0.1\r\r
+FileDescription=\r\r
+InternalName=\r\r
+LegalCopyright=\r\r
+LegalTrademarks=\r\r
+OriginalFilename=puttytel.exe\r\r
+ProductName=puttytel\r\r
+ProductVersion=0.1\r\r
+AutoIncBuildNr=0\r\r
diff --git a/putty/WINDOWS/MAKEFILE.BOR b/putty/WINDOWS/MAKEFILE.BOR
new file mode 100644 (file)
index 0000000..0e6e1f6
--- /dev/null
@@ -0,0 +1,804 @@
+# Makefile for putty under Borland C.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=-DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=-DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=-DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=-DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=-DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=-DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=-DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=-DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=-DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=-DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=-DMSVC4 (Windows only)\r
+#  - RCFL=-DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON.\r
+#\r
+#  - RCFL=-DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=-DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=-DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=-DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=-DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+\r
+# If you rename this file to `Makefile', you should change this line,\r
+# so that the .rsp files still depend on the correct makefile.\r
+MAKEFILE = Makefile.bor\r
+\r
+# C compilation flags\r
+CFLAGS = -D_WINDOWS -DWINVER=0x0500\r
+# Resource compilation flags\r
+RCFLAGS = -DNO_WINRESRC_H -DWIN32 -D_WIN32 -DWINVER=0x0401\r
+\r
+# Get include directory for resource compiler\r
+!if !$d(BCB)\r
+BCB = $(MAKEDIR)\..\r
+!endif\r
+\r
+# Borland doesn't support +=. This probably shouldn't work, but seems to.\r
+RCFLAGS = $(RCFLAGS) $(VER)\r
+\r
+.c.obj:\r
+       bcc32 -w-aus -w-ccc -w-par -w-pia $(COMPAT) $(CFLAGS) $(XFLAGS) \\r
+               -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ -I..\macosx/ \\r
+               /c $*.c\r
+.rc.res:\r
+       brcc32 $(RCFL) -i $(BCB)\include -r $(RCFLAGS) $*.rc\r
+\r
+all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \\r
+               puttytel.exe\r
+\r
+pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \\r
+               sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \\r
+               winpgnt.obj winpgntc.obj winutils.obj pageant.rsp\r
+       ilink32 -aa -Gn -L$(BCB)\lib @pageant.rsp\r
+\r
+plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \\r
+               misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \\r
+               proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \\r
+               version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \\r
+               winhandl.obj winmisc.obj winnet.obj winnoise.obj \\r
+               winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \\r
+               winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj \\r
+               plink.rsp\r
+       ilink32 -ap -Gn -L$(BCB)\lib @plink.rsp\r
+\r
+pscp.exe: be_none.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \\r
+               pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \\r
+               wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winnojmp.obj \\r
+               winpgntc.obj winproxy.obj winsftp.obj winstore.obj \\r
+               wintime.obj x11fwd.obj pscp.rsp\r
+       ilink32 -ap -Gn -L$(BCB)\lib @pscp.rsp\r
+\r
+psftp.exe: be_none.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \\r
+               psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \\r
+               wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winnojmp.obj \\r
+               winpgntc.obj winproxy.obj winsftp.obj winstore.obj \\r
+               wintime.obj x11fwd.obj psftp.rsp\r
+       ilink32 -ap -Gn -L$(BCB)\lib @psftp.rsp\r
+\r
+putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \\r
+               ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \\r
+               raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \\r
+               ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \\r
+               sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \\r
+               sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \\r
+               sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \\r
+               terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \\r
+               wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \\r
+               window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winpgntc.obj \\r
+               winprint.obj winproxy.obj winser.obj winstore.obj \\r
+               wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj \\r
+               putty.rsp\r
+       ilink32 -aa -Gn -L$(BCB)\lib @putty.rsp\r
+\r
+puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \\r
+               sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \\r
+               sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \\r
+               sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \\r
+               winctrls.obj winhelp.obj winmisc.obj winnoise.obj \\r
+               winnojmp.obj winpgen.obj winstore.obj wintime.obj \\r
+               winutils.obj puttygen.rsp\r
+       ilink32 -aa -Gn -L$(BCB)\lib @puttygen.rsp\r
+\r
+puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \\r
+               ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \\r
+               nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \\r
+               rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \\r
+               terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \\r
+               wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \\r
+               winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \\r
+               winprint.obj winproxy.obj winser.obj winstore.obj \\r
+               wintime.obj winucs.obj winutils.obj puttytel.rsp\r
+       ilink32 -aa -Gn -L$(BCB)\lib @puttytel.rsp\r
+\r
+pageant.rsp: $(MAKEFILE)\r
+       echo c0w32 + > pageant.rsp\r
+       echo misc.obj sshaes.obj sshbn.obj sshdes.obj + >> pageant.rsp\r
+       echo sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj + >> pageant.rsp\r
+       echo sshsh256.obj sshsh512.obj sshsha.obj tree234.obj + >> pageant.rsp\r
+       echo version.obj winhelp.obj winmisc.obj winpgnt.obj + >> pageant.rsp\r
+       echo winpgntc.obj winutils.obj >> pageant.rsp\r
+       echo pageant.exe >> pageant.rsp\r
+       echo nul,cw32 import32 ole32, >> pageant.rsp\r
+       echo pageant.res >> pageant.rsp\r
+\r
+plink.rsp: $(MAKEFILE)\r
+       echo c0x32 + > plink.rsp\r
+       echo be_all_s.obj cmdline.obj cproxy.obj ldisc.obj + >> plink.rsp\r
+       echo logging.obj misc.obj pgssapi.obj pinger.obj + >> plink.rsp\r
+       echo portfwd.obj proxy.obj raw.obj rlogin.obj + >> plink.rsp\r
+       echo settings.obj ssh.obj sshaes.obj ssharcf.obj + >> plink.rsp\r
+       echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> plink.rsp\r
+       echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> plink.rsp\r
+       echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> plink.rsp\r
+       echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> plink.rsp\r
+       echo telnet.obj timing.obj tree234.obj version.obj + >> plink.rsp\r
+       echo wildcard.obj wincons.obj windefs.obj wingss.obj + >> plink.rsp\r
+       echo winhandl.obj winmisc.obj winnet.obj winnoise.obj + >> plink.rsp\r
+       echo winnojmp.obj winpgntc.obj winplink.obj + >> plink.rsp\r
+       echo winproxy.obj winser.obj winstore.obj wintime.obj + >> plink.rsp\r
+       echo winx11.obj x11fwd.obj >> plink.rsp\r
+       echo plink.exe >> plink.rsp\r
+       echo nul,cw32 import32 ole32, >> plink.rsp\r
+       echo plink.res >> plink.rsp\r
+\r
+pscp.rsp: $(MAKEFILE)\r
+       echo c0x32 + > pscp.rsp\r
+       echo be_none.obj cmdline.obj cproxy.obj int64.obj + >> pscp.rsp\r
+       echo logging.obj misc.obj pgssapi.obj pinger.obj + >> pscp.rsp\r
+       echo portfwd.obj proxy.obj pscp.obj settings.obj + >> pscp.rsp\r
+       echo sftp.obj ssh.obj sshaes.obj ssharcf.obj + >> pscp.rsp\r
+       echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> pscp.rsp\r
+       echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> pscp.rsp\r
+       echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> pscp.rsp\r
+       echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> pscp.rsp\r
+       echo timing.obj tree234.obj version.obj wildcard.obj + >> pscp.rsp\r
+       echo wincons.obj windefs.obj wingss.obj winhandl.obj + >> pscp.rsp\r
+       echo winmisc.obj winnet.obj winnoise.obj winnojmp.obj + >> pscp.rsp\r
+       echo winpgntc.obj winproxy.obj winsftp.obj + >> pscp.rsp\r
+       echo winstore.obj wintime.obj x11fwd.obj >> pscp.rsp\r
+       echo pscp.exe >> pscp.rsp\r
+       echo nul,cw32 import32 ole32, >> pscp.rsp\r
+       echo pscp.res >> pscp.rsp\r
+\r
+psftp.rsp: $(MAKEFILE)\r
+       echo c0x32 + > psftp.rsp\r
+       echo be_none.obj cmdline.obj cproxy.obj int64.obj + >> psftp.rsp\r
+       echo logging.obj misc.obj pgssapi.obj pinger.obj + >> psftp.rsp\r
+       echo portfwd.obj proxy.obj psftp.obj settings.obj + >> psftp.rsp\r
+       echo sftp.obj ssh.obj sshaes.obj ssharcf.obj + >> psftp.rsp\r
+       echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj + >> psftp.rsp\r
+       echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj + >> psftp.rsp\r
+       echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj + >> psftp.rsp\r
+       echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj + >> psftp.rsp\r
+       echo timing.obj tree234.obj version.obj wildcard.obj + >> psftp.rsp\r
+       echo wincons.obj windefs.obj wingss.obj winhandl.obj + >> psftp.rsp\r
+       echo winmisc.obj winnet.obj winnoise.obj winnojmp.obj + >> psftp.rsp\r
+       echo winpgntc.obj winproxy.obj winsftp.obj + >> psftp.rsp\r
+       echo winstore.obj wintime.obj x11fwd.obj >> psftp.rsp\r
+       echo psftp.exe >> psftp.rsp\r
+       echo nul,cw32 import32 ole32, >> psftp.rsp\r
+       echo psftp.res >> psftp.rsp\r
+\r
+putty.rsp: $(MAKEFILE)\r
+       echo c0w32 + > putty.rsp\r
+       echo be_all_s.obj cmdline.obj config.obj cproxy.obj + >> putty.rsp\r
+       echo dialog.obj ldisc.obj ldiscucs.obj logging.obj + >> putty.rsp\r
+       echo minibidi.obj misc.obj pgssapi.obj pinger.obj + >> putty.rsp\r
+       echo portfwd.obj proxy.obj raw.obj rlogin.obj + >> putty.rsp\r
+       echo sercfg.obj settings.obj sizetip.obj ssh.obj + >> putty.rsp\r
+       echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj + >> putty.rsp\r
+       echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj + >> putty.rsp\r
+       echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj + >> putty.rsp\r
+       echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj + >> putty.rsp\r
+       echo sshsha.obj sshzlib.obj telnet.obj terminal.obj + >> putty.rsp\r
+       echo timing.obj tree234.obj version.obj wcwidth.obj + >> putty.rsp\r
+       echo wildcard.obj wincfg.obj winctrls.obj windefs.obj + >> putty.rsp\r
+       echo windlg.obj window.obj wingss.obj winhandl.obj + >> putty.rsp\r
+       echo winhelp.obj winjump.obj winmisc.obj winnet.obj + >> putty.rsp\r
+       echo winnoise.obj winpgntc.obj winprint.obj + >> putty.rsp\r
+       echo winproxy.obj winser.obj winstore.obj wintime.obj + >> putty.rsp\r
+       echo winucs.obj winutils.obj winx11.obj x11fwd.obj >> putty.rsp\r
+       echo putty.exe >> putty.rsp\r
+       echo nul,cw32 import32 ole32, >> putty.rsp\r
+       echo putty.res >> putty.rsp\r
+\r
+puttygen.rsp: $(MAKEFILE)\r
+       echo c0w32 + > puttygen.rsp\r
+       echo import.obj misc.obj notiming.obj sshaes.obj + >> puttygen.rsp\r
+       echo sshbn.obj sshdes.obj sshdss.obj sshdssg.obj + >> puttygen.rsp\r
+       echo sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj + >> puttygen.rsp\r
+       echo sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj + >> puttygen.rsp\r
+       echo sshsha.obj tree234.obj version.obj winctrls.obj + >> puttygen.rsp\r
+       echo winhelp.obj winmisc.obj winnoise.obj winnojmp.obj + >> puttygen.rsp\r
+       echo winpgen.obj winstore.obj wintime.obj winutils.obj >> puttygen.rsp\r
+       echo puttygen.exe >> puttygen.rsp\r
+       echo nul,cw32 import32 ole32, >> puttygen.rsp\r
+       echo puttygen.res >> puttygen.rsp\r
+\r
+puttytel.rsp: $(MAKEFILE)\r
+       echo c0w32 + > puttytel.rsp\r
+       echo be_nos_s.obj cmdline.obj config.obj dialog.obj + >> puttytel.rsp\r
+       echo ldisc.obj ldiscucs.obj logging.obj minibidi.obj + >> puttytel.rsp\r
+       echo misc.obj nocproxy.obj nogss.obj pinger.obj + >> puttytel.rsp\r
+       echo proxy.obj raw.obj rlogin.obj sercfg.obj + >> puttytel.rsp\r
+       echo settings.obj sizetip.obj telnet.obj terminal.obj + >> puttytel.rsp\r
+       echo timing.obj tree234.obj version.obj wcwidth.obj + >> puttytel.rsp\r
+       echo wincfg.obj winctrls.obj windefs.obj windlg.obj + >> puttytel.rsp\r
+       echo window.obj winhandl.obj winhelp.obj winjump.obj + >> puttytel.rsp\r
+       echo winmisc.obj winnet.obj winprint.obj winproxy.obj + >> puttytel.rsp\r
+       echo winser.obj winstore.obj wintime.obj winucs.obj + >> puttytel.rsp\r
+       echo winutils.obj >> puttytel.rsp\r
+       echo puttytel.exe >> puttytel.rsp\r
+       echo nul,cw32 import32 ole32, >> puttytel.rsp\r
+       echo puttytel.res >> puttytel.rsp\r
+\r
+be_all_s.obj: ..\be_all_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+be_none.obj: ..\be_none.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+be_nos_s.obj: ..\be_nos_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+cmdgen.obj: ..\cmdgen.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+cproxy.obj: ..\cproxy.c ..\putty.h ..\ssh.h ..\network.h ..\proxy.h \\r
+               ..\puttyps.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+dialog.obj: ..\dialog.c ..\putty.h ..\dialog.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+fromucs.obj: ..\charset\fromucs.c ..\charset\charset.h ..\charset\internal.h\r
+gtkcfg.obj: ..\unix\gtkcfg.c ..\putty.h ..\dialog.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+gtkcols.obj: ..\unix\gtkcols.c ..\unix\gtkcols.h\r
+gtkdlg.obj: ..\unix\gtkdlg.c ..\unix\gtkcols.h ..\unix\gtkfont.h ..\putty.h \\r
+               ..\storage.h ..\dialog.h ..\tree234.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+gtkfont.obj: ..\unix\gtkfont.c ..\putty.h ..\unix\gtkfont.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+gtkwin.obj: ..\unix\gtkwin.c ..\putty.h ..\terminal.h ..\unix\gtkfont.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\tree234.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h\r
+import.obj: ..\import.c ..\putty.h ..\ssh.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+int64.obj: ..\int64.c ..\int64.h\r
+ldisc.obj: ..\ldisc.c ..\putty.h ..\terminal.h ..\ldisc.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+ldiscucs.obj: ..\ldiscucs.c ..\putty.h ..\terminal.h ..\ldisc.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+localenc.obj: ..\charset\localenc.c ..\charset\charset.h \\r
+               ..\charset\internal.h\r
+logging.obj: ..\logging.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+macenc.obj: ..\charset\macenc.c ..\charset\charset.h ..\charset\internal.h\r
+mimeenc.obj: ..\charset\mimeenc.c ..\charset\charset.h ..\charset\internal.h\r
+minibidi.obj: ..\minibidi.c ..\misc.h ..\puttymem.h\r
+misc.obj: ..\misc.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+nocproxy.obj: ..\nocproxy.c ..\putty.h ..\network.h ..\proxy.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+nogss.obj: ..\nogss.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+notiming.obj: ..\notiming.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+osxctrls.obj: ..\macosx\osxctrls.m ..\putty.h ..\dialog.h \\r
+               ..\macosx\osxclass.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+osxdlg.obj: ..\macosx\osxdlg.m ..\putty.h ..\storage.h ..\dialog.h \\r
+               ..\macosx\osxclass.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+osxmain.obj: ..\macosx\osxmain.m ..\putty.h ..\macosx\osxclass.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+osxsel.obj: ..\macosx\osxsel.m ..\putty.h ..\macosx\osxclass.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+osxwin.obj: ..\macosx\osxwin.m ..\putty.h ..\terminal.h ..\macosx\osxclass.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\tree234.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h\r
+pageant.res: FORCE\r
+pgssapi.obj: ..\pgssapi.c ..\putty.h ..\pgssapi.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+pinger.obj: ..\pinger.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+plink.res: FORCE\r
+portfwd.obj: ..\portfwd.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+proxy.obj: ..\proxy.c ..\putty.h ..\network.h ..\proxy.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+pscp.obj: ..\pscp.c ..\putty.h ..\psftp.h ..\ssh.h ..\sftp.h ..\storage.h \\r
+               ..\int64.h ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+pscp.res: FORCE\r
+psftp.obj: ..\psftp.c ..\putty.h ..\psftp.h ..\storage.h ..\ssh.h ..\sftp.h \\r
+               ..\int64.h ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+psftp.res: FORCE\r
+putty.res: FORCE\r
+puttygen.res: FORCE\r
+puttytel.res: FORCE\r
+raw.obj: ..\raw.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+rlogin.obj: ..\rlogin.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+sbcs.obj: ..\charset\sbcs.c ..\charset\charset.h ..\charset\internal.h\r
+sbcsdat.obj: ..\charset\sbcsdat.c ..\charset\charset.h ..\charset\internal.h\r
+sercfg.obj: ..\sercfg.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+settings.obj: ..\settings.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+sftp.obj: ..\sftp.c ..\misc.h ..\int64.h ..\tree234.h ..\sftp.h \\r
+               ..\puttymem.h\r
+sizetip.obj: ..\windows\sizetip.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+slookup.obj: ..\charset\slookup.c ..\charset\charset.h ..\charset\internal.h \\r
+               ..\charset\enum.c ..\charset\sbcsdat.c ..\charset\utf8.c\r
+ssh.obj: ..\ssh.c ..\putty.h ..\tree234.h ..\ssh.h ..\sshgssc.h ..\sshgss.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h ..\int64.h \\r
+               ..\pgssapi.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+sshaes.obj: ..\sshaes.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+ssharcf.obj: ..\ssharcf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshblowf.obj: ..\sshblowf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshbn.obj: ..\sshbn.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+sshcrc.obj: ..\sshcrc.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshcrcda.obj: ..\sshcrcda.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+sshdes.obj: ..\sshdes.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshdh.obj: ..\sshdh.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshdss.obj: ..\sshdss.c ..\ssh.h ..\misc.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+sshdssg.obj: ..\sshdssg.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+sshgssc.obj: ..\sshgssc.c ..\putty.h ..\sshgssc.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\pgssapi.h ..\sshgss.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h\r
+sshmd5.obj: ..\sshmd5.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshprime.obj: ..\sshprime.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshpubk.obj: ..\sshpubk.c ..\putty.h ..\ssh.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+sshrand.obj: ..\sshrand.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+sshrsa.obj: ..\sshrsa.c ..\ssh.h ..\misc.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+sshrsag.obj: ..\sshrsag.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshsh256.obj: ..\sshsh256.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshsh512.obj: ..\sshsh512.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshsha.obj: ..\sshsha.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+sshzlib.obj: ..\sshzlib.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+telnet.obj: ..\telnet.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+terminal.obj: ..\terminal.c ..\putty.h ..\terminal.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+testback.obj: ..\testback.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+time.obj: ..\time.c\r
+timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h\r
+tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h\r
+utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h\r
+ux_x11.obj: ..\unix\ux_x11.c ..\putty.h ..\ssh.h ..\network.h ..\puttyps.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxagentc.obj: ..\unix\uxagentc.c ..\putty.h ..\misc.h ..\tree234.h \\r
+               ..\puttymem.h ..\puttyps.h ..\network.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxcfg.obj: ..\unix\uxcfg.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxcons.obj: ..\unix\uxcons.c ..\putty.h ..\storage.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxgen.obj: ..\unix\uxgen.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+uxgss.obj: ..\unix\uxgss.c ..\putty.h ..\pgssapi.h ..\sshgss.h ..\sshgssc.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxmisc.obj: ..\unix\uxmisc.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+uxnet.obj: ..\unix\uxnet.c ..\putty.h ..\network.h ..\tree234.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+uxnoise.obj: ..\unix\uxnoise.c ..\putty.h ..\ssh.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxplink.obj: ..\unix\uxplink.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxprint.obj: ..\unix\uxprint.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxproxy.obj: ..\unix\uxproxy.c ..\tree234.h ..\putty.h ..\network.h \\r
+               ..\proxy.h ..\puttyps.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxpterm.obj: ..\unix\uxpterm.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxpty.obj: ..\unix\uxpty.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+uxputty.obj: ..\unix\uxputty.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxsel.obj: ..\unix\uxsel.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+uxser.obj: ..\unix\uxser.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+uxsftp.obj: ..\unix\uxsftp.c ..\putty.h ..\ssh.h ..\psftp.h ..\int64.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+uxsignal.obj: ..\unix\uxsignal.c\r
+uxstore.obj: ..\unix\uxstore.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+uxucs.obj: ..\unix\uxucs.c ..\putty.h ..\charset\charset.h ..\terminal.h \\r
+               ..\misc.h ..\puttyps.h ..\network.h ..\tree234.h \\r
+               ..\puttymem.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h\r
+wcwidth.obj: ..\wcwidth.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+wildcard.obj: ..\wildcard.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+wincfg.obj: ..\windows\wincfg.c ..\putty.h ..\dialog.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+wincons.obj: ..\windows\wincons.c ..\putty.h ..\storage.h ..\ssh.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+winctrls.obj: ..\windows\winctrls.c ..\putty.h ..\misc.h ..\dialog.h \\r
+               ..\puttyps.h ..\network.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h\r
+windefs.obj: ..\windows\windefs.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+windlg.obj: ..\windows\windlg.c ..\putty.h ..\ssh.h ..\windows\win_res.h \\r
+               ..\storage.h ..\dialog.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\puttymem.h ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+window.obj: ..\windows\window.c ..\putty.h ..\terminal.h ..\storage.h \\r
+               ..\windows\win_res.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+wingss.obj: ..\windows\wingss.c ..\putty.h ..\pgssapi.h ..\sshgss.h \\r
+               ..\sshgssc.h ..\misc.h ..\puttyps.h ..\network.h \\r
+               ..\puttymem.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+winhandl.obj: ..\windows\winhandl.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winhelp.obj: ..\windows\winhelp.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winjump.obj: ..\windows\winjump.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winmisc.obj: ..\windows\winmisc.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winnet.obj: ..\windows\winnet.c ..\putty.h ..\network.h ..\tree234.h \\r
+               ..\puttyps.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+winnoise.obj: ..\windows\winnoise.c ..\putty.h ..\ssh.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+winnojmp.obj: ..\windows\winnojmp.c\r
+winpgen.obj: ..\windows\winpgen.c ..\putty.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winpgnt.obj: ..\windows\winpgnt.c ..\putty.h ..\ssh.h ..\misc.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\puttymem.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winpgntc.obj: ..\windows\winpgntc.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winplink.obj: ..\windows\winplink.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winprint.obj: ..\windows\winprint.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winproxy.obj: ..\windows\winproxy.c ..\tree234.h ..\putty.h ..\network.h \\r
+               ..\proxy.h ..\puttyps.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winser.obj: ..\windows\winser.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winsftp.obj: ..\windows\winsftp.c ..\putty.h ..\psftp.h ..\ssh.h ..\int64.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+winstore.obj: ..\windows\winstore.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+wintime.obj: ..\windows\wintime.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winucs.obj: ..\windows\winucs.c ..\putty.h ..\terminal.h ..\misc.h \\r
+               ..\puttyps.h ..\network.h ..\tree234.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winutils.obj: ..\windows\winutils.c ..\putty.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+winx11.obj: ..\windows\winx11.c ..\putty.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+x11fwd.obj: ..\x11fwd.c ..\putty.h ..\ssh.h ..\tree234.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+xenc.obj: ..\charset\xenc.c ..\charset\charset.h ..\charset\internal.h\r
+xkeysym.obj: ..\unix\xkeysym.c ..\misc.h ..\puttymem.h\r
+xpmptcfg.obj: ..\unix\xpmptcfg.c\r
+xpmpterm.obj: ..\unix\xpmpterm.c\r
+xpmpucfg.obj: ..\unix\xpmpucfg.c\r
+xpmputty.obj: ..\unix\xpmputty.c\r
+\r
+version.obj: FORCE\r
+       bcc32 $(VER) $(CFLAGS) /c ..\version.c\r
+\r
+clean:\r
+       -del *.obj\r
+       -del *.exe\r
+       -del *.res\r
+       -del *.pch\r
+       -del *.aps\r
+       -del *.il*\r
+       -del *.pdb\r
+       -del *.rsp\r
+       -del *.tds\r
+       -del *.$$$$$$\r
+\r
+FORCE:\r
+       -rem dummy command\r
diff --git a/putty/WINDOWS/MAKEFILE.CYG b/putty/WINDOWS/MAKEFILE.CYG
new file mode 100644 (file)
index 0000000..9735a4e
--- /dev/null
@@ -0,0 +1,1006 @@
+# Makefile for putty under cygwin.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=-DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=-DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=-DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=-DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=-DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=-DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=-DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=-DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=-DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=-DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=-DMSVC4 (Windows only)\r
+#  - RCFL=-DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON.\r
+#\r
+#  - RCFL=-DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=-DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=-DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=-DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=-DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+\r
+# You can define this path to point at your tools if you need to\r
+# TOOLPATH = c:\cygwin\bin\ # or similar, if you're running Windows\r
+# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/\r
+CC = $(TOOLPATH)gcc\r
+RC = $(TOOLPATH)windres\r
+# Uncomment the following two lines to compile under Winelib\r
+# CC = winegcc\r
+# RC = wrc\r
+# You may also need to tell windres where to find include files:\r
+# RCINC = --include-dir c:\cygwin\include\\r
+\r
+CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT \\r
+               -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -I.././ \\r
+               -I../charset/ -I../windows/ -I../unix/ -I../macosx/\r
+LDFLAGS = -mno-cygwin -s\r
+RCFLAGS = $(RCINC) --define WIN32=1 --define _WIN32=1 --define WINVER=0x0400\r
+\r
+# XXX GNU-ism, but it's probably all right for a Cygwin/MinGW Makefile.\r
+RCFLAGS += $(patsubst -D%,--define %,$(VER))\r
+# _WIN32_IE is required to expose identifiers that only make sense on\r
+# systems with IE5+ installed, such as some arguments to SHGetFolderPath().\r
+# WINVER etc perform a similar function for FlashWindowEx().\r
+CFLAGS += -D_WIN32_IE=0x0500\r
+CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500\r
+\r
+.SUFFIXES:\r
+\r
+all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \\r
+               puttytel.exe\r
+\r
+pageant.exe: misc.o pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o \\r
+               sshmd5.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               tree234.o version.o winhelp.o winmisc.o winpgnt.o winpgntc.o \\r
+               winutils.o\r
+       $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pageant.map misc.o \\r
+               pageant.res.o sshaes.o sshbn.o sshdes.o sshdss.o sshmd5.o \\r
+               sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o tree234.o \\r
+               version.o winhelp.o winmisc.o winpgnt.o winpgntc.o \\r
+               winutils.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \\r
+               -lole32 -lshell32 -luser32 -lwinmm -lwinspool\r
+\r
+plink.exe: be_all_s.o cmdline.o cproxy.o ldisc.o logging.o misc.o pgssapi.o \\r
+               pinger.o plink.res.o portfwd.o proxy.o raw.o rlogin.o \\r
+               settings.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \\r
+               sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \\r
+               sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \\r
+               sshsha.o sshzlib.o telnet.o timing.o tree234.o version.o \\r
+               wildcard.o wincons.o windefs.o wingss.o winhandl.o winmisc.o \\r
+               winnet.o winnoise.o winnojmp.o winpgntc.o winplink.o \\r
+               winproxy.o winser.o winstore.o wintime.o winx11.o x11fwd.o\r
+       $(CC) $(LDFLAGS) -o $@ -Wl,-Map,plink.map be_all_s.o cmdline.o \\r
+               cproxy.o ldisc.o logging.o misc.o pgssapi.o pinger.o \\r
+               plink.res.o portfwd.o proxy.o raw.o rlogin.o settings.o \\r
+               ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o telnet.o timing.o tree234.o version.o wildcard.o \\r
+               wincons.o windefs.o wingss.o winhandl.o winmisc.o winnet.o \\r
+               winnoise.o winnojmp.o winpgntc.o winplink.o winproxy.o \\r
+               winser.o winstore.o wintime.o winx11.o x11fwd.o -ladvapi32 \\r
+               -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \\r
+               -luser32 -lwinmm -lwinspool\r
+\r
+pscp.exe: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o pscp.o pscp.res.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o timing.o tree234.o version.o wildcard.o wincons.o \\r
+               windefs.o wingss.o winhandl.o winmisc.o winnet.o winnoise.o \\r
+               winnojmp.o winpgntc.o winproxy.o winsftp.o winstore.o \\r
+               wintime.o x11fwd.o\r
+       $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map be_none.o cmdline.o \\r
+               cproxy.o int64.o logging.o misc.o pgssapi.o pinger.o \\r
+               portfwd.o proxy.o pscp.o pscp.res.o settings.o sftp.o ssh.o \\r
+               sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \\r
+               sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshzlib.o \\r
+               timing.o tree234.o version.o wildcard.o wincons.o windefs.o \\r
+               wingss.o winhandl.o winmisc.o winnet.o winnoise.o winnojmp.o \\r
+               winpgntc.o winproxy.o winsftp.o winstore.o wintime.o \\r
+               x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 \\r
+               -lole32 -lshell32 -luser32 -lwinmm -lwinspool\r
+\r
+psftp.exe: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pgssapi.o \\r
+               pinger.o portfwd.o proxy.o psftp.o psftp.res.o settings.o \\r
+               sftp.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o timing.o tree234.o version.o wildcard.o wincons.o \\r
+               windefs.o wingss.o winhandl.o winmisc.o winnet.o winnoise.o \\r
+               winnojmp.o winpgntc.o winproxy.o winsftp.o winstore.o \\r
+               wintime.o x11fwd.o\r
+       $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map be_none.o cmdline.o \\r
+               cproxy.o int64.o logging.o misc.o pgssapi.o pinger.o \\r
+               portfwd.o proxy.o psftp.o psftp.res.o settings.o sftp.o \\r
+               ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o sshcrc.o \\r
+               sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o sshmd5.o \\r
+               sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \\r
+               sshzlib.o timing.o tree234.o version.o wildcard.o wincons.o \\r
+               windefs.o wingss.o winhandl.o winmisc.o winnet.o winnoise.o \\r
+               winnojmp.o winpgntc.o winproxy.o winsftp.o winstore.o \\r
+               wintime.o x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 \\r
+               -limm32 -lole32 -lshell32 -luser32 -lwinmm -lwinspool\r
+\r
+putty.exe: be_all_s.o cmdline.o config.o cproxy.o dialog.o ldisc.o \\r
+               ldiscucs.o logging.o minibidi.o misc.o pgssapi.o pinger.o \\r
+               portfwd.o proxy.o putty.res.o raw.o rlogin.o sercfg.o \\r
+               settings.o sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o \\r
+               sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \\r
+               sshgssc.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o \\r
+               sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o timing.o \\r
+               tree234.o version.o wcwidth.o wildcard.o wincfg.o winctrls.o \\r
+               windefs.o windlg.o window.o wingss.o winhandl.o winhelp.o \\r
+               winjump.o winmisc.o winnet.o winnoise.o winpgntc.o \\r
+               winprint.o winproxy.o winser.o winstore.o wintime.o winucs.o \\r
+               winutils.o winx11.o x11fwd.o\r
+       $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,putty.map be_all_s.o \\r
+               cmdline.o config.o cproxy.o dialog.o ldisc.o ldiscucs.o \\r
+               logging.o minibidi.o misc.o pgssapi.o pinger.o portfwd.o \\r
+               proxy.o putty.res.o raw.o rlogin.o sercfg.o settings.o \\r
+               sizetip.o ssh.o sshaes.o ssharcf.o sshblowf.o sshbn.o \\r
+               sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshgssc.o \\r
+               sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \\r
+               sshsha.o sshzlib.o telnet.o terminal.o timing.o tree234.o \\r
+               version.o wcwidth.o wildcard.o wincfg.o winctrls.o windefs.o \\r
+               windlg.o window.o wingss.o winhandl.o winhelp.o winjump.o \\r
+               winmisc.o winnet.o winnoise.o winpgntc.o winprint.o \\r
+               winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o \\r
+               winx11.o x11fwd.o -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 \\r
+               -limm32 -lole32 -lshell32 -luser32 -lwinmm -lwinspool\r
+\r
+puttygen.exe: import.o misc.o notiming.o puttygen.res.o sshaes.o sshbn.o \\r
+               sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o \\r
+               sshrand.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \\r
+               tree234.o version.o winctrls.o winhelp.o winmisc.o \\r
+               winnoise.o winnojmp.o winpgen.o winstore.o wintime.o \\r
+               winutils.o\r
+       $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttygen.map import.o \\r
+               misc.o notiming.o puttygen.res.o sshaes.o sshbn.o sshdes.o \\r
+               sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \\r
+               sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o tree234.o \\r
+               version.o winctrls.o winhelp.o winmisc.o winnoise.o \\r
+               winnojmp.o winpgen.o winstore.o wintime.o winutils.o \\r
+               -ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 \\r
+               -lshell32 -luser32 -lwinmm -lwinspool\r
+\r
+puttytel.exe: be_nos_s.o cmdline.o config.o dialog.o ldisc.o ldiscucs.o \\r
+               logging.o minibidi.o misc.o nocproxy.o nogss.o pinger.o \\r
+               proxy.o puttytel.res.o raw.o rlogin.o sercfg.o settings.o \\r
+               sizetip.o telnet.o terminal.o timing.o tree234.o version.o \\r
+               wcwidth.o wincfg.o winctrls.o windefs.o windlg.o window.o \\r
+               winhandl.o winhelp.o winjump.o winmisc.o winnet.o winprint.o \\r
+               winproxy.o winser.o winstore.o wintime.o winucs.o winutils.o\r
+       $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttytel.map be_nos_s.o \\r
+               cmdline.o config.o dialog.o ldisc.o ldiscucs.o logging.o \\r
+               minibidi.o misc.o nocproxy.o nogss.o pinger.o proxy.o \\r
+               puttytel.res.o raw.o rlogin.o sercfg.o settings.o sizetip.o \\r
+               telnet.o terminal.o timing.o tree234.o version.o wcwidth.o \\r
+               wincfg.o winctrls.o windefs.o windlg.o window.o winhandl.o \\r
+               winhelp.o winjump.o winmisc.o winnet.o winprint.o winproxy.o \\r
+               winser.o winstore.o wintime.o winucs.o winutils.o -ladvapi32 \\r
+               -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \\r
+               -luser32 -lwinmm -lwinspool\r
+\r
+be_all_s.o: ../be_all_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c\r
+\r
+be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c\r
+\r
+be_nos_s.o: ../be_nos_s.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c\r
+\r
+cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c\r
+\r
+cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c\r
+\r
+config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c\r
+\r
+cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c\r
+\r
+dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c\r
+\r
+fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c\r
+\r
+gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c\r
+\r
+gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c\r
+\r
+gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkfont.h ../putty.h \\r
+               ../storage.h ../dialog.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c\r
+\r
+gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c\r
+\r
+gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkfont.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c\r
+\r
+import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c\r
+\r
+int64.o: ../int64.c ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../int64.c\r
+\r
+ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c\r
+\r
+ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \\r
+               ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldiscucs.c\r
+\r
+localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c\r
+\r
+logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c\r
+\r
+macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c\r
+\r
+mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c\r
+\r
+minibidi.o: ../minibidi.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c\r
+\r
+misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c\r
+\r
+nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c\r
+\r
+nogss.o: ../nogss.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c\r
+\r
+notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c\r
+\r
+osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \\r
+               ../tree234.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxctrls.m\r
+\r
+osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \\r
+               ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxdlg.m\r
+\r
+osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxmain.m\r
+\r
+osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxsel.m\r
+\r
+osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \\r
+               ../puttyps.h ../network.h ../misc.h ../tree234.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../macosx/osxwin.m\r
+\r
+pageant.res.o: FORCE\r
+       $(RC) $(RCFL) $(RCFLAGS) ../windows/pageant.rc pageant.res.o\r
+\r
+pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c\r
+\r
+pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c\r
+\r
+plink.res.o: FORCE\r
+       $(RC) $(RCFL) $(RCFLAGS) ../windows/plink.rc plink.res.o\r
+\r
+portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c\r
+\r
+proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c\r
+\r
+pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c\r
+\r
+pscp.res.o: FORCE\r
+       $(RC) $(RCFL) $(RCFLAGS) ../windows/pscp.rc pscp.res.o\r
+\r
+psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \\r
+               ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c\r
+\r
+psftp.res.o: FORCE\r
+       $(RC) $(RCFL) $(RCFLAGS) ../windows/psftp.rc psftp.res.o\r
+\r
+putty.res.o: FORCE\r
+       $(RC) $(RCFL) $(RCFLAGS) ../windows/putty.rc putty.res.o\r
+\r
+puttygen.res.o: FORCE\r
+       $(RC) $(RCFL) $(RCFLAGS) ../windows/puttygen.rc puttygen.res.o\r
+\r
+puttytel.res.o: FORCE\r
+       $(RC) $(RCFL) $(RCFLAGS) ../windows/puttytel.rc puttytel.res.o\r
+\r
+raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c\r
+\r
+rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c\r
+\r
+sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c\r
+\r
+sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c\r
+\r
+sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c\r
+\r
+settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c\r
+\r
+sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c\r
+\r
+sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c\r
+\r
+slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \\r
+               ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c\r
+\r
+ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../sshgssc.h ../sshgss.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../pgssapi.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c\r
+\r
+sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c\r
+\r
+ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c\r
+\r
+sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c\r
+\r
+sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbn.c\r
+\r
+sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c\r
+\r
+sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c\r
+\r
+sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c\r
+\r
+sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c\r
+\r
+sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c\r
+\r
+sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c\r
+\r
+sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../puttyps.h \\r
+               ../network.h ../pgssapi.h ../sshgss.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c\r
+\r
+sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c\r
+\r
+sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c\r
+\r
+sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c\r
+\r
+sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c\r
+\r
+sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../tree234.h \\r
+               ../network.h ../int64.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c\r
+\r
+sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c\r
+\r
+sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c\r
+\r
+sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c\r
+\r
+sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c\r
+\r
+sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \\r
+               ../int64.h ../misc.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c\r
+\r
+telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c\r
+\r
+terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \\r
+               ../misc.h ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c\r
+\r
+testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testback.c\r
+\r
+time.o: ../time.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c\r
+\r
+timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c\r
+\r
+toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c\r
+\r
+tree234.o: ../tree234.c ../puttymem.h ../tree234.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c\r
+\r
+utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c\r
+\r
+ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../puttyps.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c\r
+\r
+uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \\r
+               ../puttymem.h ../puttyps.h ../network.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c\r
+\r
+uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c\r
+\r
+uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c\r
+\r
+uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c\r
+\r
+uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c\r
+\r
+uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c\r
+\r
+uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c\r
+\r
+uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c\r
+\r
+uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c\r
+\r
+uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c\r
+\r
+uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c\r
+\r
+uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c\r
+\r
+uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c\r
+\r
+uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c\r
+\r
+uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c\r
+\r
+uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c\r
+\r
+uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c\r
+\r
+uxsignal.o: ../unix/uxsignal.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c\r
+\r
+uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c\r
+\r
+uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \\r
+               ../misc.h ../puttyps.h ../network.h ../tree234.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c\r
+\r
+wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c\r
+\r
+wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c\r
+\r
+wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c\r
+\r
+wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c\r
+\r
+winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \\r
+               ../puttyps.h ../network.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../tree234.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c\r
+\r
+windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c\r
+\r
+windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \\r
+               ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \\r
+               ../puttymem.h ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c\r
+\r
+window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \\r
+               ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c\r
+\r
+wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \\r
+               ../sshgssc.h ../misc.h ../puttyps.h ../network.h \\r
+               ../puttymem.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c\r
+\r
+winhandl.o: ../windows/winhandl.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c\r
+\r
+winhelp.o: ../windows/winhelp.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c\r
+\r
+winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c\r
+\r
+winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c\r
+\r
+winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \\r
+               ../puttyps.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c\r
+\r
+winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../int64.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c\r
+\r
+winnojmp.o: ../windows/winnojmp.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c\r
+\r
+winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c\r
+\r
+winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c\r
+\r
+winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c\r
+\r
+winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \\r
+               ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c\r
+\r
+winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c\r
+\r
+winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \\r
+               ../proxy.h ../puttyps.h ../misc.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../puttymem.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c\r
+\r
+winser.o: ../windows/winser.c ../putty.h ../puttyps.h ../network.h ../misc.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../puttymem.h ../tree234.h ../windows/winhelp.h \\r
+               ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c\r
+\r
+winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h ../int64.h \\r
+               ../puttyps.h ../network.h ../misc.h ../puttymem.h \\r
+               ../tree234.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c\r
+\r
+winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \\r
+               ../network.h ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c\r
+\r
+wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \\r
+               ../misc.h ../windows/winstuff.h ../macosx/osx.h \\r
+               ../unix/unix.h ../puttymem.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c\r
+\r
+winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \\r
+               ../puttyps.h ../network.h ../tree234.h ../puttymem.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c\r
+\r
+winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../puttyps.h \\r
+               ../network.h ../puttymem.h ../windows/winstuff.h \\r
+               ../macosx/osx.h ../unix/unix.h ../tree234.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c\r
+\r
+winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \\r
+               ../misc.h ../puttymem.h ../tree234.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c\r
+\r
+x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \\r
+               ../network.h ../misc.h ../puttymem.h ../int64.h \\r
+               ../windows/winstuff.h ../macosx/osx.h ../unix/unix.h \\r
+               ../windows/winhelp.h ../charset/charset.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c\r
+\r
+xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c\r
+\r
+xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c\r
+\r
+xpmptcfg.o: ../unix/xpmptcfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c\r
+\r
+xpmpterm.o: ../unix/xpmpterm.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c\r
+\r
+xpmpucfg.o: ../unix/xpmpucfg.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c\r
+\r
+xpmputty.o: ../unix/xpmputty.c\r
+       $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c\r
+\r
+\r
+version.o: FORCE\r
+       $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c ../version.c\r
+\r
+clean:\r
+       rm -f *.o *.exe *.res.o *.map\r
+\r
+FORCE:\r
diff --git a/putty/WINDOWS/MAKEFILE.LCC b/putty/WINDOWS/MAKEFILE.LCC
new file mode 100644 (file)
index 0000000..74b5a2b
--- /dev/null
@@ -0,0 +1,873 @@
+# Makefile for putty under lcc.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="-DSNAPSHOT=1999-01-25 -DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=-DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=-DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=-DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=-DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=-DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=-DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=-DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=-DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=-DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=-DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=-DMSVC4 (Windows only)\r
+#  - RCFL=-DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON.\r
+#\r
+#  - RCFL=-DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=-DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=-DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=-DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=-DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+\r
+# If you rename this file to `Makefile', you should change this line,\r
+# so that the .rsp files still depend on the correct makefile.\r
+MAKEFILE = Makefile.lcc\r
+\r
+# C compilation flags\r
+CFLAGS = -D_WINDOWS -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ -I..\macosx/\r
+# Resource compilation flags\r
+RCFLAGS = \r
+\r
+# Get include directory for resource compiler\r
+\r
+RCFLAGS += $(VER)\r
+\r
+all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \\r
+               puttytel.exe\r
+\r
+pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \\r
+               sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \\r
+               winpgnt.obj winpgntc.obj winutils.obj\r
+       lcclnk -subsystem  windows -o pageant.exe misc.obj pageant.res sshaes.obj \\r
+               sshbn.obj sshdes.obj sshdss.obj sshmd5.obj sshpubk.obj \\r
+               sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj tree234.obj \\r
+               version.obj winhelp.obj winmisc.obj winpgnt.obj winpgntc.obj \\r
+               winutils.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \\r
+               winmm.lib imm32.lib\r
+\r
+plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \\r
+               misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \\r
+               proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \\r
+               version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \\r
+               winhandl.obj winmisc.obj winnet.obj winnoise.obj \\r
+               winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \\r
+               winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj\r
+       lcclnk  -o plink.exe be_all_s.obj cmdline.obj cproxy.obj ldisc.obj \\r
+               logging.obj misc.obj pgssapi.obj pinger.obj plink.res \\r
+               portfwd.obj proxy.obj raw.obj rlogin.obj settings.obj \\r
+               ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \\r
+               sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \\r
+               sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \\r
+               sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \\r
+               timing.obj tree234.obj version.obj wildcard.obj wincons.obj \\r
+               windefs.obj wingss.obj winhandl.obj winmisc.obj winnet.obj \\r
+               winnoise.obj winnojmp.obj winpgntc.obj winplink.obj \\r
+               winproxy.obj winser.obj winstore.obj wintime.obj winx11.obj \\r
+               x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \\r
+               winmm.lib imm32.lib\r
+\r
+pscp.exe: be_none.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \\r
+               pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \\r
+               wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winnojmp.obj \\r
+               winpgntc.obj winproxy.obj winsftp.obj winstore.obj \\r
+               wintime.obj x11fwd.obj\r
+       lcclnk  -o pscp.exe be_none.obj cmdline.obj cproxy.obj int64.obj \\r
+               logging.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \\r
+               proxy.obj pscp.obj pscp.res settings.obj sftp.obj ssh.obj \\r
+               sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj \\r
+               sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshgssc.obj \\r
+               sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \\r
+               sshsh512.obj sshsha.obj sshzlib.obj timing.obj tree234.obj \\r
+               version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \\r
+               winhandl.obj winmisc.obj winnet.obj winnoise.obj \\r
+               winnojmp.obj winpgntc.obj winproxy.obj winsftp.obj \\r
+               winstore.obj wintime.obj x11fwd.obj shell32.lib wsock32.lib \\r
+               ws2_32.lib winspool.lib winmm.lib imm32.lib\r
+\r
+psftp.exe: be_none.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \\r
+               psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \\r
+               wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winnojmp.obj \\r
+               winpgntc.obj winproxy.obj winsftp.obj winstore.obj \\r
+               wintime.obj x11fwd.obj\r
+       lcclnk  -o psftp.exe be_none.obj cmdline.obj cproxy.obj int64.obj \\r
+               logging.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \\r
+               proxy.obj psftp.obj psftp.res settings.obj sftp.obj ssh.obj \\r
+               sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj \\r
+               sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshgssc.obj \\r
+               sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \\r
+               sshsh512.obj sshsha.obj sshzlib.obj timing.obj tree234.obj \\r
+               version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \\r
+               winhandl.obj winmisc.obj winnet.obj winnoise.obj \\r
+               winnojmp.obj winpgntc.obj winproxy.obj winsftp.obj \\r
+               winstore.obj wintime.obj x11fwd.obj shell32.lib wsock32.lib \\r
+               ws2_32.lib winspool.lib winmm.lib imm32.lib\r
+\r
+putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \\r
+               ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \\r
+               raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \\r
+               ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \\r
+               sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \\r
+               sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \\r
+               sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \\r
+               terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \\r
+               wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \\r
+               window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winpgntc.obj \\r
+               winprint.obj winproxy.obj winser.obj winstore.obj \\r
+               wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj\r
+       lcclnk -subsystem  windows -o putty.exe be_all_s.obj cmdline.obj config.obj \\r
+               cproxy.obj dialog.obj ldisc.obj ldiscucs.obj logging.obj \\r
+               minibidi.obj misc.obj pgssapi.obj pinger.obj portfwd.obj \\r
+               proxy.obj putty.res raw.obj rlogin.obj sercfg.obj \\r
+               settings.obj sizetip.obj ssh.obj sshaes.obj ssharcf.obj \\r
+               sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj sshdes.obj \\r
+               sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj \\r
+               sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \\r
+               sshzlib.obj telnet.obj terminal.obj timing.obj tree234.obj \\r
+               version.obj wcwidth.obj wildcard.obj wincfg.obj winctrls.obj \\r
+               windefs.obj windlg.obj window.obj wingss.obj winhandl.obj \\r
+               winhelp.obj winjump.obj winmisc.obj winnet.obj winnoise.obj \\r
+               winpgntc.obj winprint.obj winproxy.obj winser.obj \\r
+               winstore.obj wintime.obj winucs.obj winutils.obj winx11.obj \\r
+               x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \\r
+               winmm.lib imm32.lib\r
+\r
+puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \\r
+               sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \\r
+               sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \\r
+               sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \\r
+               winctrls.obj winhelp.obj winmisc.obj winnoise.obj \\r
+               winnojmp.obj winpgen.obj winstore.obj wintime.obj \\r
+               winutils.obj\r
+       lcclnk -subsystem  windows -o puttygen.exe import.obj misc.obj notiming.obj \\r
+               puttygen.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \\r
+               sshdssg.obj sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj \\r
+               sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj \\r
+               tree234.obj version.obj winctrls.obj winhelp.obj winmisc.obj \\r
+               winnoise.obj winnojmp.obj winpgen.obj winstore.obj \\r
+               wintime.obj winutils.obj shell32.lib wsock32.lib ws2_32.lib \\r
+               winspool.lib winmm.lib imm32.lib\r
+\r
+puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \\r
+               ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \\r
+               nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \\r
+               rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \\r
+               terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \\r
+               wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \\r
+               winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \\r
+               winprint.obj winproxy.obj winser.obj winstore.obj \\r
+               wintime.obj winucs.obj winutils.obj\r
+       lcclnk -subsystem  windows -o puttytel.exe be_nos_s.obj cmdline.obj \\r
+               config.obj dialog.obj ldisc.obj ldiscucs.obj logging.obj \\r
+               minibidi.obj misc.obj nocproxy.obj nogss.obj pinger.obj \\r
+               proxy.obj puttytel.res raw.obj rlogin.obj sercfg.obj \\r
+               settings.obj sizetip.obj telnet.obj terminal.obj timing.obj \\r
+               tree234.obj version.obj wcwidth.obj wincfg.obj winctrls.obj \\r
+               windefs.obj windlg.obj window.obj winhandl.obj winhelp.obj \\r
+               winjump.obj winmisc.obj winnet.obj winprint.obj winproxy.obj \\r
+               winser.obj winstore.obj wintime.obj winucs.obj winutils.obj \\r
+               shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \\r
+               imm32.lib\r
+\r
+be_all_s.obj: ..\be_all_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_all_s.c\r
+be_none.obj: ..\be_none.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_none.c\r
+be_nos_s.obj: ..\be_nos_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_nos_s.c\r
+cmdgen.obj: ..\cmdgen.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cmdgen.c\r
+cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cmdline.c\r
+config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\config.c\r
+cproxy.obj: ..\cproxy.c ..\putty.h ..\ssh.h ..\network.h ..\proxy.h \\r
+               ..\puttyps.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cproxy.c\r
+dialog.obj: ..\dialog.c ..\putty.h ..\dialog.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\dialog.c\r
+fromucs.obj: ..\charset\fromucs.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\fromucs.c\r
+gtkcfg.obj: ..\unix\gtkcfg.c ..\putty.h ..\dialog.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkcfg.c\r
+gtkcols.obj: ..\unix\gtkcols.c ..\unix\gtkcols.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkcols.c\r
+gtkdlg.obj: ..\unix\gtkdlg.c ..\unix\gtkcols.h ..\unix\gtkfont.h ..\putty.h \\r
+               ..\storage.h ..\dialog.h ..\tree234.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkdlg.c\r
+gtkfont.obj: ..\unix\gtkfont.c ..\putty.h ..\unix\gtkfont.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkfont.c\r
+gtkwin.obj: ..\unix\gtkwin.c ..\putty.h ..\terminal.h ..\unix\gtkfont.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\tree234.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkwin.c\r
+import.obj: ..\import.c ..\putty.h ..\ssh.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\import.c\r
+int64.obj: ..\int64.c ..\int64.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\int64.c\r
+ldisc.obj: ..\ldisc.c ..\putty.h ..\terminal.h ..\ldisc.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ldisc.c\r
+ldiscucs.obj: ..\ldiscucs.c ..\putty.h ..\terminal.h ..\ldisc.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ldiscucs.c\r
+localenc.obj: ..\charset\localenc.c ..\charset\charset.h \\r
+               ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\localenc.c\r
+logging.obj: ..\logging.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\logging.c\r
+macenc.obj: ..\charset\macenc.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\macenc.c\r
+mimeenc.obj: ..\charset\mimeenc.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\mimeenc.c\r
+minibidi.obj: ..\minibidi.c ..\misc.h ..\puttymem.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\minibidi.c\r
+misc.obj: ..\misc.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\misc.c\r
+nocproxy.obj: ..\nocproxy.c ..\putty.h ..\network.h ..\proxy.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\nocproxy.c\r
+nogss.obj: ..\nogss.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\nogss.c\r
+notiming.obj: ..\notiming.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\notiming.c\r
+osxctrls.obj: ..\macosx\osxctrls.m ..\putty.h ..\dialog.h \\r
+               ..\macosx\osxclass.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\macosx\osxctrls.m\r
+osxdlg.obj: ..\macosx\osxdlg.m ..\putty.h ..\storage.h ..\dialog.h \\r
+               ..\macosx\osxclass.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\macosx\osxdlg.m\r
+osxmain.obj: ..\macosx\osxmain.m ..\putty.h ..\macosx\osxclass.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\macosx\osxmain.m\r
+osxsel.obj: ..\macosx\osxsel.m ..\putty.h ..\macosx\osxclass.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\macosx\osxsel.m\r
+osxwin.obj: ..\macosx\osxwin.m ..\putty.h ..\terminal.h ..\macosx\osxclass.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\tree234.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\macosx\osxwin.m\r
+pageant.res: FORCE\r
+       lrc $(RCFL) -r $(RCFLAGS) ..\windows\pageant.rc\r
+pgssapi.obj: ..\pgssapi.c ..\putty.h ..\pgssapi.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pgssapi.c\r
+pinger.obj: ..\pinger.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pinger.c\r
+plink.res: FORCE\r
+       lrc $(RCFL) -r $(RCFLAGS) ..\windows\plink.rc\r
+portfwd.obj: ..\portfwd.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\portfwd.c\r
+proxy.obj: ..\proxy.c ..\putty.h ..\network.h ..\proxy.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\proxy.c\r
+pscp.obj: ..\pscp.c ..\putty.h ..\psftp.h ..\ssh.h ..\sftp.h ..\storage.h \\r
+               ..\int64.h ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pscp.c\r
+pscp.res: FORCE\r
+       lrc $(RCFL) -r $(RCFLAGS) ..\windows\pscp.rc\r
+psftp.obj: ..\psftp.c ..\putty.h ..\psftp.h ..\storage.h ..\ssh.h ..\sftp.h \\r
+               ..\int64.h ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\psftp.c\r
+psftp.res: FORCE\r
+       lrc $(RCFL) -r $(RCFLAGS) ..\windows\psftp.rc\r
+putty.res: FORCE\r
+       lrc $(RCFL) -r $(RCFLAGS) ..\windows\putty.rc\r
+puttygen.res: FORCE\r
+       lrc $(RCFL) -r $(RCFLAGS) ..\windows\puttygen.rc\r
+puttytel.res: FORCE\r
+       lrc $(RCFL) -r $(RCFLAGS) ..\windows\puttytel.rc\r
+raw.obj: ..\raw.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\raw.c\r
+rlogin.obj: ..\rlogin.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\rlogin.c\r
+sbcs.obj: ..\charset\sbcs.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\sbcs.c\r
+sbcsdat.obj: ..\charset\sbcsdat.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\sbcsdat.c\r
+sercfg.obj: ..\sercfg.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sercfg.c\r
+settings.obj: ..\settings.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\settings.c\r
+sftp.obj: ..\sftp.c ..\misc.h ..\int64.h ..\tree234.h ..\sftp.h \\r
+               ..\puttymem.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sftp.c\r
+sizetip.obj: ..\windows\sizetip.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\sizetip.c\r
+slookup.obj: ..\charset\slookup.c ..\charset\charset.h ..\charset\internal.h \\r
+               ..\charset\enum.c ..\charset\sbcsdat.c ..\charset\utf8.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\slookup.c\r
+ssh.obj: ..\ssh.c ..\putty.h ..\tree234.h ..\ssh.h ..\sshgssc.h ..\sshgss.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h ..\int64.h \\r
+               ..\pgssapi.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh.c\r
+sshaes.obj: ..\sshaes.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshaes.c\r
+ssharcf.obj: ..\ssharcf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssharcf.c\r
+sshblowf.obj: ..\sshblowf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshblowf.c\r
+sshbn.obj: ..\sshbn.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshbn.c\r
+sshcrc.obj: ..\sshcrc.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshcrc.c\r
+sshcrcda.obj: ..\sshcrcda.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshcrcda.c\r
+sshdes.obj: ..\sshdes.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdes.c\r
+sshdh.obj: ..\sshdh.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdh.c\r
+sshdss.obj: ..\sshdss.c ..\ssh.h ..\misc.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdss.c\r
+sshdssg.obj: ..\sshdssg.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdssg.c\r
+sshgssc.obj: ..\sshgssc.c ..\putty.h ..\sshgssc.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\pgssapi.h ..\sshgss.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshgssc.c\r
+sshmd5.obj: ..\sshmd5.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshmd5.c\r
+sshprime.obj: ..\sshprime.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshprime.c\r
+sshpubk.obj: ..\sshpubk.c ..\putty.h ..\ssh.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshpubk.c\r
+sshrand.obj: ..\sshrand.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrand.c\r
+sshrsa.obj: ..\sshrsa.c ..\ssh.h ..\misc.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrsa.c\r
+sshrsag.obj: ..\sshrsag.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrsag.c\r
+sshsh256.obj: ..\sshsh256.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsh256.c\r
+sshsh512.obj: ..\sshsh512.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsh512.c\r
+sshsha.obj: ..\sshsha.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsha.c\r
+sshzlib.obj: ..\sshzlib.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshzlib.c\r
+telnet.obj: ..\telnet.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\telnet.c\r
+terminal.obj: ..\terminal.c ..\putty.h ..\terminal.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\terminal.c\r
+testback.obj: ..\testback.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\testback.c\r
+time.obj: ..\time.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\time.c\r
+timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\timing.c\r
+toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\toucs.c\r
+tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\tree234.c\r
+utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\utf8.c\r
+ux_x11.obj: ..\unix\ux_x11.c ..\putty.h ..\ssh.h ..\network.h ..\puttyps.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\ux_x11.c\r
+uxagentc.obj: ..\unix\uxagentc.c ..\putty.h ..\misc.h ..\tree234.h \\r
+               ..\puttymem.h ..\puttyps.h ..\network.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxagentc.c\r
+uxcfg.obj: ..\unix\uxcfg.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxcfg.c\r
+uxcons.obj: ..\unix\uxcons.c ..\putty.h ..\storage.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxcons.c\r
+uxgen.obj: ..\unix\uxgen.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxgen.c\r
+uxgss.obj: ..\unix\uxgss.c ..\putty.h ..\pgssapi.h ..\sshgss.h ..\sshgssc.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxgss.c\r
+uxmisc.obj: ..\unix\uxmisc.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxmisc.c\r
+uxnet.obj: ..\unix\uxnet.c ..\putty.h ..\network.h ..\tree234.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxnet.c\r
+uxnoise.obj: ..\unix\uxnoise.c ..\putty.h ..\ssh.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxnoise.c\r
+uxplink.obj: ..\unix\uxplink.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxplink.c\r
+uxprint.obj: ..\unix\uxprint.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxprint.c\r
+uxproxy.obj: ..\unix\uxproxy.c ..\tree234.h ..\putty.h ..\network.h \\r
+               ..\proxy.h ..\puttyps.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxproxy.c\r
+uxpterm.obj: ..\unix\uxpterm.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpterm.c\r
+uxpty.obj: ..\unix\uxpty.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpty.c\r
+uxputty.obj: ..\unix\uxputty.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxputty.c\r
+uxsel.obj: ..\unix\uxsel.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsel.c\r
+uxser.obj: ..\unix\uxser.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxser.c\r
+uxsftp.obj: ..\unix\uxsftp.c ..\putty.h ..\ssh.h ..\psftp.h ..\int64.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsftp.c\r
+uxsignal.obj: ..\unix\uxsignal.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsignal.c\r
+uxstore.obj: ..\unix\uxstore.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxstore.c\r
+uxucs.obj: ..\unix\uxucs.c ..\putty.h ..\charset\charset.h ..\terminal.h \\r
+               ..\misc.h ..\puttyps.h ..\network.h ..\tree234.h \\r
+               ..\puttymem.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxucs.c\r
+wcwidth.obj: ..\wcwidth.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\wcwidth.c\r
+wildcard.obj: ..\wildcard.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\wildcard.c\r
+wincfg.obj: ..\windows\wincfg.c ..\putty.h ..\dialog.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincfg.c\r
+wincons.obj: ..\windows\wincons.c ..\putty.h ..\storage.h ..\ssh.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincons.c\r
+winctrls.obj: ..\windows\winctrls.c ..\putty.h ..\misc.h ..\dialog.h \\r
+               ..\puttyps.h ..\network.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winctrls.c\r
+windefs.obj: ..\windows\windefs.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\windefs.c\r
+windlg.obj: ..\windows\windlg.c ..\putty.h ..\ssh.h ..\windows\win_res.h \\r
+               ..\storage.h ..\dialog.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\puttymem.h ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\windlg.c\r
+window.obj: ..\windows\window.c ..\putty.h ..\terminal.h ..\storage.h \\r
+               ..\windows\win_res.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\window.c\r
+wingss.obj: ..\windows\wingss.c ..\putty.h ..\pgssapi.h ..\sshgss.h \\r
+               ..\sshgssc.h ..\misc.h ..\puttyps.h ..\network.h \\r
+               ..\puttymem.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wingss.c\r
+winhandl.obj: ..\windows\winhandl.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winhandl.c\r
+winhelp.obj: ..\windows\winhelp.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winhelp.c\r
+winjump.obj: ..\windows\winjump.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winjump.c\r
+winmisc.obj: ..\windows\winmisc.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winmisc.c\r
+winnet.obj: ..\windows\winnet.c ..\putty.h ..\network.h ..\tree234.h \\r
+               ..\puttyps.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnet.c\r
+winnoise.obj: ..\windows\winnoise.c ..\putty.h ..\ssh.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnoise.c\r
+winnojmp.obj: ..\windows\winnojmp.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnojmp.c\r
+winpgen.obj: ..\windows\winpgen.c ..\putty.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgen.c\r
+winpgnt.obj: ..\windows\winpgnt.c ..\putty.h ..\ssh.h ..\misc.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\puttymem.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgnt.c\r
+winpgntc.obj: ..\windows\winpgntc.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgntc.c\r
+winplink.obj: ..\windows\winplink.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winplink.c\r
+winprint.obj: ..\windows\winprint.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winprint.c\r
+winproxy.obj: ..\windows\winproxy.c ..\tree234.h ..\putty.h ..\network.h \\r
+               ..\proxy.h ..\puttyps.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winproxy.c\r
+winser.obj: ..\windows\winser.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winser.c\r
+winsftp.obj: ..\windows\winsftp.c ..\putty.h ..\psftp.h ..\ssh.h ..\int64.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winsftp.c\r
+winstore.obj: ..\windows\winstore.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winstore.c\r
+wintime.obj: ..\windows\wintime.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wintime.c\r
+winucs.obj: ..\windows\winucs.c ..\putty.h ..\terminal.h ..\misc.h \\r
+               ..\puttyps.h ..\network.h ..\tree234.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winucs.c\r
+winutils.obj: ..\windows\winutils.c ..\putty.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winutils.c\r
+winx11.obj: ..\windows\winx11.c ..\putty.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winx11.c\r
+x11fwd.obj: ..\x11fwd.c ..\putty.h ..\ssh.h ..\tree234.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\x11fwd.c\r
+xenc.obj: ..\charset\xenc.c ..\charset\charset.h ..\charset\internal.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\xenc.c\r
+xkeysym.obj: ..\unix\xkeysym.c ..\misc.h ..\puttymem.h\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xkeysym.c\r
+xpmptcfg.obj: ..\unix\xpmptcfg.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmptcfg.c\r
+xpmpterm.obj: ..\unix\xpmpterm.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmpterm.c\r
+xpmpucfg.obj: ..\unix\xpmpucfg.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmpucfg.c\r
+xpmputty.obj: ..\unix\xpmputty.c\r
+       lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmputty.c\r
+\r
+version.obj: FORCE\r
+       lcc $(VER) $(CFLAGS) /c ..\version.c\r
+\r
+clean:\r
+       -del *.obj\r
+       -del *.exe\r
+       -del *.res\r
+\r
+FORCE:\r
diff --git a/putty/WINDOWS/MAKEFILE.VC b/putty/WINDOWS/MAKEFILE.VC
new file mode 100644 (file)
index 0000000..754b149
--- /dev/null
@@ -0,0 +1,1094 @@
+# Makefile for putty under Visual C.\r
+#\r
+# This file was created by `mkfiles.pl' from the `Recipe' file.\r
+# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\r
+#\r
+# Extra options you can set:\r
+#\r
+#  - VER="/DSNAPSHOT=1999-01-25 /DSVN_REV=1234"\r
+#      Generates executables whose About box report them as being a\r
+#      development snapshot. SVN_REV is a Subversion revision number.\r
+#\r
+#  - VER=/DRELEASE=0.43\r
+#      Generates executables whose About box report them as being a\r
+#      release version.\r
+#\r
+#  - COMPAT=/DAUTO_WINSOCK (Windows only)\r
+#      Causes PuTTY to assume that <windows.h> includes its own WinSock\r
+#      header file, so that it won't try to include <winsock.h>.\r
+#\r
+#  - COMPAT=/DWINSOCK_TWO (Windows only)\r
+#      Causes the PuTTY utilities to include <winsock2.h> instead of\r
+#      <winsock.h>, except Plink which _needs_ WinSock 2 so it already\r
+#      does this.\r
+#\r
+#  - COMPAT=/DNO_SECURITY (Windows only)\r
+#      Disables Pageant's use of <aclapi.h>, which is not available\r
+#      with some development environments (such as older versions of\r
+#      the Cygwin/mingw GNU toolchain). This means that Pageant\r
+#      won't care about the local user ID of processes accessing it; a\r
+#      version of Pageant built with this option will therefore refuse\r
+#      to run under NT-series OSes on security grounds (although it\r
+#      will run fine on Win95-series OSes where there is no access\r
+#      control anyway).\r
+#\r
+#  - COMPAT=/DNO_MULTIMON (Windows only)\r
+#      Disables PuTTY's use of <multimon.h>, which is not available\r
+#      with some development environments. This means that PuTTY's\r
+#      full-screen mode (configurable to work on Alt-Enter) will\r
+#      not behave usefully in a multi-monitor environment.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <multimon.h> is\r
+#      known not to be available in Cygwin.\r
+#\r
+#  - COMPAT=/DNO_HTMLHELP (Windows only)\r
+#      Disables PuTTY's use of <htmlhelp.h>, which is not available\r
+#      with some development environments. The resulting binary\r
+#      will only look for an old-style WinHelp file (.HLP/.CNT), and\r
+#      will ignore any .CHM file.\r
+#\r
+#      Note that this definition is always enabled in the Cygwin\r
+#      build, since at the time of writing this <htmlhelp.h> is\r
+#      known not to be available in Cygwin (although you can use\r
+#      the htmlhelp.h supplied with HTML Help Workshop).\r
+#\r
+#  - RCFL=/DNO_MANIFESTS (Windows only)\r
+#      Disables inclusion of XML application manifests in the PuTTY\r
+#      binaries. This may be necessary to build for 64-bit Windows;\r
+#      the manifests are only included to use the XP GUI style on\r
+#      Windows XP, and the architecture tags are a lie on 64-bit.\r
+#\r
+#  - COMPAT=/DNO_IPV6\r
+#      Disables PuTTY's ability to make IPv6 connections, enabling\r
+#      it to compile under development environments which do not\r
+#      support IPv6 in their header files.\r
+#\r
+#  - COMPAT=/DNO_GSSAPI\r
+#      Disables PuTTY's ability to use GSSAPI functions for\r
+#      authentication and key exchange.\r
+#\r
+#  - COMPAT=/DSTATIC_GSSAPI\r
+#      Causes PuTTY to try to link statically against the GSSAPI\r
+#      library instead of the default of doing it at run time.\r
+#\r
+#  - COMPAT=/DMSVC4 (Windows only)\r
+#  - RCFL=/DMSVC4\r
+#      Makes a couple of minor changes so that PuTTY compiles using\r
+#      MSVC 4. You will also need /DNO_SECURITY and /DNO_MULTIMON.\r
+#\r
+#  - RCFL=/DASCIICTLS (Windows only)\r
+#      Uses ASCII rather than Unicode to specify the tab control in\r
+#      the resource file. Probably most useful when compiling with\r
+#      Cygnus/mingw32, whose resource compiler may have less of a\r
+#      problem with it.\r
+#\r
+#  - XFLAGS=/DTELNET_DEFAULT\r
+#      Causes PuTTY to default to the Telnet protocol (in the absence\r
+#      of Default Settings and so on to the contrary). Normally PuTTY\r
+#      will default to SSH.\r
+#\r
+#  - XFLAGS=/DDEBUG\r
+#      Causes PuTTY to enable internal debugging.\r
+#\r
+#  - XFLAGS=/DMALLOC_LOG\r
+#      Causes PuTTY to emit a file called putty_mem.log, logging every\r
+#      memory allocation and free, so you can track memory leaks.\r
+#\r
+#  - XFLAGS=/DMINEFIELD (Windows only)\r
+#      Causes PuTTY to use a custom memory allocator, similar in\r
+#      concept to Electric Fence, in place of regular malloc(). Wastes\r
+#      huge amounts of RAM, but should cause heap-corruption bugs to\r
+#      show up as GPFs at the point of failure rather than appearing\r
+#      later on as second-level damage.\r
+#\r
+\r
+# If you rename this file to `Makefile', you should change this line,\r
+# so that the .rsp files still depend on the correct makefile.\r
+MAKEFILE = Makefile.vc\r
+\r
+# C compilation flags\r
+CFLAGS = /nologo /W3 /O1 -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ -I..\macosx/ /D_WINDOWS /D_WIN32_WINDOWS=0x500 /DWINVER=0x500\r
+LFLAGS = /incremental:no /fixed\r
+RCFLAGS = -DWIN32 -D_WIN32 -DWINVER=0x0400\r
+\r
+CFLAGS = $(CFLAGS) /DHAS_GSSAPI /DSECURITY_WIN32\r
+RCFLAGS = $(RCFLAGS) $(VER)\r
+\r
+\r
+all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \\r
+               puttytel.exe\r
+\r
+pageant.exe: misc.obj pageant.res sshaes.obj sshbn.obj sshdes.obj sshdss.obj \\r
+               sshmd5.obj sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj tree234.obj version.obj winhelp.obj winmisc.obj \\r
+               winpgnt.obj winpgntc.obj winutils.obj pageant.rsp\r
+       link $(LFLAGS) $(XLFLAGS) -out:pageant.exe -map:pageant.map @pageant.rsp\r
+\r
+plink.exe: be_all_s.obj cmdline.obj cproxy.obj ldisc.obj logging.obj \\r
+               misc.obj pgssapi.obj pinger.obj plink.res portfwd.obj \\r
+               proxy.obj raw.obj rlogin.obj settings.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj telnet.obj timing.obj tree234.obj \\r
+               version.obj wildcard.obj wincons.obj windefs.obj wingss.obj \\r
+               winhandl.obj winmisc.obj winnet.obj winnoise.obj \\r
+               winnojmp.obj winpgntc.obj winplink.obj winproxy.obj \\r
+               winser.obj winstore.obj wintime.obj winx11.obj x11fwd.obj \\r
+               plink.rsp\r
+       link $(LFLAGS) $(XLFLAGS) -out:plink.exe -map:plink.map @plink.rsp\r
+\r
+pscp.exe: be_none.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \\r
+               pscp.res settings.obj sftp.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \\r
+               wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winnojmp.obj \\r
+               winpgntc.obj winproxy.obj winsftp.obj winstore.obj \\r
+               wintime.obj x11fwd.obj pscp.rsp\r
+       link $(LFLAGS) $(XLFLAGS) -out:pscp.exe -map:pscp.map @pscp.rsp\r
+\r
+psftp.exe: be_none.obj cmdline.obj cproxy.obj int64.obj logging.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \\r
+               psftp.res settings.obj sftp.obj ssh.obj sshaes.obj \\r
+               ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj \\r
+               sshdes.obj sshdh.obj sshdss.obj sshgssc.obj sshmd5.obj \\r
+               sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \\r
+               sshsha.obj sshzlib.obj timing.obj tree234.obj version.obj \\r
+               wildcard.obj wincons.obj windefs.obj wingss.obj winhandl.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winnojmp.obj \\r
+               winpgntc.obj winproxy.obj winsftp.obj winstore.obj \\r
+               wintime.obj x11fwd.obj psftp.rsp\r
+       link $(LFLAGS) $(XLFLAGS) -out:psftp.exe -map:psftp.map @psftp.rsp\r
+\r
+putty.exe: be_all_s.obj cmdline.obj config.obj cproxy.obj dialog.obj \\r
+               ldisc.obj ldiscucs.obj logging.obj minibidi.obj misc.obj \\r
+               pgssapi.obj pinger.obj portfwd.obj proxy.obj putty.res \\r
+               raw.obj rlogin.obj sercfg.obj settings.obj sizetip.obj \\r
+               ssh.obj sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj \\r
+               sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \\r
+               sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj \\r
+               sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj telnet.obj \\r
+               terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \\r
+               wildcard.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \\r
+               window.obj wingss.obj winhandl.obj winhelp.obj winjump.obj \\r
+               winmisc.obj winnet.obj winnoise.obj winpgntc.obj \\r
+               winprint.obj winproxy.obj winser.obj winstore.obj \\r
+               wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj \\r
+               putty.rsp\r
+       link $(LFLAGS) $(XLFLAGS) -out:putty.exe -map:putty.map @putty.rsp\r
+\r
+puttygen.exe: import.obj misc.obj notiming.obj puttygen.res sshaes.obj \\r
+               sshbn.obj sshdes.obj sshdss.obj sshdssg.obj sshmd5.obj \\r
+               sshprime.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \\r
+               sshsh256.obj sshsh512.obj sshsha.obj tree234.obj version.obj \\r
+               winctrls.obj winhelp.obj winmisc.obj winnoise.obj \\r
+               winnojmp.obj winpgen.obj winstore.obj wintime.obj \\r
+               winutils.obj puttygen.rsp\r
+       link $(LFLAGS) $(XLFLAGS) -out:puttygen.exe -map:puttygen.map @puttygen.rsp\r
+\r
+puttytel.exe: be_nos_s.obj cmdline.obj config.obj dialog.obj ldisc.obj \\r
+               ldiscucs.obj logging.obj minibidi.obj misc.obj nocproxy.obj \\r
+               nogss.obj pinger.obj proxy.obj puttytel.res raw.obj \\r
+               rlogin.obj sercfg.obj settings.obj sizetip.obj telnet.obj \\r
+               terminal.obj timing.obj tree234.obj version.obj wcwidth.obj \\r
+               wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \\r
+               winhandl.obj winhelp.obj winjump.obj winmisc.obj winnet.obj \\r
+               winprint.obj winproxy.obj winser.obj winstore.obj \\r
+               wintime.obj winucs.obj winutils.obj puttytel.rsp\r
+       link $(LFLAGS) $(XLFLAGS) -out:puttytel.exe -map:puttytel.map @puttytel.rsp\r
+\r
+pageant.rsp: $(MAKEFILE)\r
+       echo /nologo /subsystem:windows > pageant.rsp\r
+       echo advapi32.lib comctl32.lib comdlg32.lib gdi32.lib >> pageant.rsp\r
+       echo imm32.lib misc.obj ole32.lib pageant.res >> pageant.rsp\r
+       echo shell32.lib sshaes.obj sshbn.obj sshdes.obj >> pageant.rsp\r
+       echo sshdss.obj sshmd5.obj sshpubk.obj sshrsa.obj >> pageant.rsp\r
+       echo sshsh256.obj sshsh512.obj sshsha.obj tree234.obj >> pageant.rsp\r
+       echo user32.lib version.obj winhelp.obj winmisc.obj >> pageant.rsp\r
+       echo winmm.lib winpgnt.obj winpgntc.obj winspool.lib >> pageant.rsp\r
+       echo winutils.obj >> pageant.rsp\r
+\r
+plink.rsp: $(MAKEFILE)\r
+       echo /nologo /subsystem:console > plink.rsp\r
+       echo advapi32.lib be_all_s.obj cmdline.obj >> plink.rsp\r
+       echo comctl32.lib comdlg32.lib cproxy.obj gdi32.lib >> plink.rsp\r
+       echo imm32.lib ldisc.obj logging.obj misc.obj >> plink.rsp\r
+       echo ole32.lib pgssapi.obj pinger.obj plink.res >> plink.rsp\r
+       echo portfwd.obj proxy.obj raw.obj rlogin.obj >> plink.rsp\r
+       echo settings.obj shell32.lib ssh.obj sshaes.obj >> plink.rsp\r
+       echo ssharcf.obj sshblowf.obj sshbn.obj sshcrc.obj >> plink.rsp\r
+       echo sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj >> plink.rsp\r
+       echo sshgssc.obj sshmd5.obj sshpubk.obj sshrand.obj >> plink.rsp\r
+       echo sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj >> plink.rsp\r
+       echo sshzlib.obj telnet.obj timing.obj tree234.obj >> plink.rsp\r
+       echo user32.lib version.obj wildcard.obj wincons.obj >> plink.rsp\r
+       echo windefs.obj wingss.obj winhandl.obj winmisc.obj >> plink.rsp\r
+       echo winmm.lib winnet.obj winnoise.obj winnojmp.obj >> plink.rsp\r
+       echo winpgntc.obj winplink.obj winproxy.obj winser.obj >> plink.rsp\r
+       echo winspool.lib winstore.obj wintime.obj winx11.obj >> plink.rsp\r
+       echo x11fwd.obj >> plink.rsp\r
+\r
+pscp.rsp: $(MAKEFILE)\r
+       echo /nologo /subsystem:console > pscp.rsp\r
+       echo advapi32.lib be_none.obj cmdline.obj comctl32.lib >> pscp.rsp\r
+       echo comdlg32.lib cproxy.obj gdi32.lib imm32.lib >> pscp.rsp\r
+       echo int64.obj logging.obj misc.obj ole32.lib >> pscp.rsp\r
+       echo pgssapi.obj pinger.obj portfwd.obj proxy.obj >> pscp.rsp\r
+       echo pscp.obj pscp.res settings.obj sftp.obj >> pscp.rsp\r
+       echo shell32.lib ssh.obj sshaes.obj ssharcf.obj >> pscp.rsp\r
+       echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> pscp.rsp\r
+       echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> pscp.rsp\r
+       echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj >> pscp.rsp\r
+       echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj >> pscp.rsp\r
+       echo timing.obj tree234.obj user32.lib version.obj >> pscp.rsp\r
+       echo wildcard.obj wincons.obj windefs.obj wingss.obj >> pscp.rsp\r
+       echo winhandl.obj winmisc.obj winmm.lib winnet.obj >> pscp.rsp\r
+       echo winnoise.obj winnojmp.obj winpgntc.obj >> pscp.rsp\r
+       echo winproxy.obj winsftp.obj winspool.lib >> pscp.rsp\r
+       echo winstore.obj wintime.obj x11fwd.obj >> pscp.rsp\r
+\r
+psftp.rsp: $(MAKEFILE)\r
+       echo /nologo /subsystem:console > psftp.rsp\r
+       echo advapi32.lib be_none.obj cmdline.obj comctl32.lib >> psftp.rsp\r
+       echo comdlg32.lib cproxy.obj gdi32.lib imm32.lib >> psftp.rsp\r
+       echo int64.obj logging.obj misc.obj ole32.lib >> psftp.rsp\r
+       echo pgssapi.obj pinger.obj portfwd.obj proxy.obj >> psftp.rsp\r
+       echo psftp.obj psftp.res settings.obj sftp.obj >> psftp.rsp\r
+       echo shell32.lib ssh.obj sshaes.obj ssharcf.obj >> psftp.rsp\r
+       echo sshblowf.obj sshbn.obj sshcrc.obj sshcrcda.obj >> psftp.rsp\r
+       echo sshdes.obj sshdh.obj sshdss.obj sshgssc.obj >> psftp.rsp\r
+       echo sshmd5.obj sshpubk.obj sshrand.obj sshrsa.obj >> psftp.rsp\r
+       echo sshsh256.obj sshsh512.obj sshsha.obj sshzlib.obj >> psftp.rsp\r
+       echo timing.obj tree234.obj user32.lib version.obj >> psftp.rsp\r
+       echo wildcard.obj wincons.obj windefs.obj wingss.obj >> psftp.rsp\r
+       echo winhandl.obj winmisc.obj winmm.lib winnet.obj >> psftp.rsp\r
+       echo winnoise.obj winnojmp.obj winpgntc.obj >> psftp.rsp\r
+       echo winproxy.obj winsftp.obj winspool.lib >> psftp.rsp\r
+       echo winstore.obj wintime.obj x11fwd.obj >> psftp.rsp\r
+\r
+putty.rsp: $(MAKEFILE)\r
+       echo /nologo /subsystem:windows > putty.rsp\r
+       echo advapi32.lib be_all_s.obj cmdline.obj >> putty.rsp\r
+       echo comctl32.lib comdlg32.lib config.obj cproxy.obj >> putty.rsp\r
+       echo dialog.obj gdi32.lib imm32.lib ldisc.obj >> putty.rsp\r
+       echo ldiscucs.obj logging.obj minibidi.obj misc.obj >> putty.rsp\r
+       echo ole32.lib pgssapi.obj pinger.obj portfwd.obj >> putty.rsp\r
+       echo proxy.obj putty.res raw.obj rlogin.obj sercfg.obj >> putty.rsp\r
+       echo settings.obj shell32.lib sizetip.obj ssh.obj >> putty.rsp\r
+       echo sshaes.obj ssharcf.obj sshblowf.obj sshbn.obj >> putty.rsp\r
+       echo sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj >> putty.rsp\r
+       echo sshdss.obj sshgssc.obj sshmd5.obj sshpubk.obj >> putty.rsp\r
+       echo sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj >> putty.rsp\r
+       echo sshsha.obj sshzlib.obj telnet.obj terminal.obj >> putty.rsp\r
+       echo timing.obj tree234.obj user32.lib version.obj >> putty.rsp\r
+       echo wcwidth.obj wildcard.obj wincfg.obj winctrls.obj >> putty.rsp\r
+       echo windefs.obj windlg.obj window.obj wingss.obj >> putty.rsp\r
+       echo winhandl.obj winhelp.obj winjump.obj winmisc.obj >> putty.rsp\r
+       echo winmm.lib winnet.obj winnoise.obj winpgntc.obj >> putty.rsp\r
+       echo winprint.obj winproxy.obj winser.obj winspool.lib >> putty.rsp\r
+       echo winstore.obj wintime.obj winucs.obj winutils.obj >> putty.rsp\r
+       echo winx11.obj x11fwd.obj >> putty.rsp\r
+\r
+puttygen.rsp: $(MAKEFILE)\r
+       echo /nologo /subsystem:windows > puttygen.rsp\r
+       echo advapi32.lib comctl32.lib comdlg32.lib gdi32.lib >> puttygen.rsp\r
+       echo imm32.lib import.obj misc.obj notiming.obj >> puttygen.rsp\r
+       echo ole32.lib puttygen.res shell32.lib sshaes.obj >> puttygen.rsp\r
+       echo sshbn.obj sshdes.obj sshdss.obj sshdssg.obj >> puttygen.rsp\r
+       echo sshmd5.obj sshprime.obj sshpubk.obj sshrand.obj >> puttygen.rsp\r
+       echo sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj >> puttygen.rsp\r
+       echo sshsha.obj tree234.obj user32.lib version.obj >> puttygen.rsp\r
+       echo winctrls.obj winhelp.obj winmisc.obj winmm.lib >> puttygen.rsp\r
+       echo winnoise.obj winnojmp.obj winpgen.obj >> puttygen.rsp\r
+       echo winspool.lib winstore.obj wintime.obj >> puttygen.rsp\r
+       echo winutils.obj >> puttygen.rsp\r
+\r
+puttytel.rsp: $(MAKEFILE)\r
+       echo /nologo /subsystem:windows > puttytel.rsp\r
+       echo advapi32.lib be_nos_s.obj cmdline.obj >> puttytel.rsp\r
+       echo comctl32.lib comdlg32.lib config.obj dialog.obj >> puttytel.rsp\r
+       echo gdi32.lib imm32.lib ldisc.obj ldiscucs.obj >> puttytel.rsp\r
+       echo logging.obj minibidi.obj misc.obj nocproxy.obj >> puttytel.rsp\r
+       echo nogss.obj ole32.lib pinger.obj proxy.obj >> puttytel.rsp\r
+       echo puttytel.res raw.obj rlogin.obj sercfg.obj >> puttytel.rsp\r
+       echo settings.obj shell32.lib sizetip.obj telnet.obj >> puttytel.rsp\r
+       echo terminal.obj timing.obj tree234.obj user32.lib >> puttytel.rsp\r
+       echo version.obj wcwidth.obj wincfg.obj winctrls.obj >> puttytel.rsp\r
+       echo windefs.obj windlg.obj window.obj winhandl.obj >> puttytel.rsp\r
+       echo winhelp.obj winjump.obj winmisc.obj winmm.lib >> puttytel.rsp\r
+       echo winnet.obj winprint.obj winproxy.obj winser.obj >> puttytel.rsp\r
+       echo winspool.lib winstore.obj wintime.obj winucs.obj >> puttytel.rsp\r
+       echo winutils.obj >> puttytel.rsp\r
+\r
+be_all_s.obj: ..\be_all_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\be_all_s.c\r
+\r
+be_none.obj: ..\be_none.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\be_none.c\r
+\r
+be_nos_s.obj: ..\be_nos_s.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\be_nos_s.c\r
+\r
+cmdgen.obj: ..\cmdgen.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\cmdgen.c\r
+\r
+cmdline.obj: ..\cmdline.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\cmdline.c\r
+\r
+config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\config.c\r
+\r
+cproxy.obj: ..\cproxy.c ..\putty.h ..\ssh.h ..\network.h ..\proxy.h \\r
+               ..\puttyps.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\cproxy.c\r
+\r
+dialog.obj: ..\dialog.c ..\putty.h ..\dialog.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\dialog.c\r
+\r
+fromucs.obj: ..\charset\fromucs.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\fromucs.c\r
+\r
+gtkcfg.obj: ..\unix\gtkcfg.c ..\putty.h ..\dialog.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\gtkcfg.c\r
+\r
+gtkcols.obj: ..\unix\gtkcols.c ..\unix\gtkcols.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\gtkcols.c\r
+\r
+gtkdlg.obj: ..\unix\gtkdlg.c ..\unix\gtkcols.h ..\unix\gtkfont.h ..\putty.h \\r
+               ..\storage.h ..\dialog.h ..\tree234.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\gtkdlg.c\r
+\r
+gtkfont.obj: ..\unix\gtkfont.c ..\putty.h ..\unix\gtkfont.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\gtkfont.c\r
+\r
+gtkwin.obj: ..\unix\gtkwin.c ..\putty.h ..\terminal.h ..\unix\gtkfont.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\tree234.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\gtkwin.c\r
+\r
+import.obj: ..\import.c ..\putty.h ..\ssh.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\import.c\r
+\r
+int64.obj: ..\int64.c ..\int64.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\int64.c\r
+\r
+ldisc.obj: ..\ldisc.c ..\putty.h ..\terminal.h ..\ldisc.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\ldisc.c\r
+\r
+ldiscucs.obj: ..\ldiscucs.c ..\putty.h ..\terminal.h ..\ldisc.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\ldiscucs.c\r
+\r
+localenc.obj: ..\charset\localenc.c ..\charset\charset.h \\r
+               ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\localenc.c\r
+\r
+logging.obj: ..\logging.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\logging.c\r
+\r
+macenc.obj: ..\charset\macenc.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\macenc.c\r
+\r
+mimeenc.obj: ..\charset\mimeenc.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\mimeenc.c\r
+\r
+minibidi.obj: ..\minibidi.c ..\misc.h ..\puttymem.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\minibidi.c\r
+\r
+misc.obj: ..\misc.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\misc.c\r
+\r
+nocproxy.obj: ..\nocproxy.c ..\putty.h ..\network.h ..\proxy.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\nocproxy.c\r
+\r
+nogss.obj: ..\nogss.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\nogss.c\r
+\r
+notiming.obj: ..\notiming.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\notiming.c\r
+\r
+osxctrls.obj: ..\macosx\osxctrls.m ..\putty.h ..\dialog.h \\r
+               ..\macosx\osxclass.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\macosx\osxctrls.m\r
+\r
+osxdlg.obj: ..\macosx\osxdlg.m ..\putty.h ..\storage.h ..\dialog.h \\r
+               ..\macosx\osxclass.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\macosx\osxdlg.m\r
+\r
+osxmain.obj: ..\macosx\osxmain.m ..\putty.h ..\macosx\osxclass.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\macosx\osxmain.m\r
+\r
+osxsel.obj: ..\macosx\osxsel.m ..\putty.h ..\macosx\osxclass.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\macosx\osxsel.m\r
+\r
+osxwin.obj: ..\macosx\osxwin.m ..\putty.h ..\terminal.h ..\macosx\osxclass.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\tree234.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\macosx\osxwin.m\r
+\r
+pageant.res: *.c *.h *.rc ..\windows\pageant.rc ..\windows\rcstuff.h \\r
+               ..\windows\pageant.ico ..\windows\pageants.ico \\r
+               ..\windows\version.rc2 ..\windows\pageant.mft\r
+       rc $(RCFL) -r $(RCFLAGS) ..\windows\pageant.rc\r
+\r
+pgssapi.obj: ..\pgssapi.c ..\putty.h ..\pgssapi.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\pgssapi.c\r
+\r
+pinger.obj: ..\pinger.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\pinger.c\r
+\r
+plink.res: *.c *.h *.rc ..\windows\plink.rc ..\windows\rcstuff.h \\r
+               ..\windows\putty.ico ..\windows\version.rc2\r
+       rc $(RCFL) -r $(RCFLAGS) ..\windows\plink.rc\r
+\r
+portfwd.obj: ..\portfwd.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\portfwd.c\r
+\r
+proxy.obj: ..\proxy.c ..\putty.h ..\network.h ..\proxy.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\proxy.c\r
+\r
+pscp.obj: ..\pscp.c ..\putty.h ..\psftp.h ..\ssh.h ..\sftp.h ..\storage.h \\r
+               ..\int64.h ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\pscp.c\r
+\r
+pscp.res: *.c *.h *.rc ..\windows\pscp.rc ..\windows\rcstuff.h \\r
+               ..\windows\pscp.ico ..\windows\version.rc2\r
+       rc $(RCFL) -r $(RCFLAGS) ..\windows\pscp.rc\r
+\r
+psftp.obj: ..\psftp.c ..\putty.h ..\psftp.h ..\storage.h ..\ssh.h ..\sftp.h \\r
+               ..\int64.h ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\psftp.c\r
+\r
+psftp.res: *.c *.h *.rc ..\windows\psftp.rc ..\windows\rcstuff.h \\r
+               ..\windows\pscp.ico ..\windows\version.rc2\r
+       rc $(RCFL) -r $(RCFLAGS) ..\windows\psftp.rc\r
+\r
+putty.res: *.c *.h *.rc ..\windows\putty.rc ..\windows\rcstuff.h \\r
+               ..\windows\win_res.rc2 ..\windows\putty.mft \\r
+               ..\windows\win_res.h ..\windows\putty.ico \\r
+               ..\windows\puttycfg.ico ..\windows\version.rc2\r
+       rc $(RCFL) -r $(RCFLAGS) ..\windows\putty.rc\r
+\r
+puttygen.res: *.c *.h *.rc ..\windows\puttygen.rc ..\windows\rcstuff.h \\r
+               ..\windows\puttygen.ico ..\windows\version.rc2 \\r
+               ..\windows\puttygen.mft\r
+       rc $(RCFL) -r $(RCFLAGS) ..\windows\puttygen.rc\r
+\r
+puttytel.res: *.c *.h *.rc ..\windows\puttytel.rc ..\windows\rcstuff.h \\r
+               ..\windows\win_res.rc2 ..\windows\putty.mft \\r
+               ..\windows\win_res.h ..\windows\putty.ico \\r
+               ..\windows\puttycfg.ico ..\windows\version.rc2\r
+       rc $(RCFL) -r $(RCFLAGS) ..\windows\puttytel.rc\r
+\r
+raw.obj: ..\raw.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\raw.c\r
+\r
+rlogin.obj: ..\rlogin.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\rlogin.c\r
+\r
+sbcs.obj: ..\charset\sbcs.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\sbcs.c\r
+\r
+sbcsdat.obj: ..\charset\sbcsdat.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\sbcsdat.c\r
+\r
+sercfg.obj: ..\sercfg.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sercfg.c\r
+\r
+settings.obj: ..\settings.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\settings.c\r
+\r
+sftp.obj: ..\sftp.c ..\misc.h ..\int64.h ..\tree234.h ..\sftp.h \\r
+               ..\puttymem.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sftp.c\r
+\r
+sizetip.obj: ..\windows\sizetip.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\sizetip.c\r
+\r
+slookup.obj: ..\charset\slookup.c ..\charset\charset.h ..\charset\internal.h \\r
+               ..\charset\enum.c ..\charset\sbcsdat.c ..\charset\utf8.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\slookup.c\r
+\r
+ssh.obj: ..\ssh.c ..\putty.h ..\tree234.h ..\ssh.h ..\sshgssc.h ..\sshgss.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h ..\int64.h \\r
+               ..\pgssapi.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\ssh.c\r
+\r
+sshaes.obj: ..\sshaes.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshaes.c\r
+\r
+ssharcf.obj: ..\ssharcf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\ssharcf.c\r
+\r
+sshblowf.obj: ..\sshblowf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshblowf.c\r
+\r
+sshbn.obj: ..\sshbn.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshbn.c\r
+\r
+sshcrc.obj: ..\sshcrc.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshcrc.c\r
+\r
+sshcrcda.obj: ..\sshcrcda.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshcrcda.c\r
+\r
+sshdes.obj: ..\sshdes.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshdes.c\r
+\r
+sshdh.obj: ..\sshdh.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshdh.c\r
+\r
+sshdss.obj: ..\sshdss.c ..\ssh.h ..\misc.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshdss.c\r
+\r
+sshdssg.obj: ..\sshdssg.c ..\misc.h ..\ssh.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshdssg.c\r
+\r
+sshgssc.obj: ..\sshgssc.c ..\putty.h ..\sshgssc.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\pgssapi.h ..\sshgss.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshgssc.c\r
+\r
+sshmd5.obj: ..\sshmd5.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshmd5.c\r
+\r
+sshprime.obj: ..\sshprime.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshprime.c\r
+\r
+sshpubk.obj: ..\sshpubk.c ..\putty.h ..\ssh.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshpubk.c\r
+\r
+sshrand.obj: ..\sshrand.c ..\putty.h ..\ssh.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshrand.c\r
+\r
+sshrsa.obj: ..\sshrsa.c ..\ssh.h ..\misc.h ..\puttymem.h ..\tree234.h \\r
+               ..\network.h ..\int64.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshrsa.c\r
+\r
+sshrsag.obj: ..\sshrsag.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshrsag.c\r
+\r
+sshsh256.obj: ..\sshsh256.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshsh256.c\r
+\r
+sshsh512.obj: ..\sshsh512.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshsh512.c\r
+\r
+sshsha.obj: ..\sshsha.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshsha.c\r
+\r
+sshzlib.obj: ..\sshzlib.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \\r
+               ..\int64.h ..\misc.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\sshzlib.c\r
+\r
+telnet.obj: ..\telnet.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\telnet.c\r
+\r
+terminal.obj: ..\terminal.c ..\putty.h ..\terminal.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\tree234.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\terminal.c\r
+\r
+testback.obj: ..\testback.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\testback.c\r
+\r
+time.obj: ..\time.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\time.c\r
+\r
+timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\timing.c\r
+\r
+toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\toucs.c\r
+\r
+tree234.obj: ..\tree234.c ..\puttymem.h ..\tree234.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\tree234.c\r
+\r
+utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\utf8.c\r
+\r
+ux_x11.obj: ..\unix\ux_x11.c ..\putty.h ..\ssh.h ..\network.h ..\puttyps.h \\r
+               ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\ux_x11.c\r
+\r
+uxagentc.obj: ..\unix\uxagentc.c ..\putty.h ..\misc.h ..\tree234.h \\r
+               ..\puttymem.h ..\puttyps.h ..\network.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxagentc.c\r
+\r
+uxcfg.obj: ..\unix\uxcfg.c ..\putty.h ..\dialog.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxcfg.c\r
+\r
+uxcons.obj: ..\unix\uxcons.c ..\putty.h ..\storage.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxcons.c\r
+\r
+uxgen.obj: ..\unix\uxgen.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxgen.c\r
+\r
+uxgss.obj: ..\unix\uxgss.c ..\putty.h ..\pgssapi.h ..\sshgss.h ..\sshgssc.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxgss.c\r
+\r
+uxmisc.obj: ..\unix\uxmisc.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxmisc.c\r
+\r
+uxnet.obj: ..\unix\uxnet.c ..\putty.h ..\network.h ..\tree234.h ..\puttyps.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxnet.c\r
+\r
+uxnoise.obj: ..\unix\uxnoise.c ..\putty.h ..\ssh.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxnoise.c\r
+\r
+uxplink.obj: ..\unix\uxplink.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxplink.c\r
+\r
+uxprint.obj: ..\unix\uxprint.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxprint.c\r
+\r
+uxproxy.obj: ..\unix\uxproxy.c ..\tree234.h ..\putty.h ..\network.h \\r
+               ..\proxy.h ..\puttyps.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxproxy.c\r
+\r
+uxpterm.obj: ..\unix\uxpterm.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxpterm.c\r
+\r
+uxpty.obj: ..\unix\uxpty.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxpty.c\r
+\r
+uxputty.obj: ..\unix\uxputty.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxputty.c\r
+\r
+uxsel.obj: ..\unix\uxsel.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxsel.c\r
+\r
+uxser.obj: ..\unix\uxser.c ..\putty.h ..\tree234.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxser.c\r
+\r
+uxsftp.obj: ..\unix\uxsftp.c ..\putty.h ..\ssh.h ..\psftp.h ..\int64.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxsftp.c\r
+\r
+uxsignal.obj: ..\unix\uxsignal.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxsignal.c\r
+\r
+uxstore.obj: ..\unix\uxstore.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxstore.c\r
+\r
+uxucs.obj: ..\unix\uxucs.c ..\putty.h ..\charset\charset.h ..\terminal.h \\r
+               ..\misc.h ..\puttyps.h ..\network.h ..\tree234.h \\r
+               ..\puttymem.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\uxucs.c\r
+\r
+wcwidth.obj: ..\wcwidth.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\wcwidth.c\r
+\r
+wildcard.obj: ..\wildcard.c ..\putty.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\wildcard.c\r
+\r
+wincfg.obj: ..\windows\wincfg.c ..\putty.h ..\dialog.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\wincfg.c\r
+\r
+wincons.obj: ..\windows\wincons.c ..\putty.h ..\storage.h ..\ssh.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\wincons.c\r
+\r
+winctrls.obj: ..\windows\winctrls.c ..\putty.h ..\misc.h ..\dialog.h \\r
+               ..\puttyps.h ..\network.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winctrls.c\r
+\r
+windefs.obj: ..\windows\windefs.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\windefs.c\r
+\r
+windlg.obj: ..\windows\windlg.c ..\putty.h ..\ssh.h ..\windows\win_res.h \\r
+               ..\storage.h ..\dialog.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\puttymem.h ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\windlg.c\r
+\r
+window.obj: ..\windows\window.c ..\putty.h ..\terminal.h ..\storage.h \\r
+               ..\windows\win_res.h ..\puttyps.h ..\network.h ..\misc.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\window.c\r
+\r
+wingss.obj: ..\windows\wingss.c ..\putty.h ..\pgssapi.h ..\sshgss.h \\r
+               ..\sshgssc.h ..\misc.h ..\puttyps.h ..\network.h \\r
+               ..\puttymem.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\tree234.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\wingss.c\r
+\r
+winhandl.obj: ..\windows\winhandl.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winhandl.c\r
+\r
+winhelp.obj: ..\windows\winhelp.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winhelp.c\r
+\r
+winjump.obj: ..\windows\winjump.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winjump.c\r
+\r
+winmisc.obj: ..\windows\winmisc.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winmisc.c\r
+\r
+winnet.obj: ..\windows\winnet.c ..\putty.h ..\network.h ..\tree234.h \\r
+               ..\puttyps.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winnet.c\r
+\r
+winnoise.obj: ..\windows\winnoise.c ..\putty.h ..\ssh.h ..\storage.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\int64.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\windows\winhelp.h \\r
+               ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winnoise.c\r
+\r
+winnojmp.obj: ..\windows\winnojmp.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winnojmp.c\r
+\r
+winpgen.obj: ..\windows\winpgen.c ..\putty.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winpgen.c\r
+\r
+winpgnt.obj: ..\windows\winpgnt.c ..\putty.h ..\ssh.h ..\misc.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\puttymem.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winpgnt.c\r
+\r
+winpgntc.obj: ..\windows\winpgntc.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winpgntc.c\r
+\r
+winplink.obj: ..\windows\winplink.c ..\putty.h ..\storage.h ..\tree234.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winplink.c\r
+\r
+winprint.obj: ..\windows\winprint.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winprint.c\r
+\r
+winproxy.obj: ..\windows\winproxy.c ..\tree234.h ..\putty.h ..\network.h \\r
+               ..\proxy.h ..\puttyps.h ..\misc.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\puttymem.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winproxy.c\r
+\r
+winser.obj: ..\windows\winser.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winser.c\r
+\r
+winsftp.obj: ..\windows\winsftp.c ..\putty.h ..\psftp.h ..\ssh.h ..\int64.h \\r
+               ..\puttyps.h ..\network.h ..\misc.h ..\puttymem.h \\r
+               ..\tree234.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winsftp.c\r
+\r
+winstore.obj: ..\windows\winstore.c ..\putty.h ..\storage.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winstore.c\r
+\r
+wintime.obj: ..\windows\wintime.c ..\putty.h ..\puttyps.h ..\network.h \\r
+               ..\misc.h ..\windows\winstuff.h ..\macosx\osx.h \\r
+               ..\unix\unix.h ..\puttymem.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\wintime.c\r
+\r
+winucs.obj: ..\windows\winucs.c ..\putty.h ..\terminal.h ..\misc.h \\r
+               ..\puttyps.h ..\network.h ..\tree234.h ..\puttymem.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winucs.c\r
+\r
+winutils.obj: ..\windows\winutils.c ..\putty.h ..\misc.h ..\puttyps.h \\r
+               ..\network.h ..\puttymem.h ..\windows\winstuff.h \\r
+               ..\macosx\osx.h ..\unix\unix.h ..\tree234.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winutils.c\r
+\r
+winx11.obj: ..\windows\winx11.c ..\putty.h ..\ssh.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\tree234.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\windows\winx11.c\r
+\r
+x11fwd.obj: ..\x11fwd.c ..\putty.h ..\ssh.h ..\tree234.h ..\puttyps.h \\r
+               ..\network.h ..\misc.h ..\puttymem.h ..\int64.h \\r
+               ..\windows\winstuff.h ..\macosx\osx.h ..\unix\unix.h \\r
+               ..\windows\winhelp.h ..\charset\charset.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\x11fwd.c\r
+\r
+xenc.obj: ..\charset\xenc.c ..\charset\charset.h ..\charset\internal.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\charset\xenc.c\r
+\r
+xkeysym.obj: ..\unix\xkeysym.c ..\misc.h ..\puttymem.h\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\xkeysym.c\r
+\r
+xpmptcfg.obj: ..\unix\xpmptcfg.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\xpmptcfg.c\r
+\r
+xpmpterm.obj: ..\unix\xpmpterm.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\xpmpterm.c\r
+\r
+xpmpucfg.obj: ..\unix\xpmpucfg.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\xpmpucfg.c\r
+\r
+xpmputty.obj: ..\unix\xpmputty.c\r
+       cl $(COMPAT) $(CFLAGS) $(XFLAGS) /c ..\unix\xpmputty.c\r
+\r
+\r
+version.obj: *.c *.h *.rc\r
+       cl $(VER) $(CFLAGS) /c ..\version.c\r
+\r
+clean: tidy\r
+       -del *.exe\r
+\r
+tidy:\r
+       -del *.obj\r
+       -del *.res\r
+       -del *.pch\r
+       -del *.aps\r
+       -del *.ilk\r
+       -del *.pdb\r
+       -del *.rsp\r
+       -del *.dsp\r
+       -del *.dsw\r
+       -del *.ncb\r
+       -del *.opt\r
+       -del *.plg\r
+       -del *.map\r
+       -del *.idb\r
+       -del debug.log\r
diff --git a/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP b/putty/WINDOWS/MSVC/PAGEANT/PAGEANT.DSP
new file mode 100644 (file)
index 0000000..935cedd
--- /dev/null
@@ -0,0 +1,245 @@
+# Microsoft Developer Studio Project File - Name="pageant" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Application" 0x0101\r
+\r
+CFG=pageant - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "pageant.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "pageant.mak" CFG="pageant - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "pageant - Win32 Release" (based on "Win32 (x86) Application")\r
+!MESSAGE "pageant - Win32 Debug" (based on "Win32 (x86) Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "pageant - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "NDEBUG"\r
+# ADD RSC /l 0x809 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /machine:I386\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "pageant - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "_DEBUG"\r
+# ADD RSC /l 0x809 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "pageant - Win32 Release"\r
+# Name "pageant - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshaes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshbn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshmd5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshpubk.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrsa.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh256.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh512.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsha.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winmisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winpgnt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winpgntc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winutils.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\charset\charset.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\macosx\osx.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\network.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\putty.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttymem.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttyps.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\unix\unix.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\rcstuff.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstuff.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\pageant.ico\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\pageant.rc\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\pageants.ico\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/putty/WINDOWS/MSVC/PLINK/PLINK.DSP b/putty/WINDOWS/MSVC/PLINK/PLINK.DSP
new file mode 100644 (file)
index 0000000..fc3d6eb
--- /dev/null
@@ -0,0 +1,418 @@
+# Microsoft Developer Studio Project File - Name="plink" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Application" 0x0101\r
+\r
+CFG=plink - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "plink.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "plink.mak" CFG="plink - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "plink - Win32 Release" (based on "Win32 (x86) Application")\r
+!MESSAGE "plink - Win32 Debug" (based on "Win32 (x86) Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "plink - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "NDEBUG"\r
+# ADD RSC /l 0x809 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:console /machine:I386\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "plink - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "_DEBUG"\r
+# ADD RSC /l 0x809 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "plink - Win32 Release"\r
+# Name "plink - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\be_all_s.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cmdline.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\logging.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pinger.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\portfwd.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\raw.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\rlogin.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\settings.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.c\r
+\r
+!IF  "$(CFG)" == "plink - Win32 Release"\r
+\r
+!ELSEIF  "$(CFG)" == "plink - Win32 Debug"\r
+\r
+# ADD CPP /Zi\r
+\r
+!ENDIF \r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshaes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssharcf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshblowf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshbn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrcda.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdh.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshmd5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshpubk.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrand.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrsa.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh256.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh512.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsha.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshzlib.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\telnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\timing.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\wildcard.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wincons.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\windefs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wingss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhandl.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winmisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnoise.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnojmp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winpgntc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winplink.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winser.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wintime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winx11.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\x11fwd.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\charset\charset.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldisc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\macosx\osx.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\network.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\putty.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttymem.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttyps.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgss.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\storage.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\terminal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\unix\unix.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\rcstuff.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstuff.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\plink.rc\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\putty.ico\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/putty/WINDOWS/MSVC/PSCP/PSCP.DSP b/putty/WINDOWS/MSVC/PSCP/PSCP.DSP
new file mode 100644 (file)
index 0000000..763849b
--- /dev/null
@@ -0,0 +1,406 @@
+# Microsoft Developer Studio Project File - Name="pscp" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Application" 0x0101\r
+\r
+CFG=pscp - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "pscp.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "pscp.mak" CFG="pscp - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "pscp - Win32 Release" (based on "Win32 (x86) Application")\r
+!MESSAGE "pscp - Win32 Debug" (based on "Win32 (x86) Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "pscp - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "NDEBUG"\r
+# ADD RSC /l 0x809 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:console /machine:I386\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "pscp - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "_DEBUG"\r
+# ADD RSC /l 0x809 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "pscp - Win32 Release"\r
+# Name "pscp - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\be_none.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cmdline.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\logging.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pinger.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\portfwd.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pscp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\settings.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sftp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.c\r
+\r
+!IF  "$(CFG)" == "pscp - Win32 Release"\r
+\r
+!ELSEIF  "$(CFG)" == "pscp - Win32 Debug"\r
+\r
+# ADD CPP /Zi\r
+\r
+!ENDIF \r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshaes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssharcf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshblowf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshbn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrcda.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdh.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshmd5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshpubk.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrand.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrsa.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh256.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh512.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsha.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshzlib.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\timing.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\wildcard.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wincons.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\windefs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wingss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhandl.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winmisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnoise.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnojmp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winpgntc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winsftp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wintime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\x11fwd.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\charset\charset.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\macosx\osx.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\network.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\psftp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\putty.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttymem.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttyps.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sftp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgss.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\storage.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\unix\unix.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\rcstuff.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstuff.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\pscp.ico\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\pscp.rc\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP b/putty/WINDOWS/MSVC/PSFTP/PSFTP.DSP
new file mode 100644 (file)
index 0000000..af4ce25
--- /dev/null
@@ -0,0 +1,406 @@
+# Microsoft Developer Studio Project File - Name="psftp" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Application" 0x0101\r
+\r
+CFG=psftp - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "psftp.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "psftp.mak" CFG="psftp - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "psftp - Win32 Release" (based on "Win32 (x86) Application")\r
+!MESSAGE "psftp - Win32 Debug" (based on "Win32 (x86) Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "psftp - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "NDEBUG"\r
+# ADD RSC /l 0x809 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:console /machine:I386\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "psftp - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "_DEBUG"\r
+# ADD RSC /l 0x809 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "psftp - Win32 Release"\r
+# Name "psftp - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\be_none.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cmdline.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\logging.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pinger.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\portfwd.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\psftp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\settings.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sftp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.c\r
+\r
+!IF  "$(CFG)" == "psftp - Win32 Release"\r
+\r
+!ELSEIF  "$(CFG)" == "psftp - Win32 Debug"\r
+\r
+# ADD CPP /Zi\r
+\r
+!ENDIF \r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshaes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssharcf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshblowf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshbn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrcda.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdh.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshmd5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshpubk.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrand.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrsa.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh256.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh512.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsha.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshzlib.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\timing.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\wildcard.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wincons.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\windefs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wingss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhandl.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winmisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnoise.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnojmp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winpgntc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winsftp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wintime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\x11fwd.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\charset\charset.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\macosx\osx.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\network.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\psftp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\putty.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttymem.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttyps.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sftp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgss.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\storage.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\unix\unix.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\rcstuff.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstuff.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\pscp.ico\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\psftp.rc\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/putty/WINDOWS/MSVC/PUTTY.DSW b/putty/WINDOWS/MSVC/PUTTY.DSW
new file mode 100644 (file)
index 0000000..c70ae89
--- /dev/null
@@ -0,0 +1,35 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00\r
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r
+\r
+###############################################################################\r
+\r
+Project: "pageant"=".\pageant\pageant.dsp" - Package Owner=<4>\r
+Project: "plink"=".\plink\plink.dsp" - Package Owner=<4>\r
+Project: "pscp"=".\pscp\pscp.dsp" - Package Owner=<4>\r
+Project: "psftp"=".\psftp\psftp.dsp" - Package Owner=<4>\r
+Project: "putty"=".\putty\putty.dsp" - Package Owner=<4>\r
+Project: "puttygen"=".\puttygen\puttygen.dsp" - Package Owner=<4>\r
+Project: "puttytel"=".\puttytel\puttytel.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Global:\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<3>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
diff --git a/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP b/putty/WINDOWS/MSVC/PUTTY/PUTTY.DSP
new file mode 100644 (file)
index 0000000..69a2480
--- /dev/null
@@ -0,0 +1,486 @@
+# Microsoft Developer Studio Project File - Name="putty" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Application" 0x0101\r
+\r
+CFG=putty - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "putty.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "putty.mak" CFG="putty - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "putty - Win32 Release" (based on "Win32 (x86) Application")\r
+!MESSAGE "putty - Win32 Debug" (based on "Win32 (x86) Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "putty - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "NDEBUG"\r
+# ADD RSC /l 0x809 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /machine:I386\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "putty - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "_DEBUG"\r
+# ADD RSC /l 0x809 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "putty - Win32 Release"\r
+# Name "putty - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\be_all_s.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cmdline.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\config.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\dialog.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldiscucs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\logging.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\minibidi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pinger.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\portfwd.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\raw.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\rlogin.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sercfg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\settings.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.c\r
+\r
+!IF  "$(CFG)" == "putty - Win32 Release"\r
+\r
+!ELSEIF  "$(CFG)" == "putty - Win32 Debug"\r
+\r
+# ADD CPP /Zi\r
+\r
+!ENDIF \r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshaes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssharcf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshblowf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshbn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshcrcda.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdh.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshmd5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshpubk.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrand.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrsa.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh256.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh512.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsha.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshzlib.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\telnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\terminal.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\timing.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\wcwidth.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\wildcard.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\sizetip.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wincfg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winctrls.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\windefs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\windlg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\window.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wingss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhandl.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winjump.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winmisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnoise.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winpgntc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winprint.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winser.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wintime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winucs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winutils.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winx11.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\x11fwd.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\charset\charset.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\dialog.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldisc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\macosx\osx.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\network.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pgssapi.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\putty.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttymem.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttyps.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgss.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshgssc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\storage.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\terminal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\unix\unix.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\rcstuff.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\win_res.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstuff.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\putty.ico\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\putty.rc\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\puttycfg.ico\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP b/putty/WINDOWS/MSVC/PUTTYGEN/PUTTYGEN.DSP
new file mode 100644 (file)
index 0000000..6ee999c
--- /dev/null
@@ -0,0 +1,289 @@
+# Microsoft Developer Studio Project File - Name="puttygen" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Application" 0x0101\r
+\r
+CFG=puttygen - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "puttygen.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "puttygen.mak" CFG="puttygen - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "puttygen - Win32 Release" (based on "Win32 (x86) Application")\r
+!MESSAGE "puttygen - Win32 Debug" (based on "Win32 (x86) Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "puttygen - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "NDEBUG"\r
+# ADD RSC /l 0x809 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /machine:I386\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "puttygen - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "_DEBUG"\r
+# ADD RSC /l 0x809 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "puttygen - Win32 Release"\r
+# Name "puttygen - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\import.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\notiming.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshaes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshbn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshdssg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshmd5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshprime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshpubk.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrand.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrsa.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshrsag.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh256.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsh512.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sshsha.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winctrls.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winmisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnoise.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnojmp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winpgen.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wintime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winutils.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\charset\charset.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\dialog.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\macosx\osx.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\network.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\putty.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttymem.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttyps.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\storage.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\unix\unix.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\rcstuff.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstuff.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\puttygen.ico\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\puttygen.rc\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP b/putty/WINDOWS/MSVC/PUTTYTEL/PUTTYTEL.DSP
new file mode 100644 (file)
index 0000000..cd74ae6
--- /dev/null
@@ -0,0 +1,361 @@
+# Microsoft Developer Studio Project File - Name="puttytel" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Application" 0x0101\r
+\r
+CFG=puttytel - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "puttytel.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "puttytel.mak" CFG="puttytel - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "puttytel - Win32 Release" (based on "Win32 (x86) Application")\r
+!MESSAGE "puttytel - Win32 Debug" (based on "Win32 (x86) Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "puttytel - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "NDEBUG"\r
+# ADD RSC /l 0x809 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /machine:I386\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "puttytel - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /I "..\..\..\macosx/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x809 /d "_DEBUG"\r
+# ADD RSC /l 0x809 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 advapi32.lib comctl32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib winmm.lib winspool.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "puttytel - Win32 Release"\r
+# Name "puttytel - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\be_nos_s.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\cmdline.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\config.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\dialog.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldiscucs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\logging.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\minibidi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\nocproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\nogss.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\pinger.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\raw.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\rlogin.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\sercfg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\settings.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\telnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\terminal.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\timing.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\wcwidth.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\sizetip.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wincfg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winctrls.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\windefs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\windlg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\window.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhandl.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winjump.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winmisc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winprint.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winproxy.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winser.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\wintime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winucs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winutils.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\charset\charset.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\dialog.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ldisc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\macosx\osx.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\misc.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\network.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\proxy.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\putty.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttymem.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\puttyps.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\ssh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\storage.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\terminal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\tree234.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\unix\unix.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\rcstuff.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\win_res.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winhelp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\winstuff.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\putty.ico\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\puttycfg.ico\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\..\windows\puttytel.rc\r
+# End Source File\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/putty/WINDOWS/PAGEANT.ICO b/putty/WINDOWS/PAGEANT.ICO
new file mode 100644 (file)
index 0000000..93274fd
Binary files /dev/null and b/putty/WINDOWS/PAGEANT.ICO differ
diff --git a/putty/WINDOWS/PAGEANT.MFT b/putty/WINDOWS/PAGEANT.MFT
new file mode 100644 (file)
index 0000000..3934c8d
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<!-- Do not attempt to do anything clever with this file, as some versions of\r
+     Windows are very sensitive to the exact format.\r
+     Hence, some facts below are fibs. -->\r
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\r
+<assemblyIdentity\r
+   version="0.0.0.0"\r
+   processorArchitecture="x86"\r
+   name="Pageant"\r
+   type="win32" />\r
+   <description>PuTTY SSH authentication agent</description>\r
+   <dependency>\r
+   <dependentAssembly>\r
+        <!-- Load Common Controls 6 instead of 5 to get WinXP native-\r
+             looking controls in the client area. -->\r
+        <assemblyIdentity type="win32"\r
+             name="Microsoft.Windows.Common-Controls"\r
+             version="6.0.0.0" \r
+             publicKeyToken="6595b64144ccf1df"\r
+             language="*" \r
+             processorArchitecture="x86"/> \r
+   </dependentAssembly>\r
+   </dependency>\r
+   <!-- Declare us to be "DPI-aware". -->\r
+   <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">\r
+     <asmv3:windowsSettings\r
+         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">\r
+       <dpiAware>true</dpiAware>\r
+     </asmv3:windowsSettings>\r
+   </asmv3:application>\r
+</assembly>\r
diff --git a/putty/WINDOWS/PAGEANT.RC b/putty/WINDOWS/PAGEANT.RC
new file mode 100644 (file)
index 0000000..f6422f3
--- /dev/null
@@ -0,0 +1,95 @@
+/*\r
+ * Windows resources for Pageant.\r
+ */\r
+\r
+#include "rcstuff.h"\r
+\r
+#define APPNAME "Pageant"\r
+#define APPDESC "PuTTY SSH authentication agent"\r
+\r
+200 ICON "pageant.ico"\r
+201 ICON "pageants.ico"\r
+\r
+210 DIALOG DISCARDABLE 0, 0, 140, 60\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "Pageant: Enter Passphrase"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    CTEXT "Enter passphrase for key", 100, 10, 6, 120, 8\r
+    CTEXT "", 101, 10, 16, 120, 8\r
+    EDITTEXT 102, 10, 26, 120, 12, ES_PASSWORD | ES_AUTOHSCROLL\r
+    DEFPUSHBUTTON "O&K", IDOK, 20, 42, 40, 14\r
+    PUSHBUTTON "&Cancel", IDCANCEL, 80, 42, 40, 14\r
+END\r
+\r
+211 DIALOG DISCARDABLE 0, 0, 330, 200\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "Pageant Key List"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    LISTBOX 100, 10, 10, 310, 155,\r
+        LBS_EXTENDEDSEL | LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | WS_TABSTOP\r
+    PUSHBUTTON "&Add Key", 101, 75, 162, 60, 14\r
+    PUSHBUTTON "&Remove Key", 102, 195, 162, 60, 14\r
+    PUSHBUTTON "&Help", 103, 10, 182, 50, 14\r
+    DEFPUSHBUTTON "&Close", IDOK, 270, 182, 50, 14\r
+END\r
+\r
+/* Accelerators used: cl */\r
+213 DIALOG DISCARDABLE 140, 40, 136, 70\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "About Pageant"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14\r
+    PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14\r
+    CTEXT "Pageant", 102, 10, 6, 120, 8\r
+    CTEXT "", 100, 10, 16, 120, 16\r
+    CTEXT "\251 1997-2011 Simon Tatham. All rights reserved.",\r
+          103, 10, 34, 120, 16\r
+END\r
+\r
+/* No accelerators used */\r
+214 DIALOG DISCARDABLE 50, 50, 226, 263\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "PuTTY Licence"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14\r
+\r
+    LTEXT "Copyright \251 1997-2011 Simon Tatham", 1000, 10, 10, 206, 8\r
+\r
+    LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8\r
+    LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8\r
+    LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8\r
+    LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8\r
+\r
+    LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8\r
+    LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8\r
+    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8\r
+    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8\r
+    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8\r
+    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8\r
+    LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8\r
+\r
+    LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8\r
+    LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8\r
+\r
+    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8\r
+    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8\r
+    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8\r
+    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8\r
+    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8\r
+    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8\r
+    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8\r
+    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8\r
+    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8\r
+    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8\r
+\r
+END\r
+\r
+#include "version.rc2"\r
+\r
+#ifndef NO_MANIFESTS\r
+1 RT_MANIFEST "pageant.mft"\r
+#endif /* NO_MANIFESTS */\r
diff --git a/putty/WINDOWS/PAGEANTS.ICO b/putty/WINDOWS/PAGEANTS.ICO
new file mode 100644 (file)
index 0000000..28a6d01
Binary files /dev/null and b/putty/WINDOWS/PAGEANTS.ICO differ
diff --git a/putty/WINDOWS/PLINK.RC b/putty/WINDOWS/PLINK.RC
new file mode 100644 (file)
index 0000000..7dd7ce9
--- /dev/null
@@ -0,0 +1,8 @@
+#include "rcstuff.h"\r
+\r
+#define APPNAME "Plink"\r
+#define APPDESC "Command-line SSH, Telnet, and Rlogin client"\r
+\r
+200 ICON "putty.ico"\r
+\r
+#include "version.rc2"\r
diff --git a/putty/WINDOWS/PSCP.ICO b/putty/WINDOWS/PSCP.ICO
new file mode 100644 (file)
index 0000000..2b3c1be
Binary files /dev/null and b/putty/WINDOWS/PSCP.ICO differ
diff --git a/putty/WINDOWS/PSCP.RC b/putty/WINDOWS/PSCP.RC
new file mode 100644 (file)
index 0000000..54617e1
--- /dev/null
@@ -0,0 +1,8 @@
+#include "rcstuff.h"\r
+\r
+#define APPNAME "PSCP"\r
+#define APPDESC "Command-line SCP/SFTP client"\r
+\r
+200 ICON "pscp.ico"\r
+\r
+#include "version.rc2"\r
diff --git a/putty/WINDOWS/PSFTP.RC b/putty/WINDOWS/PSFTP.RC
new file mode 100644 (file)
index 0000000..c3efc6f
--- /dev/null
@@ -0,0 +1,8 @@
+#include "rcstuff.h"\r
+\r
+#define APPNAME "PSFTP"\r
+#define APPDESC "Command-line interactive SFTP client"\r
+\r
+200 ICON "pscp.ico"\r
+\r
+#include "version.rc2"\r
diff --git a/putty/WINDOWS/PUTTY.ICO b/putty/WINDOWS/PUTTY.ICO
new file mode 100644 (file)
index 0000000..fcd641a
Binary files /dev/null and b/putty/WINDOWS/PUTTY.ICO differ
diff --git a/putty/WINDOWS/PUTTY.ISS b/putty/WINDOWS/PUTTY.ISS
new file mode 100644 (file)
index 0000000..fb979ca
--- /dev/null
@@ -0,0 +1,106 @@
+; -*- no -*-\r
+; $Id: putty.iss 9202 2011-07-12 18:26:18Z simon $\r
+;\r
+; -- Inno Setup installer script for PuTTY and its related tools.\r
+;    Last tested with Inno Setup 5.0.8.\r
+;\r
+; TODO for future releases:\r
+;\r
+;  - It might be nice to have an option to add PSCP, Plink and PSFTP to\r
+;    the PATH. See wish `installer-addpath'.\r
+;\r
+;  - Maybe a "custom" installation might be useful? Hassle with\r
+;    UninstallDisplayIcon, though.\r
+\r
+[Setup]\r
+AppName=PuTTY\r
+AppVerName=PuTTY version 0.61\r
+VersionInfoTextVersion=Release 0.61\r
+AppVersion=0.61\r
+VersionInfoVersion=0.61.0.0\r
+AppPublisher=Simon Tatham\r
+AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/\r
+AppReadmeFile={app}\README.txt\r
+DefaultDirName={pf}\PuTTY\r
+DefaultGroupName=PuTTY\r
+SetupIconFile=puttyins.ico\r
+UninstallDisplayIcon={app}\putty.exe\r
+ChangesAssociations=yes\r
+;ChangesEnvironment=yes -- when PATH munging is sorted (probably)\r
+Compression=zip/9\r
+AllowNoIcons=yes\r
+\r
+[Files]\r
+; We flag all files with "restartreplace" et al primarily for the benefit\r
+; of unattended un/installations/upgrades, when the user is running one\r
+; of the apps at a time. Without it, the operation will fail noisily in\r
+; this situation.\r
+; This does mean that the user will be prompted to restart their machine\r
+; if any of the files _were_ open during installation (or, if /VERYSILENT\r
+; is used, the machine will be restarted automatically!). The /NORESTART\r
+; flag avoids this.\r
+; It might be nicer to have a "no worries, replace the file next time you\r
+; reboot" option, but the developers have no interest in adding one.\r
+; NB: apparently, using long (non-8.3) filenames with restartreplace is a\r
+; bad idea. (Not that we do.)\r
+Source: "putty.exe"; DestDir: "{app}"; Flags: promptifolder replacesameversion restartreplace uninsrestartdelete\r
+Source: "pageant.exe"; DestDir: "{app}"; Flags: promptifolder replacesameversion restartreplace uninsrestartdelete\r
+Source: "puttygen.exe"; DestDir: "{app}"; Flags: promptifolder replacesameversion restartreplace uninsrestartdelete\r
+Source: "pscp.exe"; DestDir: "{app}"; Flags: promptifolder replacesameversion restartreplace uninsrestartdelete\r
+Source: "psftp.exe"; DestDir: "{app}"; Flags: promptifolder replacesameversion restartreplace uninsrestartdelete\r
+Source: "plink.exe"; DestDir: "{app}"; Flags: promptifolder replacesameversion restartreplace uninsrestartdelete\r
+Source: "website.url"; DestDir: "{app}"; Flags: restartreplace uninsrestartdelete\r
+Source: "..\doc\putty.chm"; DestDir: "{app}"; Flags: restartreplace uninsrestartdelete\r
+Source: "..\doc\putty.hlp"; DestDir: "{app}"; Flags: restartreplace uninsrestartdelete\r
+Source: "..\doc\putty.cnt"; DestDir: "{app}"; Flags: restartreplace uninsrestartdelete\r
+Source: "..\LICENCE"; DestDir: "{app}"; Flags: restartreplace uninsrestartdelete\r
+Source: "README.txt"; DestDir: "{app}"; Flags: isreadme restartreplace uninsrestartdelete\r
+\r
+[Icons]\r
+Name: "{group}\PuTTY"; Filename: "{app}\putty.exe"\r
+; We have to fall back from the .chm to the older .hlp file on some Windows\r
+; versions.\r
+Name: "{group}\PuTTY Manual"; Filename: "{app}\putty.chm"; MinVersion: 4.1,5.0\r
+Name: "{group}\PuTTY Manual"; Filename: "{app}\putty.hlp"; OnlyBelowVersion: 4.1,5.0\r
+Name: "{group}\PuTTY Web Site"; Filename: "{app}\website.url"\r
+Name: "{group}\PSFTP"; Filename: "{app}\psftp.exe"\r
+Name: "{group}\PuTTYgen"; Filename: "{app}\puttygen.exe"\r
+Name: "{group}\Pageant"; Filename: "{app}\pageant.exe"\r
+Name: "{commondesktop}\PuTTY"; Filename: "{app}\putty.exe"; Tasks: desktopicon\common\r
+Name: "{userdesktop}\PuTTY"; Filename: "{app}\putty.exe"; Tasks: desktopicon\user\r
+; Putting this in {commonappdata} doesn't seem to work, on 98SE at least.\r
+Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\PuTTY"; Filename: "{app}\putty.exe"; Tasks: quicklaunchicon\r
+\r
+[Tasks]\r
+Name: desktopicon; Description: "Create a &desktop icon for PuTTY"; GroupDescription: "Additional icons:"; Flags: unchecked\r
+Name: desktopicon\common; Description: "For all users"; GroupDescription: "Additional icons:"; Flags: exclusive unchecked\r
+Name: desktopicon\user; Description: "For the current user only"; GroupDescription: "Additional icons:"; Flags: exclusive unchecked\r
+Name: quicklaunchicon; Description: "Create a &Quick Launch icon for PuTTY (current user only)"; GroupDescription: "Additional icons:"; Flags: unchecked\r
+Name: associate; Description: "&Associate .PPK files (PuTTY Private Key) with Pageant and PuTTYgen"; GroupDescription: "Other tasks:"\r
+\r
+[Registry]\r
+Root: HKCR; Subkey: ".ppk"; ValueType: string; ValueName: ""; ValueData: "PuTTYPrivateKey"; Flags: uninsdeletevalue; Tasks: associate\r
+Root: HKCR; Subkey: "PuTTYPrivateKey"; ValueType: string; ValueName: ""; ValueData: "PuTTY Private Key File"; Flags: uninsdeletekey; Tasks: associate\r
+Root: HKCR; Subkey: "PuTTYPrivateKey\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\pageant.exe,0"; Tasks: associate\r
+Root: HKCR; Subkey: "PuTTYPrivateKey\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\pageant.exe"" ""%1"""; Tasks: associate\r
+Root: HKCR; Subkey: "PuTTYPrivateKey\shell\edit"; ValueType: string; ValueName: ""; ValueData: "&Edit"; Tasks: associate\r
+Root: HKCR; Subkey: "PuTTYPrivateKey\shell\edit\command"; ValueType: string; ValueName: ""; ValueData: """{app}\puttygen.exe"" ""%1"""; Tasks: associate\r
+; Add to PATH on NT-class OS?\r
+\r
+[UninstallRun]\r
+; -cleanup-during-uninstall is an undocumented option that tailors the\r
+; message displayed.\r
+; XXX: it would be nice if this task weren't run if a silent uninstall is\r
+;      requested, but "skipifsilent" is disallowed.\r
+Filename: "{app}\putty.exe"; Parameters: "-cleanup-during-uninstall"; RunOnceId: "PuTTYCleanup"; StatusMsg: "Cleaning up saved sessions etc (optional)..."\r
+\r
+[Messages]\r
+; Since it's possible for the user to be asked to restart their computer,\r
+; we should override the default messages to explain exactly why, so they\r
+; can make an informed decision. (Especially as 95% of users won't need or\r
+; want to restart; see rant above.)\r
+FinishedRestartLabel=One or more [name] programs are still running. Setup will not replace these program files until you restart your computer. Would you like to restart now?\r
+; This message is popped up in a message box on a /SILENT install.\r
+FinishedRestartMessage=One or more [name] programs are still running.%nSetup will not replace these program files until you restart your computer.%n%nWould you like to restart now?\r
+; ...and this comes up if you try to uninstall.\r
+UninstalledAndNeedsRestart=One or more %1 programs are still running.%nThe program files will not be removed until your computer is restarted.%n%nWould you like to restart now?\r
diff --git a/putty/WINDOWS/PUTTY.MFT b/putty/WINDOWS/PUTTY.MFT
new file mode 100644 (file)
index 0000000..7eed30c
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<!-- Do not attempt to do anything clever with this file, as some versions of\r
+     Windows are very sensitive to the exact format.\r
+     Hence, some facts below are fibs. -->\r
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\r
+<assemblyIdentity\r
+   version="0.0.0.0"\r
+   processorArchitecture="x86"\r
+   name="PuTTY"\r
+   type="win32" />\r
+   <description>A network client and terminal emulator</description>\r
+   <dependency>\r
+   <dependentAssembly>\r
+        <!-- Load Common Controls 6 instead of 5 to get WinXP native-\r
+             looking controls in the client area. -->\r
+        <assemblyIdentity type="win32"\r
+             name="Microsoft.Windows.Common-Controls"\r
+             version="6.0.0.0" \r
+             publicKeyToken="6595b64144ccf1df"\r
+             language="*" \r
+             processorArchitecture="x86"/> \r
+   </dependentAssembly>\r
+   </dependency>\r
+   <!-- Declare us to be "DPI-aware". -->\r
+   <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">\r
+     <asmv3:windowsSettings\r
+         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">\r
+       <dpiAware>true</dpiAware>\r
+     </asmv3:windowsSettings>\r
+   </asmv3:application>\r
+</assembly>\r
diff --git a/putty/WINDOWS/PUTTY.RC b/putty/WINDOWS/PUTTY.RC
new file mode 100644 (file)
index 0000000..77c9f21
--- /dev/null
@@ -0,0 +1,10 @@
+#include "rcstuff.h"\r
+\r
+#define APPNAME "PuTTY"\r
+#define APPDESC "SSH, Telnet and Rlogin client"\r
+\r
+#include "win_res.rc2"\r
+\r
+#ifndef NO_MANIFESTS\r
+1 RT_MANIFEST "putty.mft"\r
+#endif /* NO_MANIFESTS */\r
diff --git a/putty/WINDOWS/PUTTYCFG.ICO b/putty/WINDOWS/PUTTYCFG.ICO
new file mode 100644 (file)
index 0000000..bb32be8
Binary files /dev/null and b/putty/WINDOWS/PUTTYCFG.ICO differ
diff --git a/putty/WINDOWS/PUTTYGEN.ICO b/putty/WINDOWS/PUTTYGEN.ICO
new file mode 100644 (file)
index 0000000..ddc8305
Binary files /dev/null and b/putty/WINDOWS/PUTTYGEN.ICO differ
diff --git a/putty/WINDOWS/PUTTYGEN.MFT b/putty/WINDOWS/PUTTYGEN.MFT
new file mode 100644 (file)
index 0000000..5f394a1
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<!-- Do not attempt to do anything clever with this file, as some versions of\r
+     Windows are very sensitive to the exact format.\r
+     Hence, some facts below are fibs. -->\r
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\r
+<assemblyIdentity\r
+   version="0.0.0.0"\r
+   processorArchitecture="x86"\r
+   name="PuTTYgen"\r
+   type="win32" />\r
+   <description>SSH key generator for PuTTY</description>\r
+   <dependency>\r
+   <dependentAssembly>\r
+        <!-- Load Common Controls 6 instead of 5 to get WinXP native-\r
+             looking controls in the client area. -->\r
+        <assemblyIdentity type="win32"\r
+             name="Microsoft.Windows.Common-Controls"\r
+             version="6.0.0.0" \r
+             publicKeyToken="6595b64144ccf1df"\r
+             language="*" \r
+             processorArchitecture="x86"/> \r
+   </dependentAssembly>\r
+   </dependency>\r
+   <!-- Declare us to be "DPI-aware". -->\r
+   <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">\r
+     <asmv3:windowsSettings\r
+         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">\r
+       <dpiAware>true</dpiAware>\r
+     </asmv3:windowsSettings>\r
+   </asmv3:application>\r
+</assembly>\r
diff --git a/putty/WINDOWS/PUTTYGEN.RC b/putty/WINDOWS/PUTTYGEN.RC
new file mode 100644 (file)
index 0000000..1cea261
--- /dev/null
@@ -0,0 +1,88 @@
+/*\r
+ * Windows resources for PuTTYgen.\r
+ */\r
+\r
+#include "rcstuff.h"\r
+\r
+#define APPNAME "PuTTYgen"\r
+#define APPDESC "PuTTY SSH key generation utility"\r
+\r
+200 ICON "puttygen.ico"\r
+\r
+201 DIALOG DISCARDABLE 0, 0, 318, 270\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "PuTTY Key Generator"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+END\r
+\r
+210 DIALOG DISCARDABLE 0, 0, 140, 60\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "PuTTYgen: Enter Passphrase"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    CTEXT "Enter passphrase for key", 100, 10, 6, 120, 8\r
+    CTEXT "", 101, 10, 16, 120, 8\r
+    EDITTEXT 102, 10, 26, 120, 12, ES_PASSWORD | ES_AUTOHSCROLL\r
+    DEFPUSHBUTTON "O&K", IDOK, 20, 42, 40, 14\r
+    PUSHBUTTON "&Cancel", IDCANCEL, 80, 42, 40, 14\r
+END\r
+\r
+/* Accelerators used: cl */\r
+213 DIALOG DISCARDABLE 140, 40, 136, 70\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "About PuTTYgen"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14\r
+    PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14\r
+    CTEXT "PuTTYgen", 102, 10, 6, 120, 8\r
+    CTEXT "", 100, 10, 16, 120, 16\r
+    CTEXT "\251 1997-2011 Simon Tatham. All rights reserved.",\r
+          103, 10, 34, 120, 16\r
+END\r
+\r
+/* No accelerators used */\r
+214 DIALOG DISCARDABLE 50, 50, 226, 263\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "PuTTY Licence"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14\r
+\r
+    LTEXT "Copyright \251 1997-2011 Simon Tatham", 1000, 10, 10, 206, 8\r
+\r
+    LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8\r
+    LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8\r
+    LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8\r
+    LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8\r
+\r
+    LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8\r
+    LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8\r
+    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8\r
+    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8\r
+    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8\r
+    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8\r
+    LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8\r
+\r
+    LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8\r
+    LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8\r
+\r
+    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8\r
+    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8\r
+    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8\r
+    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8\r
+    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8\r
+    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8\r
+    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8\r
+    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8\r
+    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8\r
+    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8\r
+\r
+END\r
+\r
+#include "version.rc2"\r
+\r
+#ifndef NO_MANIFESTS\r
+1 RT_MANIFEST "puttygen.mft"\r
+#endif /* NO_MANIFESTS */\r
diff --git a/putty/WINDOWS/PUTTYINS.ICO b/putty/WINDOWS/PUTTYINS.ICO
new file mode 100644 (file)
index 0000000..2bd92c6
Binary files /dev/null and b/putty/WINDOWS/PUTTYINS.ICO differ
diff --git a/putty/WINDOWS/PUTTYTEL.RC b/putty/WINDOWS/PUTTYTEL.RC
new file mode 100644 (file)
index 0000000..86964b6
--- /dev/null
@@ -0,0 +1,11 @@
+#include "rcstuff.h"\r
+\r
+#define APPNAME "PuTTYtel"\r
+#define APPDESC "Telnet and Rlogin client"\r
+\r
+#include "win_res.rc2"\r
+\r
+#ifndef NO_MANIFESTS\r
+/* FIXME */\r
+1 RT_MANIFEST "putty.mft"\r
+#endif /* NO_MANIFESTS */\r
diff --git a/putty/WINDOWS/RCSTUFF.H b/putty/WINDOWS/RCSTUFF.H
new file mode 100644 (file)
index 0000000..74280e5
--- /dev/null
@@ -0,0 +1,50 @@
+/*\r
+ * Miscellaneous stuff to include in all .rc files.\r
+ */\r
+\r
+#ifndef PUTTY_RCSTUFF_H\r
+#define PUTTY_RCSTUFF_H\r
+\r
+#ifdef __LCC__\r
+#include <win.h>\r
+#else\r
+\r
+/* Some compilers, like Borland, don't have winresrc.h */\r
+#ifndef NO_WINRESRC_H\r
+#ifndef MSVC4\r
+#include <winresrc.h>\r
+#else\r
+#include <winres.h>\r
+#endif\r
+#endif\r
+\r
+#endif /* end #ifdef __LCC__ */\r
+\r
+/* Some systems don't define this, so I do it myself if necessary */\r
+#ifndef TCS_MULTILINE\r
+#define TCS_MULTILINE 0x0200\r
+#endif\r
+\r
+/* Likewise */\r
+#ifndef RT_MANIFEST\r
+#define RT_MANIFEST 24\r
+#endif\r
+\r
+/* LCC is the offender here. */\r
+#ifndef VS_FF_DEBUG\r
+#define VS_FF_DEBUG        1\r
+#endif\r
+#ifndef VS_FF_PRERELEASE\r
+#define VS_FF_PRERELEASE   2\r
+#endif\r
+#ifndef VS_FF_PRIVATEBUILD\r
+#define VS_FF_PRIVATEBUILD 8\r
+#endif\r
+#ifndef VOS__WINDOWS32\r
+#define VOS__WINDOWS32     4\r
+#endif\r
+#ifndef VFT_APP\r
+#define VFT_APP            1\r
+#endif\r
+\r
+#endif /* PUTTY_RCSTUFF_H */\r
diff --git a/putty/WINDOWS/README.TXT b/putty/WINDOWS/README.TXT
new file mode 100644 (file)
index 0000000..7d35728
--- /dev/null
@@ -0,0 +1,40 @@
+PuTTY README\r\r
+============\r\r
+\r\r
+This is the README file for the PuTTY installer distribution. If\r\r
+you're reading this, you've probably just run our installer and\r\r
+installed PuTTY on your system.\r\r
+\r\r
+What should I do next?\r\r
+----------------------\r\r
+\r\r
+If you want to use PuTTY to connect to other computers, or use PSFTP\r\r
+to transfer files, you should just be able to run them from the\r\r
+Start menu.\r\r
+\r\r
+If you want to use the command-line-only file transfer utility PSCP,\r\r
+you will probably want to put the PuTTY installation directory on\r\r
+your PATH. How you do this depends on your version of Windows. On\r\r
+Windows NT, 2000, and XP, you can set it using Control Panel > System;\r\r
+on Windows 95, 98, and Me, you will need to edit AUTOEXEC.BAT. Consult\r\r
+your Windows manuals for details.\r\r
+\r\r
+Some versions of Windows will refuse to run HTML Help files (.CHM)\r\r
+if they are installed on a network drive. If you have installed\r\r
+PuTTY on a network drive, you might want to check that the help file\r\r
+works properly. If not, see http://support.microsoft.com/kb/896054\r\r
+for information on how to solve this problem.\r\r
+\r\r
+What do I do if it doesn't work?\r\r
+--------------------------------\r\r
+\r\r
+The PuTTY home web site is\r\r
+\r\r
+    http://www.chiark.greenend.org.uk/~sgtatham/putty/\r\r
+\r\r
+Here you will find our list of known bugs and pending feature\r\r
+requests. If your problem is not listed in there, or in the FAQ, or\r\r
+in the manuals, read the Feedback page to find out how to report\r\r
+bugs to us. PLEASE read the Feedback page carefully: it is there to\r\r
+save you time as well as us. Do not send us one-line bug reports\r\r
+telling us `it doesn't work'.\r\r
diff --git a/putty/WINDOWS/SIZETIP.C b/putty/WINDOWS/SIZETIP.C
new file mode 100644 (file)
index 0000000..951c569
--- /dev/null
@@ -0,0 +1,194 @@
+/*\r
+ * sizetip.c - resize tips for PuTTY(tel) terminal window.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <tchar.h>\r
+\r
+#include "putty.h"\r
+\r
+static ATOM tip_class = 0;\r
+\r
+static HFONT tip_font;\r
+static COLORREF tip_bg;\r
+static COLORREF tip_text;\r
+\r
+static LRESULT CALLBACK SizeTipWndProc(HWND hWnd, UINT nMsg,\r
+                                      WPARAM wParam, LPARAM lParam)\r
+{\r
+\r
+    switch (nMsg) {\r
+      case WM_ERASEBKGND:\r
+       return TRUE;\r
+\r
+      case WM_PAINT:\r
+       {\r
+           HBRUSH hbr;\r
+           HGDIOBJ holdbr;\r
+           RECT cr;\r
+           int wtlen;\r
+           LPTSTR wt;\r
+           HDC hdc;\r
+\r
+           PAINTSTRUCT ps;\r
+           hdc = BeginPaint(hWnd, &ps);\r
+\r
+           SelectObject(hdc, tip_font);\r
+           SelectObject(hdc, GetStockObject(BLACK_PEN));\r
+\r
+           hbr = CreateSolidBrush(tip_bg);\r
+           holdbr = SelectObject(hdc, hbr);\r
+\r
+           GetClientRect(hWnd, &cr);\r
+           Rectangle(hdc, cr.left, cr.top, cr.right, cr.bottom);\r
+\r
+           wtlen = GetWindowTextLength(hWnd);\r
+           wt = (LPTSTR) snewn(wtlen + 1, TCHAR);\r
+           GetWindowText(hWnd, wt, wtlen + 1);\r
+\r
+           SetTextColor(hdc, tip_text);\r
+           SetBkColor(hdc, tip_bg);\r
+\r
+           TextOut(hdc, cr.left + 3, cr.top + 3, wt, wtlen);\r
+\r
+           sfree(wt);\r
+\r
+           SelectObject(hdc, holdbr);\r
+           DeleteObject(hbr);\r
+\r
+           EndPaint(hWnd, &ps);\r
+       }\r
+       return 0;\r
+\r
+      case WM_NCHITTEST:\r
+       return HTTRANSPARENT;\r
+\r
+      case WM_DESTROY:\r
+       DeleteObject(tip_font);\r
+       tip_font = NULL;\r
+       break;\r
+\r
+      case WM_SETTEXT:\r
+       {\r
+           LPCTSTR str = (LPCTSTR) lParam;\r
+           SIZE sz;\r
+           HDC hdc = CreateCompatibleDC(NULL);\r
+\r
+           SelectObject(hdc, tip_font);\r
+           GetTextExtentPoint32(hdc, str, _tcslen(str), &sz);\r
+\r
+           SetWindowPos(hWnd, NULL, 0, 0, sz.cx + 6, sz.cy + 6,\r
+                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);\r
+           InvalidateRect(hWnd, NULL, FALSE);\r
+\r
+           DeleteDC(hdc);\r
+       }\r
+       break;\r
+    }\r
+\r
+    return DefWindowProc(hWnd, nMsg, wParam, lParam);\r
+}\r
+\r
+static HWND tip_wnd = NULL;\r
+static int tip_enabled = 0;\r
+\r
+void UpdateSizeTip(HWND src, int cx, int cy)\r
+{\r
+    TCHAR str[32];\r
+\r
+    if (!tip_enabled)\r
+       return;\r
+\r
+    if (!tip_wnd) {\r
+       NONCLIENTMETRICS nci;\r
+\r
+       /* First make sure the window class is registered */\r
+\r
+       if (!tip_class) {\r
+           WNDCLASS wc;\r
+           wc.style = CS_HREDRAW | CS_VREDRAW;\r
+           wc.lpfnWndProc = SizeTipWndProc;\r
+           wc.cbClsExtra = 0;\r
+           wc.cbWndExtra = 0;\r
+           wc.hInstance = hinst;\r
+           wc.hIcon = NULL;\r
+           wc.hCursor = NULL;\r
+           wc.hbrBackground = NULL;\r
+           wc.lpszMenuName = NULL;\r
+           wc.lpszClassName = "SizeTipClass";\r
+\r
+           tip_class = RegisterClass(&wc);\r
+       }\r
+#if 0\r
+       /* Default values based on Windows Standard color scheme */\r
+\r
+       tip_font = GetStockObject(SYSTEM_FONT);\r
+       tip_bg = RGB(255, 255, 225);\r
+       tip_text = RGB(0, 0, 0);\r
+#endif\r
+\r
+       /* Prepare other GDI objects and drawing info */\r
+\r
+       tip_bg = GetSysColor(COLOR_INFOBK);\r
+       tip_text = GetSysColor(COLOR_INFOTEXT);\r
+\r
+       memset(&nci, 0, sizeof(NONCLIENTMETRICS));\r
+       nci.cbSize = sizeof(NONCLIENTMETRICS);\r
+       SystemParametersInfo(SPI_GETNONCLIENTMETRICS,\r
+                            sizeof(NONCLIENTMETRICS), &nci, 0);\r
+       tip_font = CreateFontIndirect(&nci.lfStatusFont);\r
+    }\r
+\r
+    /* Generate the tip text */\r
+\r
+    sprintf(str, "%dx%d", cx, cy);\r
+\r
+    if (!tip_wnd) {\r
+       HDC hdc;\r
+       SIZE sz;\r
+       RECT wr;\r
+       int ix, iy;\r
+\r
+       /* calculate the tip's size */\r
+\r
+       hdc = CreateCompatibleDC(NULL);\r
+       GetTextExtentPoint32(hdc, str, _tcslen(str), &sz);\r
+       DeleteDC(hdc);\r
+\r
+       GetWindowRect(src, &wr);\r
+\r
+       ix = wr.left;\r
+       if (ix < 16)\r
+           ix = 16;\r
+\r
+       iy = wr.top - sz.cy;\r
+       if (iy < 16)\r
+           iy = 16;\r
+\r
+       /* Create the tip window */\r
+\r
+       tip_wnd =\r
+           CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,\r
+                          MAKEINTRESOURCE(tip_class), str, WS_POPUP, ix,\r
+                          iy, sz.cx, sz.cy, NULL, NULL, hinst, NULL);\r
+\r
+       ShowWindow(tip_wnd, SW_SHOWNOACTIVATE);\r
+\r
+    } else {\r
+\r
+       /* Tip already exists, just set the text */\r
+\r
+       SetWindowText(tip_wnd, str);\r
+    }\r
+}\r
+\r
+void EnableSizeTip(int bEnable)\r
+{\r
+    if (tip_wnd && !bEnable) {\r
+       DestroyWindow(tip_wnd);\r
+       tip_wnd = NULL;\r
+    }\r
+\r
+    tip_enabled = bEnable;\r
+}\r
diff --git a/putty/WINDOWS/VERSION.RC2 b/putty/WINDOWS/VERSION.RC2
new file mode 100644 (file)
index 0000000..6a6e591
--- /dev/null
@@ -0,0 +1,134 @@
+/*\r
+ * Standard Windows version information.\r
+ * (For inclusion in other .rc files with appropriate macro definitions.)\r
+ * FIXME: This file is called '.rc2' rather than '.rc' to avoid MSVC trying\r
+ * to compile it on its own when using the project files. Nicer solutions\r
+ * welcome.\r
+ */\r
+\r
+/*\r
+ * Binary versions in Windows are major.minor.build.revision. Each\r
+ * component is 16-bit.\r
+ * Here we have:\r
+ *   major.minor\r
+ *     PuTTY version number (e.g. 0.58). (We've made a policy decision\r
+ *     that these will be numeric from now on.)\r
+ *     Present in releases and snapshots (for the sake of monotonicity\r
+ *     in version numbers).\r
+ *   build\r
+ *     In releases, always 0.\r
+ *     In snapshots, nearest Subversion revision. (It shouldn't be\r
+ *     assumed that only one binary will have a given build number, of\r
+ *     course.)\r
+ *   revision\r
+ *     Reserved; always 0.\r
+ *\r
+ * Examples of these version numbers:\r
+ *   Release:  0.58.0.0            (but 0.58 didn't have a VERSIONINFO resource)\r
+ *   Snapshot: 0.58.6356.0  (between 0.58 and the next release)\r
+ *   Local:    0.0.0.0\r
+ */\r
+\r
+/*\r
+ * Mechanics of version naming/numbering.\r
+ * (This is a ripoff of ../version.c.)\r
+ */\r
+\r
+#define STR1(x) #x\r
+#define STR(x) STR1(x)\r
+\r
+/* We keep this around even for snapshots, for monotonicity of version\r
+ * numbering. It needs to be kept up to date. NB _comma_-separated. */\r
+#define BASE_VERSION 0,61\r
+\r
+#if defined SNAPSHOT\r
+\r
+/* Make SVN_REV mandatory for snapshots, to avoid issuing binary\r
+ * version numbers that look like full releases. */\r
+#ifndef SVN_REV\r
+#error SVN_REV not defined/nonzero for snapshot build\r
+#endif\r
+\r
+#define VERSION_TEXT "Development snapshot " STR(SNAPSHOT) ":r" STR(SVN_REV)\r
+#ifdef MODIFIED\r
+#define BINARY_VERSION 0,0,0,0\r
+#else\r
+#define BINARY_VERSION BASE_VERSION,SVN_REV,0\r
+#endif\r
+\r
+#elif defined RELEASE\r
+\r
+#define VERSION_TEXT "Release " STR(RELEASE)\r
+#define BINARY_VERSION BASE_VERSION,0,0\r
+\r
+#elif defined SVN_REV\r
+\r
+#define VERSION_TEXT "Custom build r" STR(SVN_REV)\r
+#ifdef MODIFIED\r
+#define BINARY_VERSION 0,0,0,0\r
+#else\r
+#define BINARY_VERSION BASE_VERSION,SVN_REV,0\r
+#endif\r
+\r
+#else\r
+\r
+/* We can't reliably get the same date and time as version.c, so\r
+ * we won't bother trying. */\r
+#define VERSION_TEXT "Unidentified build"\r
+#define BINARY_VERSION 0,0,0,0\r
+\r
+#endif\r
+\r
+/*\r
+ * The actual VERSIONINFO resource.\r
+ */\r
+VS_VERSION_INFO VERSIONINFO\r
+/* (None of this "fixed" info appears to be trivially user-visible on\r
+ * Win98SE. The binary version does show up on Win2K.) */\r
+FILEVERSION    BINARY_VERSION\r
+PRODUCTVERSION BINARY_VERSION  /* version of whole suite */\r
+FILEFLAGSMASK  VS_FF_DEBUG | VS_FF_PRERELEASE | VS_FF_PRIVATEBUILD\r
+FILEFLAGS      0x0L\r
+#if defined DEBUG\r
+               | VS_FF_DEBUG\r
+#endif\r
+#if defined SNAPSHOT\r
+               | VS_FF_PRERELEASE\r
+#elif !defined RELEASE\r
+               | VS_FF_PRIVATEBUILD\r
+#endif\r
+FILEOS         VOS__WINDOWS32\r
+FILETYPE       VFT_APP\r
+FILESUBTYPE    0x0L /* n/a for VFT_APP */\r
+BEGIN\r
+    /* (On Win98SE and Win2K, we can see most of this on the Version tab\r
+     * in the file properties in Explorer.) */\r
+    BLOCK "StringFileInfo"\r
+    BEGIN\r
+       /* "lang-charset" LLLLCCCC = (UK English, Unicode) */\r
+       BLOCK "080904B0"\r
+       BEGIN\r
+           VALUE "CompanyName",        "Simon Tatham"  /* required :/ */\r
+           VALUE "ProductName",        "PuTTY suite"\r
+           VALUE "FileDescription",    APPDESC\r
+           VALUE "InternalName",       APPNAME\r
+           VALUE "OriginalFilename",   APPNAME\r
+           VALUE "FileVersion",        VERSION_TEXT\r
+           VALUE "ProductVersion",     VERSION_TEXT\r
+           VALUE "LegalCopyright",     "Copyright \251 1997-2011 Simon Tatham."\r
+#if (!defined SNAPSHOT) && (!defined RELEASE)\r
+           /* Only if VS_FF_PRIVATEBUILD. */\r
+           VALUE "PrivateBuild",       VERSION_TEXT /* NBI */\r
+#endif\r
+       END\r
+    END\r
+    BLOCK "VarFileInfo"\r
+    BEGIN\r
+       /* Once again -- same meanings -- apparently necessary */\r
+       VALUE "Translation", 0x809, 1200\r
+    END\r
+END\r
+\r
+#undef VERSION_TEXT\r
+#undef BASE_VERSION\r
+#undef BINARY_VERSION\r
diff --git a/putty/WINDOWS/WEBSITE.URL b/putty/WINDOWS/WEBSITE.URL
new file mode 100644 (file)
index 0000000..4b50369
Binary files /dev/null and b/putty/WINDOWS/WEBSITE.URL differ
diff --git a/putty/WINDOWS/WINCFG.C b/putty/WINDOWS/WINCFG.C
new file mode 100644 (file)
index 0000000..60694fc
--- /dev/null
@@ -0,0 +1,404 @@
+/*\r
+ * wincfg.c - the Windows-specific parts of the PuTTY configuration\r
+ * box.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "storage.h"\r
+\r
+static void about_handler(union control *ctrl, void *dlg,\r
+                         void *data, int event)\r
+{\r
+    HWND *hwndp = (HWND *)ctrl->generic.context.p;\r
+\r
+    if (event == EVENT_ACTION) {\r
+       modal_about_box(*hwndp);\r
+    }\r
+}\r
+\r
+static void help_handler(union control *ctrl, void *dlg,\r
+                        void *data, int event)\r
+{\r
+    HWND *hwndp = (HWND *)ctrl->generic.context.p;\r
+\r
+    if (event == EVENT_ACTION) {\r
+       show_help(*hwndp);\r
+    }\r
+}\r
+\r
+static void variable_pitch_handler(union control *ctrl, void *dlg,\r
+                                   void *data, int event)\r
+{\r
+    if (event == EVENT_REFRESH) {\r
+       dlg_checkbox_set(ctrl, dlg, !dlg_get_fixed_pitch_flag(dlg));\r
+    } else if (event == EVENT_VALCHANGE) {\r
+       dlg_set_fixed_pitch_flag(dlg, !dlg_checkbox_get(ctrl, dlg));\r
+    }\r
+}\r
+\r
+void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,\r
+                         int midsession, int protocol)\r
+{\r
+    struct controlset *s;\r
+    union control *c;\r
+    char *str;\r
+\r
+    if (!midsession) {\r
+       /*\r
+        * Add the About and Help buttons to the standard panel.\r
+        */\r
+       s = ctrl_getset(b, "", "", "");\r
+       c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help),\r
+                           about_handler, P(hwndp));\r
+       c->generic.column = 0;\r
+       if (has_help) {\r
+           c = ctrl_pushbutton(s, "Help", 'h', HELPCTX(no_help),\r
+                               help_handler, P(hwndp));\r
+           c->generic.column = 1;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Full-screen mode is a Windows peculiarity; hence\r
+     * scrollbar_in_fullscreen is as well.\r
+     */\r
+    s = ctrl_getset(b, "Window", "scrollback",\r
+                   "Control the scrollback in the window");\r
+    ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i',\r
+                 HELPCTX(window_scrollback),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,scrollbar_in_fullscreen)));\r
+    /*\r
+     * Really this wants to go just after `Display scrollbar'. See\r
+     * if we can find that control, and do some shuffling.\r
+     */\r
+    {\r
+        int i;\r
+        for (i = 0; i < s->ncontrols; i++) {\r
+            c = s->ctrls[i];\r
+            if (c->generic.type == CTRL_CHECKBOX &&\r
+                c->generic.context.i == offsetof(Config,scrollbar)) {\r
+                /*\r
+                 * Control i is the scrollbar checkbox.\r
+                 * Control s->ncontrols-1 is the scrollbar-in-FS one.\r
+                 */\r
+                if (i < s->ncontrols-2) {\r
+                    c = s->ctrls[s->ncontrols-1];\r
+                    memmove(s->ctrls+i+2, s->ctrls+i+1,\r
+                            (s->ncontrols-i-2)*sizeof(union control *));\r
+                    s->ctrls[i+1] = c;\r
+                }\r
+                break;\r
+            }\r
+        }\r
+    }\r
+\r
+    /*\r
+     * Windows has the AltGr key, which has various Windows-\r
+     * specific options.\r
+     */\r
+    s = ctrl_getset(b, "Terminal/Keyboard", "features",\r
+                   "Enable extra keyboard features:");\r
+    ctrl_checkbox(s, "AltGr acts as Compose key", 't',\r
+                 HELPCTX(keyboard_compose),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,compose_key)));\r
+    ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd',\r
+                 HELPCTX(keyboard_ctrlalt),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,ctrlaltkeys)));\r
+\r
+    /*\r
+     * Windows allows an arbitrary .WAV to be played as a bell, and\r
+     * also the use of the PC speaker. For this we must search the\r
+     * existing controlset for the radio-button set controlling the\r
+     * `beep' option, and add extra buttons to it.\r
+     * \r
+     * Note that although this _looks_ like a hideous hack, it's\r
+     * actually all above board. The well-defined interface to the\r
+     * per-platform dialog box code is the _data structures_ `union\r
+     * control', `struct controlset' and so on; so code like this\r
+     * that reaches into those data structures and changes bits of\r
+     * them is perfectly legitimate and crosses no boundaries. All\r
+     * the ctrl_* routines that create most of the controls are\r
+     * convenient shortcuts provided on the cross-platform side of\r
+     * the interface, and template creation code is under no actual\r
+     * obligation to use them.\r
+     */\r
+    s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");\r
+    {\r
+       int i;\r
+       for (i = 0; i < s->ncontrols; i++) {\r
+           c = s->ctrls[i];\r
+           if (c->generic.type == CTRL_RADIO &&\r
+               c->generic.context.i == offsetof(Config, beep)) {\r
+               assert(c->generic.handler == dlg_stdradiobutton_handler);\r
+               c->radio.nbuttons += 2;\r
+               c->radio.buttons =\r
+                   sresize(c->radio.buttons, c->radio.nbuttons, char *);\r
+               c->radio.buttons[c->radio.nbuttons-1] =\r
+                   dupstr("Play a custom sound file");\r
+               c->radio.buttons[c->radio.nbuttons-2] =\r
+                   dupstr("Beep using the PC speaker");\r
+               c->radio.buttondata =\r
+                   sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);\r
+               c->radio.buttondata[c->radio.nbuttons-1] = I(BELL_WAVEFILE);\r
+               c->radio.buttondata[c->radio.nbuttons-2] = I(BELL_PCSPEAKER);\r
+               if (c->radio.shortcuts) {\r
+                   c->radio.shortcuts =\r
+                       sresize(c->radio.shortcuts, c->radio.nbuttons, char);\r
+                   c->radio.shortcuts[c->radio.nbuttons-1] = NO_SHORTCUT;\r
+                   c->radio.shortcuts[c->radio.nbuttons-2] = NO_SHORTCUT;\r
+               }\r
+               break;\r
+           }\r
+       }\r
+    }\r
+    ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT,\r
+                FILTER_WAVE_FILES, FALSE, "Select bell sound file",\r
+                HELPCTX(bell_style),\r
+                dlg_stdfilesel_handler, I(offsetof(Config, bell_wavefile)));\r
+\r
+    /*\r
+     * While we've got this box open, taskbar flashing on a bell is\r
+     * also Windows-specific.\r
+     */\r
+    ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3,\r
+                     HELPCTX(bell_taskbar),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, beep_ind)),\r
+                     "Disabled", I(B_IND_DISABLED),\r
+                     "Flashing", I(B_IND_FLASH),\r
+                     "Steady", I(B_IND_STEADY), NULL);\r
+\r
+    /*\r
+     * The sunken-edge border is a Windows GUI feature.\r
+     */\r
+    s = ctrl_getset(b, "Window/Appearance", "border",\r
+                   "Adjust the window border");\r
+    ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's',\r
+                 HELPCTX(appearance_border),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,sunken_edge)));\r
+\r
+    /*\r
+     * Configurable font quality settings for Windows.\r
+     */\r
+    s = ctrl_getset(b, "Window/Appearance", "font",\r
+                   "Font settings");\r
+    ctrl_checkbox(s, "Allow selection of variable-pitch fonts", NO_SHORTCUT,\r
+                  HELPCTX(appearance_font), variable_pitch_handler, I(0));\r
+    ctrl_radiobuttons(s, "Font quality:", 'q', 2,\r
+                     HELPCTX(appearance_font),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, font_quality)),\r
+                     "Antialiased", I(FQ_ANTIALIASED),\r
+                     "Non-Antialiased", I(FQ_NONANTIALIASED),\r
+                     "ClearType", I(FQ_CLEARTYPE),\r
+                     "Default", I(FQ_DEFAULT), NULL);\r
+\r
+    /*\r
+     * Cyrillic Lock is a horrid misfeature even on Windows, and\r
+     * the least we can do is ensure it never makes it to any other\r
+     * platform (at least unless someone fixes it!).\r
+     */\r
+    s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);\r
+    ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's',\r
+                 HELPCTX(translation_cyrillic),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,xlat_capslockcyr)));\r
+\r
+    /*\r
+     * On Windows we can use but not enumerate translation tables\r
+     * from the operating system. Briefly document this.\r
+     */\r
+    s = ctrl_getset(b, "Window/Translation", "trans",\r
+                   "Character set translation on received data");\r
+    ctrl_text(s, "(Codepages supported by Windows but not listed here, "\r
+             "such as CP866 on many systems, can be entered manually)",\r
+             HELPCTX(translation_codepage));\r
+\r
+    /*\r
+     * Windows has the weird OEM font mode, which gives us some\r
+     * additional options when working with line-drawing\r
+     * characters.\r
+     */\r
+    str = dupprintf("Adjust how %s displays line drawing characters", appname);\r
+    s = ctrl_getset(b, "Window/Translation", "linedraw", str);\r
+    sfree(str);\r
+    {\r
+       int i;\r
+       for (i = 0; i < s->ncontrols; i++) {\r
+           c = s->ctrls[i];\r
+           if (c->generic.type == CTRL_RADIO &&\r
+               c->generic.context.i == offsetof(Config, vtmode)) {\r
+               assert(c->generic.handler == dlg_stdradiobutton_handler);\r
+               c->radio.nbuttons += 3;\r
+               c->radio.buttons =\r
+                   sresize(c->radio.buttons, c->radio.nbuttons, char *);\r
+               c->radio.buttons[c->radio.nbuttons-3] =\r
+                   dupstr("Font has XWindows encoding");\r
+               c->radio.buttons[c->radio.nbuttons-2] =\r
+                   dupstr("Use font in both ANSI and OEM modes");\r
+               c->radio.buttons[c->radio.nbuttons-1] =\r
+                   dupstr("Use font in OEM mode only");\r
+               c->radio.buttondata =\r
+                   sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);\r
+               c->radio.buttondata[c->radio.nbuttons-3] = I(VT_XWINDOWS);\r
+               c->radio.buttondata[c->radio.nbuttons-2] = I(VT_OEMANSI);\r
+               c->radio.buttondata[c->radio.nbuttons-1] = I(VT_OEMONLY);\r
+               if (!c->radio.shortcuts) {\r
+                   int j;\r
+                   c->radio.shortcuts = snewn(c->radio.nbuttons, char);\r
+                   for (j = 0; j < c->radio.nbuttons; j++)\r
+                       c->radio.shortcuts[j] = NO_SHORTCUT;\r
+               } else {\r
+                   c->radio.shortcuts = sresize(c->radio.shortcuts,\r
+                                                c->radio.nbuttons, char);\r
+               }\r
+               c->radio.shortcuts[c->radio.nbuttons-3] = 'x';\r
+               c->radio.shortcuts[c->radio.nbuttons-2] = 'b';\r
+               c->radio.shortcuts[c->radio.nbuttons-1] = 'e';\r
+               break;\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * RTF paste is Windows-specific.\r
+     */\r
+    s = ctrl_getset(b, "Window/Selection", "format",\r
+                   "Formatting of pasted characters");\r
+    ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f',\r
+                 HELPCTX(selection_rtf),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,rtf_paste)));\r
+\r
+    /*\r
+     * Windows often has no middle button, so we supply a selection\r
+     * mode in which the more critical Paste action is available on\r
+     * the right button instead.\r
+     */\r
+    s = ctrl_getset(b, "Window/Selection", "mouse",\r
+                   "Control use of mouse");\r
+    ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1,\r
+                     HELPCTX(selection_buttons),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, mouse_is_xterm)),\r
+                     "Windows (Middle extends, Right brings up menu)", I(2),\r
+                     "Compromise (Middle extends, Right pastes)", I(0),\r
+                     "xterm (Right extends, Middle pastes)", I(1), NULL);\r
+    /*\r
+     * This really ought to go at the _top_ of its box, not the\r
+     * bottom, so we'll just do some shuffling now we've set it\r
+     * up...\r
+     */\r
+    c = s->ctrls[s->ncontrols-1];      /* this should be the new control */\r
+    memmove(s->ctrls+1, s->ctrls, (s->ncontrols-1)*sizeof(union control *));\r
+    s->ctrls[0] = c;\r
+\r
+    /*\r
+     * Logical palettes don't even make sense anywhere except Windows.\r
+     */\r
+    s = ctrl_getset(b, "Window/Colours", "general",\r
+                   "General options for colour usage");\r
+    ctrl_checkbox(s, "Attempt to use logical palettes", 'l',\r
+                 HELPCTX(colours_logpal),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,try_palette)));\r
+    ctrl_checkbox(s, "Use system colours", 's',\r
+                  HELPCTX(colours_system),\r
+                  dlg_stdcheckbox_handler, I(offsetof(Config,system_colour)));\r
+\r
+\r
+    /*\r
+     * Resize-by-changing-font is a Windows insanity.\r
+     */\r
+    s = ctrl_getset(b, "Window", "size", "Set the size of the window");\r
+    ctrl_radiobuttons(s, "When window is resized:", 'z', 1,\r
+                     HELPCTX(window_resize),\r
+                     dlg_stdradiobutton_handler,\r
+                     I(offsetof(Config, resize_action)),\r
+                     "Change the number of rows and columns", I(RESIZE_TERM),\r
+                     "Change the size of the font", I(RESIZE_FONT),\r
+                     "Change font size only when maximised", I(RESIZE_EITHER),\r
+                     "Forbid resizing completely", I(RESIZE_DISABLED), NULL);\r
+\r
+    /*\r
+     * Most of the Window/Behaviour stuff is there to mimic Windows\r
+     * conventions which PuTTY can optionally disregard. Hence,\r
+     * most of these options are Windows-specific.\r
+     */\r
+    s = ctrl_getset(b, "Window/Behaviour", "main", NULL);\r
+    ctrl_checkbox(s, "Window closes on ALT-F4", '4',\r
+                 HELPCTX(behaviour_altf4),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_f4)));\r
+    ctrl_checkbox(s, "System menu appears on ALT-Space", 'y',\r
+                 HELPCTX(behaviour_altspace),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_space)));\r
+    ctrl_checkbox(s, "System menu appears on ALT alone", 'l',\r
+                 HELPCTX(behaviour_altonly),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alt_only)));\r
+    ctrl_checkbox(s, "Ensure window is always on top", 'e',\r
+                 HELPCTX(behaviour_alwaysontop),\r
+                 dlg_stdcheckbox_handler, I(offsetof(Config,alwaysontop)));\r
+    ctrl_checkbox(s, "Full screen on Alt-Enter", 'f',\r
+                 HELPCTX(behaviour_altenter),\r
+                 dlg_stdcheckbox_handler,\r
+                 I(offsetof(Config,fullscreenonaltenter)));\r
+\r
+    /*\r
+     * Windows supports a local-command proxy. This also means we\r
+     * must adjust the text on the `Telnet command' control.\r
+     */\r
+    if (!midsession) {\r
+       int i;\r
+        s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);\r
+       for (i = 0; i < s->ncontrols; i++) {\r
+           c = s->ctrls[i];\r
+           if (c->generic.type == CTRL_RADIO &&\r
+               c->generic.context.i == offsetof(Config, proxy_type)) {\r
+               assert(c->generic.handler == dlg_stdradiobutton_handler);\r
+               c->radio.nbuttons++;\r
+               c->radio.buttons =\r
+                   sresize(c->radio.buttons, c->radio.nbuttons, char *);\r
+               c->radio.buttons[c->radio.nbuttons-1] =\r
+                   dupstr("Local");\r
+               c->radio.buttondata =\r
+                   sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);\r
+               c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_CMD);\r
+               break;\r
+           }\r
+       }\r
+\r
+       for (i = 0; i < s->ncontrols; i++) {\r
+           c = s->ctrls[i];\r
+           if (c->generic.type == CTRL_EDITBOX &&\r
+               c->generic.context.i ==\r
+               offsetof(Config, proxy_telnet_command)) {\r
+               assert(c->generic.handler == dlg_stdeditbox_handler);\r
+               sfree(c->generic.label);\r
+               c->generic.label = dupstr("Telnet command, or local"\r
+                                         " proxy command");\r
+               break;\r
+           }\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Serial back end is available on Windows.\r
+     */\r
+    if (!midsession || (protocol == PROT_SERIAL))\r
+        ser_setup_config_box(b, midsession, 0x1F, 0x0F);\r
+\r
+    /*\r
+     * $XAUTHORITY is not reliable on Windows, so we provide a\r
+     * means to override it.\r
+     */\r
+    if (!midsession && backend_from_proto(PROT_SSH)) {\r
+       s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");\r
+       ctrl_filesel(s, "X authority file for local display", 't',\r
+                    NULL, FALSE, "Select X authority file",\r
+                    HELPCTX(ssh_tunnels_xauthority),\r
+                    dlg_stdfilesel_handler, I(offsetof(Config, xauthfile)));\r
+    }\r
+}\r
diff --git a/putty/WINDOWS/WINCONS.C b/putty/WINDOWS/WINCONS.C
new file mode 100644 (file)
index 0000000..4f984d9
--- /dev/null
@@ -0,0 +1,409 @@
+/*\r
+ * wincons.c - various interactive-prompt routines shared between\r
+ * the Windows console PuTTY tools\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+\r
+#include "putty.h"\r
+#include "storage.h"\r
+#include "ssh.h"\r
+\r
+int console_batch_mode = FALSE;\r
+\r
+static void *console_logctx = NULL;\r
+\r
+/*\r
+ * Clean up and exit.\r
+ */\r
+void cleanup_exit(int code)\r
+{\r
+    /*\r
+     * Clean up.\r
+     */\r
+    sk_cleanup();\r
+\r
+    random_save_seed();\r
+#ifdef MSCRYPTOAPI\r
+    crypto_wrapup();\r
+#endif\r
+\r
+    exit(code);\r
+}\r
+\r
+void set_busy_status(void *frontend, int status)\r
+{\r
+}\r
+\r
+void notify_remote_exit(void *frontend)\r
+{\r
+}\r
+\r
+void timer_change_notify(long next)\r
+{\r
+}\r
+\r
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
+                        char *keystr, char *fingerprint,\r
+                        void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    int ret;\r
+    HANDLE hin;\r
+    DWORD savemode, i;\r
+\r
+    static const char absentmsg_batch[] =\r
+       "The server's host key is not cached in the registry. You\n"\r
+       "have no guarantee that the server is the computer you\n"\r
+       "think it is.\n"\r
+       "The server's %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "Connection abandoned.\n";\r
+    static const char absentmsg[] =\r
+       "The server's host key is not cached in the registry. You\n"\r
+       "have no guarantee that the server is the computer you\n"\r
+       "think it is.\n"\r
+       "The server's %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you trust this host, enter \"y\" to add the key to\n"\r
+       "PuTTY's cache and carry on connecting.\n"\r
+       "If you want to carry on connecting just once, without\n"\r
+       "adding the key to the cache, enter \"n\".\n"\r
+       "If you do not trust this host, press Return to abandon the\n"\r
+       "connection.\n"\r
+       "Store key in cache? (y/n) ";\r
+\r
+    static const char wrongmsg_batch[] =\r
+       "WARNING - POTENTIAL SECURITY BREACH!\n"\r
+       "The server's host key does not match the one PuTTY has\n"\r
+       "cached in the registry. This means that either the\n"\r
+       "server administrator has changed the host key, or you\n"\r
+       "have actually connected to another computer pretending\n"\r
+       "to be the server.\n"\r
+       "The new %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "Connection abandoned.\n";\r
+    static const char wrongmsg[] =\r
+       "WARNING - POTENTIAL SECURITY BREACH!\n"\r
+       "The server's host key does not match the one PuTTY has\n"\r
+       "cached in the registry. This means that either the\n"\r
+       "server administrator has changed the host key, or you\n"\r
+       "have actually connected to another computer pretending\n"\r
+       "to be the server.\n"\r
+       "The new %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you were expecting this change and trust the new key,\n"\r
+       "enter \"y\" to update PuTTY's cache and continue connecting.\n"\r
+       "If you want to carry on connecting but without updating\n"\r
+       "the cache, enter \"n\".\n"\r
+       "If you want to abandon the connection completely, press\n"\r
+       "Return to cancel. Pressing Return is the ONLY guaranteed\n"\r
+       "safe choice.\n"\r
+       "Update cached key? (y/n, Return cancels connection) ";\r
+\r
+    static const char abandoned[] = "Connection abandoned.\n";\r
+\r
+    char line[32];\r
+\r
+    /*\r
+     * Verify the key against the registry.\r
+     */\r
+    ret = verify_host_key(host, port, keytype, keystr);\r
+\r
+    if (ret == 0)                     /* success - key matched OK */\r
+       return 1;\r
+\r
+    if (ret == 2) {                   /* key was different */\r
+       if (console_batch_mode) {\r
+           fprintf(stderr, wrongmsg_batch, keytype, fingerprint);\r
+            return 0;\r
+       }\r
+       fprintf(stderr, wrongmsg, keytype, fingerprint);\r
+       fflush(stderr);\r
+    }\r
+    if (ret == 1) {                   /* key was absent */\r
+       if (console_batch_mode) {\r
+           fprintf(stderr, absentmsg_batch, keytype, fingerprint);\r
+            return 0;\r
+       }\r
+       fprintf(stderr, absentmsg, keytype, fingerprint);\r
+       fflush(stderr);\r
+    }\r
+\r
+    hin = GetStdHandle(STD_INPUT_HANDLE);\r
+    GetConsoleMode(hin, &savemode);\r
+    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |\r
+                        ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));\r
+    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);\r
+    SetConsoleMode(hin, savemode);\r
+\r
+    if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {\r
+       if (line[0] == 'y' || line[0] == 'Y')\r
+           store_host_key(host, port, keytype, keystr);\r
+        return 1;\r
+    } else {\r
+       fprintf(stderr, abandoned);\r
+        return 0;\r
+    }\r
+}\r
+\r
+void update_specials_menu(void *frontend)\r
+{\r
+}\r
+\r
+/*\r
+ * Ask whether the selected algorithm is acceptable (since it was\r
+ * below the configured 'warn' threshold).\r
+ */\r
+int askalg(void *frontend, const char *algtype, const char *algname,\r
+          void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    HANDLE hin;\r
+    DWORD savemode, i;\r
+\r
+    static const char msg[] =\r
+       "The first %s supported by the server is\n"\r
+       "%s, which is below the configured warning threshold.\n"\r
+       "Continue with connection? (y/n) ";\r
+    static const char msg_batch[] =\r
+       "The first %s supported by the server is\n"\r
+       "%s, which is below the configured warning threshold.\n"\r
+       "Connection abandoned.\n";\r
+    static const char abandoned[] = "Connection abandoned.\n";\r
+\r
+    char line[32];\r
+\r
+    if (console_batch_mode) {\r
+       fprintf(stderr, msg_batch, algtype, algname);\r
+       return 0;\r
+    }\r
+\r
+    fprintf(stderr, msg, algtype, algname);\r
+    fflush(stderr);\r
+\r
+    hin = GetStdHandle(STD_INPUT_HANDLE);\r
+    GetConsoleMode(hin, &savemode);\r
+    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |\r
+                        ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));\r
+    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);\r
+    SetConsoleMode(hin, savemode);\r
+\r
+    if (line[0] == 'y' || line[0] == 'Y') {\r
+       return 1;\r
+    } else {\r
+       fprintf(stderr, abandoned);\r
+       return 0;\r
+    }\r
+}\r
+\r
+/*\r
+ * Ask whether to wipe a session log file before writing to it.\r
+ * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).\r
+ */\r
+int askappend(void *frontend, Filename filename,\r
+             void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    HANDLE hin;\r
+    DWORD savemode, i;\r
+\r
+    static const char msgtemplate[] =\r
+       "The session log file \"%.*s\" already exists.\n"\r
+       "You can overwrite it with a new session log,\n"\r
+       "append your session log to the end of it,\n"\r
+       "or disable session logging for this session.\n"\r
+       "Enter \"y\" to wipe the file, \"n\" to append to it,\n"\r
+       "or just press Return to disable logging.\n"\r
+       "Wipe the log file? (y/n, Return cancels logging) ";\r
+\r
+    static const char msgtemplate_batch[] =\r
+       "The session log file \"%.*s\" already exists.\n"\r
+       "Logging will not be enabled.\n";\r
+\r
+    char line[32];\r
+\r
+    if (console_batch_mode) {\r
+       fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);\r
+       fflush(stderr);\r
+       return 0;\r
+    }\r
+    fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);\r
+    fflush(stderr);\r
+\r
+    hin = GetStdHandle(STD_INPUT_HANDLE);\r
+    GetConsoleMode(hin, &savemode);\r
+    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |\r
+                        ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));\r
+    ReadFile(hin, line, sizeof(line) - 1, &i, NULL);\r
+    SetConsoleMode(hin, savemode);\r
+\r
+    if (line[0] == 'y' || line[0] == 'Y')\r
+       return 2;\r
+    else if (line[0] == 'n' || line[0] == 'N')\r
+       return 1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+/*\r
+ * Warn about the obsolescent key file format.\r
+ * \r
+ * Uniquely among these functions, this one does _not_ expect a\r
+ * frontend handle. This means that if PuTTY is ported to a\r
+ * platform which requires frontend handles, this function will be\r
+ * an anomaly. Fortunately, the problem it addresses will not have\r
+ * been present on that platform, so it can plausibly be\r
+ * implemented as an empty function.\r
+ */\r
+void old_keyfile_warning(void)\r
+{\r
+    static const char message[] =\r
+       "You are loading an SSH-2 private key which has an\n"\r
+       "old version of the file format. This means your key\n"\r
+       "file is not fully tamperproof. Future versions of\n"\r
+       "PuTTY may stop supporting this private key format,\n"\r
+       "so we recommend you convert your key to the new\n"\r
+       "format.\n"\r
+       "\n"\r
+       "Once the key is loaded into PuTTYgen, you can perform\n"\r
+       "this conversion simply by saving it again.\n";\r
+\r
+    fputs(message, stderr);\r
+}\r
+\r
+/*\r
+ * Display the fingerprints of the PGP Master Keys to the user.\r
+ */\r
+void pgp_fingerprints(void)\r
+{\r
+    fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"\r
+         "be used to establish a trust path from this executable to another\n"\r
+         "one. See the manual for more information.\n"\r
+         "(Note: these fingerprints have nothing to do with SSH!)\n"\r
+         "\n"\r
+         "PuTTY Master Key (RSA), 1024-bit:\n"\r
+         "  " PGP_RSA_MASTER_KEY_FP "\n"\r
+         "PuTTY Master Key (DSA), 1024-bit:\n"\r
+         "  " PGP_DSA_MASTER_KEY_FP "\n", stdout);\r
+}\r
+\r
+void console_provide_logctx(void *logctx)\r
+{\r
+    console_logctx = logctx;\r
+}\r
+\r
+void logevent(void *frontend, const char *string)\r
+{\r
+    log_eventlog(console_logctx, string);\r
+}\r
+\r
+static void console_data_untrusted(HANDLE hout, const char *data, int len)\r
+{\r
+    DWORD dummy;\r
+    /* FIXME: control-character filtering */\r
+    WriteFile(hout, data, len, &dummy, NULL);\r
+}\r
+\r
+int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    HANDLE hin, hout;\r
+    size_t curr_prompt;\r
+\r
+    /*\r
+     * Zero all the results, in case we abort half-way through.\r
+     */\r
+    {\r
+       int i;\r
+       for (i = 0; i < (int)p->n_prompts; i++)\r
+           memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);\r
+    }\r
+\r
+    /*\r
+     * The prompts_t might contain a message to be displayed but no\r
+     * actual prompt. More usually, though, it will contain\r
+     * questions that the user needs to answer, in which case we\r
+     * need to ensure that we're able to get the answers.\r
+     */\r
+    if (p->n_prompts) {\r
+       if (console_batch_mode)\r
+           return 0;\r
+       hin = GetStdHandle(STD_INPUT_HANDLE);\r
+       if (hin == INVALID_HANDLE_VALUE) {\r
+           fprintf(stderr, "Cannot get standard input handle\n");\r
+           cleanup_exit(1);\r
+       }\r
+    }\r
+\r
+    /*\r
+     * And if we have anything to print, we need standard output.\r
+     */\r
+    if ((p->name_reqd && p->name) || p->instruction || p->n_prompts) {\r
+       hout = GetStdHandle(STD_OUTPUT_HANDLE);\r
+       if (hout == INVALID_HANDLE_VALUE) {\r
+           fprintf(stderr, "Cannot get standard output handle\n");\r
+           cleanup_exit(1);\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Preamble.\r
+     */\r
+    /* We only print the `name' caption if we have to... */\r
+    if (p->name_reqd && p->name) {\r
+       size_t l = strlen(p->name);\r
+       console_data_untrusted(hout, p->name, l);\r
+       if (p->name[l-1] != '\n')\r
+           console_data_untrusted(hout, "\n", 1);\r
+    }\r
+    /* ...but we always print any `instruction'. */\r
+    if (p->instruction) {\r
+       size_t l = strlen(p->instruction);\r
+       console_data_untrusted(hout, p->instruction, l);\r
+       if (p->instruction[l-1] != '\n')\r
+           console_data_untrusted(hout, "\n", 1);\r
+    }\r
+\r
+    for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {\r
+\r
+       DWORD savemode, newmode, i = 0;\r
+       prompt_t *pr = p->prompts[curr_prompt];\r
+       BOOL r;\r
+\r
+       GetConsoleMode(hin, &savemode);\r
+       newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;\r
+       if (!pr->echo)\r
+           newmode &= ~ENABLE_ECHO_INPUT;\r
+       else\r
+           newmode |= ENABLE_ECHO_INPUT;\r
+       SetConsoleMode(hin, newmode);\r
+\r
+       console_data_untrusted(hout, pr->prompt, strlen(pr->prompt));\r
+\r
+       r = ReadFile(hin, pr->result, pr->result_len - 1, &i, NULL);\r
+\r
+       SetConsoleMode(hin, savemode);\r
+\r
+       if ((int) i > pr->result_len)\r
+           i = pr->result_len - 1;\r
+       else\r
+           i = i - 2;\r
+       pr->result[i] = '\0';\r
+\r
+       if (!pr->echo) {\r
+           DWORD dummy;\r
+           WriteFile(hout, "\r\n", 2, &dummy, NULL);\r
+       }\r
+\r
+    }\r
+\r
+    return 1; /* success */\r
+\r
+}\r
+\r
+void frontend_keypress(void *handle)\r
+{\r
+    /*\r
+     * This is nothing but a stub, in console code.\r
+     */\r
+    return;\r
+}\r
diff --git a/putty/WINDOWS/WINCTRLS.C b/putty/WINDOWS/WINCTRLS.C
new file mode 100644 (file)
index 0000000..159c925
--- /dev/null
@@ -0,0 +1,2623 @@
+/*\r
+ * winctrls.c: routines to self-manage the controls in a dialog\r
+ * box.\r
+ */\r
+\r
+/*\r
+ * Possible TODO in new cross-platform config box stuff:\r
+ *\r
+ *  - When lining up two controls alongside each other, I wonder if\r
+ *    we could conveniently arrange to centre them vertically?\r
+ *    Particularly ugly in the current setup is the `Add new\r
+ *    forwarded port:' static next to the rather taller `Remove'\r
+ *    button.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+#include "misc.h"\r
+#include "dialog.h"\r
+\r
+#include <commctrl.h>\r
+\r
+#define GAPBETWEEN 3\r
+#define GAPWITHIN 1\r
+#define GAPXBOX 7\r
+#define GAPYBOX 4\r
+#define DLGWIDTH 168\r
+#define STATICHEIGHT 8\r
+#define TITLEHEIGHT 12\r
+#define CHECKBOXHEIGHT 8\r
+#define RADIOHEIGHT 8\r
+#define EDITHEIGHT 12\r
+#define LISTHEIGHT 11\r
+#define LISTINCREMENT 8\r
+#define COMBOHEIGHT 12\r
+#define PUSHBTNHEIGHT 14\r
+#define PROGBARHEIGHT 14\r
+\r
+void ctlposinit(struct ctlpos *cp, HWND hwnd,\r
+               int leftborder, int rightborder, int topborder)\r
+{\r
+    RECT r, r2;\r
+    cp->hwnd = hwnd;\r
+    cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0);\r
+    cp->ypos = topborder;\r
+    GetClientRect(hwnd, &r);\r
+    r2.left = r2.top = 0;\r
+    r2.right = 4;\r
+    r2.bottom = 8;\r
+    MapDialogRect(hwnd, &r2);\r
+    cp->dlu4inpix = r2.right;\r
+    cp->width = (r.right * 4) / (r2.right) - 2 * GAPBETWEEN;\r
+    cp->xoff = leftborder;\r
+    cp->width -= leftborder + rightborder;\r
+}\r
+\r
+HWND doctl(struct ctlpos *cp, RECT r,\r
+          char *wclass, int wstyle, int exstyle, char *wtext, int wid)\r
+{\r
+    HWND ctl;\r
+    /*\r
+     * Note nonstandard use of RECT. This is deliberate: by\r
+     * transforming the width and height directly we arrange to\r
+     * have all supposedly same-sized controls really same-sized.\r
+     */\r
+\r
+    r.left += cp->xoff;\r
+    MapDialogRect(cp->hwnd, &r);\r
+\r
+    /*\r
+     * We can pass in cp->hwnd == NULL, to indicate a dry run\r
+     * without creating any actual controls.\r
+     */\r
+    if (cp->hwnd) {\r
+       ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle,\r
+                            r.left, r.top, r.right, r.bottom,\r
+                            cp->hwnd, (HMENU) wid, hinst, NULL);\r
+       SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));\r
+\r
+       if (!strcmp(wclass, "LISTBOX")) {\r
+           /*\r
+            * Bizarre Windows bug: the list box calculates its\r
+            * number of lines based on the font it has at creation\r
+            * time, but sending it WM_SETFONT doesn't cause it to\r
+            * recalculate. So now, _after_ we've sent it\r
+            * WM_SETFONT, we explicitly resize it (to the same\r
+            * size it was already!) to force it to reconsider.\r
+            */\r
+           SetWindowPos(ctl, NULL, 0, 0, r.right, r.bottom,\r
+                        SWP_NOACTIVATE | SWP_NOCOPYBITS |\r
+                        SWP_NOMOVE | SWP_NOZORDER);\r
+       }\r
+    } else\r
+       ctl = NULL;\r
+    return ctl;\r
+}\r
+\r
+/*\r
+ * A title bar across the top of a sub-dialog.\r
+ */\r
+void bartitle(struct ctlpos *cp, char *name, int id)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.right = cp->width;\r
+    r.top = cp->ypos;\r
+    r.bottom = STATICHEIGHT;\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, name, id);\r
+}\r
+\r
+/*\r
+ * Begin a grouping box, with or without a group title.\r
+ */\r
+void beginbox(struct ctlpos *cp, char *name, int idbox)\r
+{\r
+    cp->boxystart = cp->ypos;\r
+    if (!name)\r
+       cp->boxystart -= STATICHEIGHT / 2;\r
+    if (name)\r
+       cp->ypos += STATICHEIGHT;\r
+    cp->ypos += GAPYBOX;\r
+    cp->width -= 2 * GAPXBOX;\r
+    cp->xoff += GAPXBOX;\r
+    cp->boxid = idbox;\r
+    cp->boxtext = name;\r
+}\r
+\r
+/*\r
+ * End a grouping box.\r
+ */\r
+void endbox(struct ctlpos *cp)\r
+{\r
+    RECT r;\r
+    cp->xoff -= GAPXBOX;\r
+    cp->width += 2 * GAPXBOX;\r
+    cp->ypos += GAPYBOX - GAPBETWEEN;\r
+    r.left = GAPBETWEEN;\r
+    r.right = cp->width;\r
+    r.top = cp->boxystart;\r
+    r.bottom = cp->ypos - cp->boxystart;\r
+    doctl(cp, r, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 0,\r
+         cp->boxtext ? cp->boxtext : "", cp->boxid);\r
+    cp->ypos += GAPYBOX;\r
+}\r
+\r
+/*\r
+ * A static line, followed by a full-width edit box.\r
+ */\r
+void editboxfw(struct ctlpos *cp, int password, char *text,\r
+              int staticid, int editid)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.right = cp->width;\r
+\r
+    if (text) {\r
+       r.top = cp->ypos;\r
+       r.bottom = STATICHEIGHT;\r
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);\r
+       cp->ypos += STATICHEIGHT + GAPWITHIN;\r
+    }\r
+    r.top = cp->ypos;\r
+    r.bottom = EDITHEIGHT;\r
+    doctl(cp, r, "EDIT",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL |\r
+         (password ? ES_PASSWORD : 0),\r
+         WS_EX_CLIENTEDGE, "", editid);\r
+    cp->ypos += EDITHEIGHT + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * A static line, followed by a full-width combo box.\r
+ */\r
+void combobox(struct ctlpos *cp, char *text, int staticid, int listid)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.right = cp->width;\r
+\r
+    if (text) {\r
+       r.top = cp->ypos;\r
+       r.bottom = STATICHEIGHT;\r
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid);\r
+       cp->ypos += STATICHEIGHT + GAPWITHIN;\r
+    }\r
+    r.top = cp->ypos;\r
+    r.bottom = COMBOHEIGHT * 10;\r
+    doctl(cp, r, "COMBOBOX",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |\r
+         CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid);\r
+    cp->ypos += COMBOHEIGHT + GAPBETWEEN;\r
+}\r
+\r
+struct radio { char *text; int id; };\r
+\r
+static void radioline_common(struct ctlpos *cp, char *text, int id,\r
+                            int nacross, struct radio *buttons, int nbuttons)\r
+{\r
+    RECT r;\r
+    int group;\r
+    int i;\r
+    int j;\r
+\r
+    if (text) {\r
+       r.left = GAPBETWEEN;\r
+       r.top = cp->ypos;\r
+       r.right = cp->width;\r
+       r.bottom = STATICHEIGHT;\r
+       cp->ypos += r.bottom + GAPWITHIN;\r
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);\r
+    }\r
+\r
+    group = WS_GROUP;\r
+    i = 0;\r
+    for (j = 0; j < nbuttons; j++) {\r
+       char *btext = buttons[j].text;\r
+       int bid = buttons[j].id;\r
+\r
+       if (i == nacross) {\r
+           cp->ypos += r.bottom + (nacross > 1 ? GAPBETWEEN : GAPWITHIN);\r
+           i = 0;\r
+       }\r
+       r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross;\r
+       if (j < nbuttons-1)\r
+           r.right =\r
+               (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left;\r
+       else\r
+           r.right = cp->width - r.left;\r
+       r.top = cp->ypos;\r
+       r.bottom = RADIOHEIGHT;\r
+       doctl(cp, r, "BUTTON",\r
+             BS_NOTIFY | BS_AUTORADIOBUTTON | WS_CHILD |\r
+             WS_VISIBLE | WS_TABSTOP | group, 0, btext, bid);\r
+       group = 0;\r
+       i++;\r
+    }\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * A set of radio buttons on the same line, with a static above\r
+ * them. `nacross' dictates how many parts the line is divided into\r
+ * (you might want this not to equal the number of buttons if you\r
+ * needed to line up some 2s and some 3s to look good in the same\r
+ * panel).\r
+ * \r
+ * There's a bit of a hack in here to ensure that if nacross\r
+ * exceeds the actual number of buttons, the rightmost button\r
+ * really does get all the space right to the edge of the line, so\r
+ * you can do things like\r
+ * \r
+ * (*) Button1  (*) Button2  (*) ButtonWithReallyLongTitle\r
+ */\r
+void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)\r
+{\r
+    va_list ap;\r
+    struct radio *buttons;\r
+    int i, nbuttons;\r
+\r
+    va_start(ap, nacross);\r
+    nbuttons = 0;\r
+    while (1) {\r
+       char *btext = va_arg(ap, char *);\r
+       int bid;\r
+       if (!btext)\r
+           break;\r
+       bid = va_arg(ap, int);\r
+       nbuttons++;\r
+    }\r
+    va_end(ap);\r
+    buttons = snewn(nbuttons, struct radio);\r
+    va_start(ap, nacross);\r
+    for (i = 0; i < nbuttons; i++) {\r
+       buttons[i].text = va_arg(ap, char *);\r
+       buttons[i].id = va_arg(ap, int);\r
+    }\r
+    va_end(ap);\r
+    radioline_common(cp, text, id, nacross, buttons, nbuttons);\r
+    sfree(buttons);\r
+}\r
+\r
+/*\r
+ * A set of radio buttons on the same line, without a static above\r
+ * them. Otherwise just like radioline.\r
+ */\r
+void bareradioline(struct ctlpos *cp, int nacross, ...)\r
+{\r
+    va_list ap;\r
+    struct radio *buttons;\r
+    int i, nbuttons;\r
+\r
+    va_start(ap, nacross);\r
+    nbuttons = 0;\r
+    while (1) {\r
+       char *btext = va_arg(ap, char *);\r
+       int bid;\r
+       if (!btext)\r
+           break;\r
+       bid = va_arg(ap, int);\r
+    }\r
+    va_end(ap);\r
+    buttons = snewn(nbuttons, struct radio);\r
+    va_start(ap, nacross);\r
+    for (i = 0; i < nbuttons; i++) {\r
+       buttons[i].text = va_arg(ap, char *);\r
+       buttons[i].id = va_arg(ap, int);\r
+    }\r
+    va_end(ap);\r
+    radioline_common(cp, NULL, 0, nacross, buttons, nbuttons);\r
+    sfree(buttons);\r
+}\r
+\r
+/*\r
+ * A set of radio buttons on multiple lines, with a static above\r
+ * them.\r
+ */\r
+void radiobig(struct ctlpos *cp, char *text, int id, ...)\r
+{\r
+    va_list ap;\r
+    struct radio *buttons;\r
+    int i, nbuttons;\r
+\r
+    va_start(ap, id);\r
+    nbuttons = 0;\r
+    while (1) {\r
+       char *btext = va_arg(ap, char *);\r
+       int bid;\r
+       if (!btext)\r
+           break;\r
+       bid = va_arg(ap, int);\r
+    }\r
+    va_end(ap);\r
+    buttons = snewn(nbuttons, struct radio);\r
+    va_start(ap, id);\r
+    for (i = 0; i < nbuttons; i++) {\r
+       buttons[i].text = va_arg(ap, char *);\r
+       buttons[i].id = va_arg(ap, int);\r
+    }\r
+    va_end(ap);\r
+    radioline_common(cp, text, id, 1, buttons, nbuttons);\r
+    sfree(buttons);\r
+}\r
+\r
+/*\r
+ * A single standalone checkbox.\r
+ */\r
+void checkbox(struct ctlpos *cp, char *text, int id)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = CHECKBOXHEIGHT;\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+    doctl(cp, r, "BUTTON",\r
+         BS_NOTIFY | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0,\r
+         text, id);\r
+}\r
+\r
+/*\r
+ * Wrap a piece of text for a static text control. Returns the\r
+ * wrapped text (a malloc'ed string containing \ns), and also\r
+ * returns the number of lines required.\r
+ */\r
+char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines)\r
+{\r
+    HDC hdc = GetDC(hwnd);\r
+    int lpx = GetDeviceCaps(hdc, LOGPIXELSX);\r
+    int width, nlines, j;\r
+    INT *pwidths, nfit;\r
+    SIZE size;\r
+    char *ret, *p, *q;\r
+    RECT r;\r
+    HFONT oldfont, newfont;\r
+\r
+    ret = snewn(1+strlen(text), char);\r
+    p = text;\r
+    q = ret;\r
+    pwidths = snewn(1+strlen(text), INT);\r
+\r
+    /*\r
+     * Work out the width the text will need to fit in, by doing\r
+     * the same adjustment that the `statictext' function itself\r
+     * will perform.\r
+     */\r
+    SetMapMode(hdc, MM_TEXT);         /* ensure logical units == pixels */\r
+    r.left = r.top = r.bottom = 0;\r
+    r.right = cp->width;\r
+    MapDialogRect(hwnd, &r);\r
+    width = r.right;\r
+\r
+    nlines = 1;\r
+\r
+    /*\r
+     * We must select the correct font into the HDC before calling\r
+     * GetTextExtent*, or silly things will happen.\r
+     */\r
+    newfont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);\r
+    oldfont = SelectObject(hdc, newfont);\r
+\r
+    while (*p) {\r
+       if (!GetTextExtentExPoint(hdc, p, strlen(p), width,\r
+                                 &nfit, pwidths, &size) ||\r
+           (size_t)nfit >= strlen(p)) {\r
+           /*\r
+            * Either GetTextExtentExPoint returned failure, or the\r
+            * whole of the rest of the text fits on this line.\r
+            * Either way, we stop wrapping, copy the remainder of\r
+            * the input string unchanged to the output, and leave.\r
+            */\r
+           strcpy(q, p);\r
+           break;\r
+       }\r
+\r
+       /*\r
+        * Now we search backwards along the string from `nfit',\r
+        * looking for a space at which to break the line. If we\r
+        * don't find one at all, that's fine - we'll just break\r
+        * the line at `nfit'.\r
+        */\r
+       for (j = nfit; j > 0; j--) {\r
+           if (isspace((unsigned char)p[j])) {\r
+               nfit = j;\r
+               break;\r
+           }\r
+       }\r
+\r
+       strncpy(q, p, nfit);\r
+       q[nfit] = '\n';\r
+       q += nfit+1;\r
+\r
+       p += nfit;\r
+       while (*p && isspace((unsigned char)*p))\r
+           p++;\r
+\r
+       nlines++;\r
+    }\r
+\r
+    SelectObject(hdc, oldfont);\r
+    ReleaseDC(cp->hwnd, hdc);\r
+\r
+    if (lines) *lines = nlines;\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * A single standalone static text control.\r
+ */\r
+void statictext(struct ctlpos *cp, char *text, int lines, int id)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = STATICHEIGHT * lines;\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+    doctl(cp, r, "STATIC",\r
+         WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP,\r
+         0, text, id);\r
+}\r
+\r
+/*\r
+ * An owner-drawn static text control for a panel title.\r
+ */\r
+void paneltitle(struct ctlpos *cp, int id)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = TITLEHEIGHT;\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_OWNERDRAW,\r
+         0, NULL, id);\r
+}\r
+\r
+/*\r
+ * A button on the right hand side, with a static to its left.\r
+ */\r
+void staticbtn(struct ctlpos *cp, char *stext, int sid,\r
+              char *btext, int bid)\r
+{\r
+    const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?\r
+                       PUSHBTNHEIGHT : STATICHEIGHT);\r
+    RECT r;\r
+    int lwid, rwid, rpos;\r
+\r
+    rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;\r
+    lwid = rpos - 2 * GAPBETWEEN;\r
+    rwid = cp->width + GAPBETWEEN - rpos;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;\r
+    r.right = lwid;\r
+    r.bottom = STATICHEIGHT;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+\r
+    r.left = rpos;\r
+    r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;\r
+    r.right = rwid;\r
+    r.bottom = PUSHBTNHEIGHT;\r
+    doctl(cp, r, "BUTTON",\r
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,\r
+         0, btext, bid);\r
+\r
+    cp->ypos += height + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * A simple push button.\r
+ */\r
+void button(struct ctlpos *cp, char *btext, int bid, int defbtn)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = PUSHBTNHEIGHT;\r
+\r
+    /* Q67655: the _dialog box_ must know which button is default\r
+     * as well as the button itself knowing */\r
+    if (defbtn && cp->hwnd)\r
+       SendMessage(cp->hwnd, DM_SETDEFID, bid, 0);\r
+\r
+    doctl(cp, r, "BUTTON",\r
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP |\r
+         (defbtn ? BS_DEFPUSHBUTTON : 0) | BS_PUSHBUTTON,\r
+         0, btext, bid);\r
+\r
+    cp->ypos += PUSHBTNHEIGHT + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * Like staticbtn, but two buttons.\r
+ */\r
+void static2btn(struct ctlpos *cp, char *stext, int sid,\r
+               char *btext1, int bid1, char *btext2, int bid2)\r
+{\r
+    const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?\r
+                       PUSHBTNHEIGHT : STATICHEIGHT);\r
+    RECT r;\r
+    int lwid, rwid1, rwid2, rpos1, rpos2;\r
+\r
+    rpos1 = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2;\r
+    rpos2 = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;\r
+    lwid = rpos1 - 2 * GAPBETWEEN;\r
+    rwid1 = rpos2 - rpos1 - GAPBETWEEN;\r
+    rwid2 = cp->width + GAPBETWEEN - rpos2;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;\r
+    r.right = lwid;\r
+    r.bottom = STATICHEIGHT;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+\r
+    r.left = rpos1;\r
+    r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;\r
+    r.right = rwid1;\r
+    r.bottom = PUSHBTNHEIGHT;\r
+    doctl(cp, r, "BUTTON",\r
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,\r
+         0, btext1, bid1);\r
+\r
+    r.left = rpos2;\r
+    r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;\r
+    r.right = rwid2;\r
+    r.bottom = PUSHBTNHEIGHT;\r
+    doctl(cp, r, "BUTTON",\r
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,\r
+         0, btext2, bid2);\r
+\r
+    cp->ypos += height + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * An edit control on the right hand side, with a static to its left.\r
+ */\r
+static void staticedit_internal(struct ctlpos *cp, char *stext,\r
+                               int sid, int eid, int percentedit,\r
+                               int style)\r
+{\r
+    const int height = (EDITHEIGHT > STATICHEIGHT ?\r
+                       EDITHEIGHT : STATICHEIGHT);\r
+    RECT r;\r
+    int lwid, rwid, rpos;\r
+\r
+    rpos =\r
+       GAPBETWEEN + (100 - percentedit) * (cp->width + GAPBETWEEN) / 100;\r
+    lwid = rpos - 2 * GAPBETWEEN;\r
+    rwid = cp->width + GAPBETWEEN - rpos;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;\r
+    r.right = lwid;\r
+    r.bottom = STATICHEIGHT;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+\r
+    r.left = rpos;\r
+    r.top = cp->ypos + (height - EDITHEIGHT) / 2;\r
+    r.right = rwid;\r
+    r.bottom = EDITHEIGHT;\r
+    doctl(cp, r, "EDIT",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | style,\r
+         WS_EX_CLIENTEDGE, "", eid);\r
+\r
+    cp->ypos += height + GAPBETWEEN;\r
+}\r
+\r
+void staticedit(struct ctlpos *cp, char *stext,\r
+               int sid, int eid, int percentedit)\r
+{\r
+    staticedit_internal(cp, stext, sid, eid, percentedit, 0);\r
+}\r
+\r
+void staticpassedit(struct ctlpos *cp, char *stext,\r
+                   int sid, int eid, int percentedit)\r
+{\r
+    staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD);\r
+}\r
+\r
+/*\r
+ * A drop-down list box on the right hand side, with a static to\r
+ * its left.\r
+ */\r
+void staticddl(struct ctlpos *cp, char *stext,\r
+              int sid, int lid, int percentlist)\r
+{\r
+    const int height = (COMBOHEIGHT > STATICHEIGHT ?\r
+                       COMBOHEIGHT : STATICHEIGHT);\r
+    RECT r;\r
+    int lwid, rwid, rpos;\r
+\r
+    rpos =\r
+       GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100;\r
+    lwid = rpos - 2 * GAPBETWEEN;\r
+    rwid = cp->width + GAPBETWEEN - rpos;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;\r
+    r.right = lwid;\r
+    r.bottom = STATICHEIGHT;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+\r
+    r.left = rpos;\r
+    r.top = cp->ypos + (height - EDITHEIGHT) / 2;\r
+    r.right = rwid;\r
+    r.bottom = COMBOHEIGHT*4;\r
+    doctl(cp, r, "COMBOBOX",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |\r
+         CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);\r
+\r
+    cp->ypos += height + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * A combo box on the right hand side, with a static to its left.\r
+ */\r
+void staticcombo(struct ctlpos *cp, char *stext,\r
+                int sid, int lid, int percentlist)\r
+{\r
+    const int height = (COMBOHEIGHT > STATICHEIGHT ?\r
+                       COMBOHEIGHT : STATICHEIGHT);\r
+    RECT r;\r
+    int lwid, rwid, rpos;\r
+\r
+    rpos =\r
+       GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100;\r
+    lwid = rpos - 2 * GAPBETWEEN;\r
+    rwid = cp->width + GAPBETWEEN - rpos;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;\r
+    r.right = lwid;\r
+    r.bottom = STATICHEIGHT;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+\r
+    r.left = rpos;\r
+    r.top = cp->ypos + (height - EDITHEIGHT) / 2;\r
+    r.right = rwid;\r
+    r.bottom = COMBOHEIGHT*10;\r
+    doctl(cp, r, "COMBOBOX",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |\r
+         CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);\r
+\r
+    cp->ypos += height + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * A static, with a full-width drop-down list box below it.\r
+ */\r
+void staticddlbig(struct ctlpos *cp, char *stext,\r
+                 int sid, int lid)\r
+{\r
+    RECT r;\r
+\r
+    if (stext) {\r
+       r.left = GAPBETWEEN;\r
+       r.top = cp->ypos;\r
+       r.right = cp->width;\r
+       r.bottom = STATICHEIGHT;\r
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+       cp->ypos += STATICHEIGHT;\r
+    }\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = COMBOHEIGHT*4;\r
+    doctl(cp, r, "COMBOBOX",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |\r
+         CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);\r
+    cp->ypos += COMBOHEIGHT + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * A big multiline edit control with a static labelling it.\r
+ */\r
+void bigeditctrl(struct ctlpos *cp, char *stext,\r
+                int sid, int eid, int lines)\r
+{\r
+    RECT r;\r
+\r
+    if (stext) {\r
+       r.left = GAPBETWEEN;\r
+       r.top = cp->ypos;\r
+       r.right = cp->width;\r
+       r.bottom = STATICHEIGHT;\r
+       cp->ypos += r.bottom + GAPWITHIN;\r
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+    }\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = EDITHEIGHT + (lines - 1) * STATICHEIGHT;\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+    doctl(cp, r, "EDIT",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_MULTILINE,\r
+         WS_EX_CLIENTEDGE, "", eid);\r
+}\r
+\r
+/*\r
+ * A list box with a static labelling it.\r
+ */\r
+void listbox(struct ctlpos *cp, char *stext,\r
+            int sid, int lid, int lines, int multi)\r
+{\r
+    RECT r;\r
+\r
+    if (stext != NULL) {\r
+       r.left = GAPBETWEEN;\r
+       r.top = cp->ypos;\r
+       r.right = cp->width;\r
+       r.bottom = STATICHEIGHT;\r
+       cp->ypos += r.bottom + GAPWITHIN;\r
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+    }\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = LISTHEIGHT + (lines - 1) * LISTINCREMENT;\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+    doctl(cp, r, "LISTBOX",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |\r
+         LBS_NOTIFY | LBS_HASSTRINGS | LBS_USETABSTOPS |\r
+         (multi ? LBS_MULTIPLESEL : 0),\r
+         WS_EX_CLIENTEDGE, "", lid);\r
+}\r
+\r
+/*\r
+ * A tab-control substitute when a real tab control is unavailable.\r
+ */\r
+void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id)\r
+{\r
+    const int height = (COMBOHEIGHT > STATICHEIGHT ?\r
+                       COMBOHEIGHT : STATICHEIGHT);\r
+    RECT r;\r
+    int bigwid, lwid, rwid, rpos;\r
+    static const int BIGGAP = 15;\r
+    static const int MEDGAP = 3;\r
+\r
+    bigwid = cp->width + 2 * GAPBETWEEN - 2 * BIGGAP;\r
+    cp->ypos += MEDGAP;\r
+    rpos = BIGGAP + (bigwid + BIGGAP) / 2;\r
+    lwid = rpos - 2 * BIGGAP;\r
+    rwid = bigwid + BIGGAP - rpos;\r
+\r
+    r.left = BIGGAP;\r
+    r.top = cp->ypos + (height - STATICHEIGHT) / 2;\r
+    r.right = lwid;\r
+    r.bottom = STATICHEIGHT;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+\r
+    r.left = rpos;\r
+    r.top = cp->ypos + (height - COMBOHEIGHT) / 2;\r
+    r.right = rwid;\r
+    r.bottom = COMBOHEIGHT * 10;\r
+    doctl(cp, r, "COMBOBOX",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP |\r
+         CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);\r
+\r
+    cp->ypos += height + MEDGAP + GAPBETWEEN;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = 2;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ,\r
+         0, "", s2id);\r
+}\r
+\r
+/*\r
+ * A static line, followed by an edit control on the left hand side\r
+ * and a button on the right.\r
+ */\r
+void editbutton(struct ctlpos *cp, char *stext, int sid,\r
+               int eid, char *btext, int bid)\r
+{\r
+    const int height = (EDITHEIGHT > PUSHBTNHEIGHT ?\r
+                       EDITHEIGHT : PUSHBTNHEIGHT);\r
+    RECT r;\r
+    int lwid, rwid, rpos;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = STATICHEIGHT;\r
+    cp->ypos += r.bottom + GAPWITHIN;\r
+    doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+\r
+    rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;\r
+    lwid = rpos - 2 * GAPBETWEEN;\r
+    rwid = cp->width + GAPBETWEEN - rpos;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos + (height - EDITHEIGHT) / 2;\r
+    r.right = lwid;\r
+    r.bottom = EDITHEIGHT;\r
+    doctl(cp, r, "EDIT",\r
+         WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,\r
+         WS_EX_CLIENTEDGE, "", eid);\r
+\r
+    r.left = rpos;\r
+    r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;\r
+    r.right = rwid;\r
+    r.bottom = PUSHBTNHEIGHT;\r
+    doctl(cp, r, "BUTTON",\r
+         BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,\r
+         0, btext, bid);\r
+\r
+    cp->ypos += height + GAPBETWEEN;\r
+}\r
+\r
+/*\r
+ * A special control for manipulating an ordered preference list\r
+ * (eg. for cipher selection).\r
+ * XXX: this is a rough hack and could be improved.\r
+ */\r
+void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines,\r
+              char *stext, int sid, int listid, int upbid, int dnbid)\r
+{\r
+    const static int percents[] = { 5, 75, 20 };\r
+    RECT r;\r
+    int xpos, percent = 0, i;\r
+    int listheight = LISTHEIGHT + (lines - 1) * LISTINCREMENT;\r
+    const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN;\r
+    int totalheight, buttonpos;\r
+\r
+    /* Squirrel away IDs. */\r
+    hdl->listid = listid;\r
+    hdl->upbid  = upbid;\r
+    hdl->dnbid  = dnbid;\r
+\r
+    /* The static label. */\r
+    if (stext != NULL) {\r
+       r.left = GAPBETWEEN;\r
+       r.top = cp->ypos;\r
+       r.right = cp->width;\r
+       r.bottom = STATICHEIGHT;\r
+       cp->ypos += r.bottom + GAPWITHIN;\r
+       doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);\r
+    }\r
+\r
+    if (listheight > BTNSHEIGHT) {\r
+        totalheight = listheight;\r
+       buttonpos = (listheight - BTNSHEIGHT) / 2;\r
+    } else {\r
+        totalheight = BTNSHEIGHT;\r
+       buttonpos = 0;\r
+    }\r
+\r
+    for (i=0; i<3; i++) {\r
+        int left, wid;\r
+        xpos = (cp->width + GAPBETWEEN) * percent / 100;\r
+        left = xpos + GAPBETWEEN;\r
+        percent += percents[i];\r
+        xpos = (cp->width + GAPBETWEEN) * percent / 100;\r
+        wid = xpos - left;\r
+\r
+        switch (i) {\r
+          case 1:\r
+            /* The drag list box. */\r
+            r.left = left; r.right = wid;\r
+            r.top = cp->ypos; r.bottom = listheight;\r
+            {\r
+                HWND ctl;\r
+                ctl = doctl(cp, r, "LISTBOX",\r
+                            WS_CHILD | WS_VISIBLE | WS_TABSTOP |\r
+                           WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS,\r
+                            WS_EX_CLIENTEDGE,\r
+                            "", listid);\r
+               MakeDragList(ctl);\r
+            }\r
+            break;\r
+\r
+          case 2:\r
+            /* The "Up" and "Down" buttons. */\r
+           /* XXX worry about accelerators if we have more than one\r
+            * prefslist on a panel */\r
+            r.left = left; r.right = wid;\r
+            r.top = cp->ypos + buttonpos; r.bottom = PUSHBTNHEIGHT;\r
+            doctl(cp, r, "BUTTON",\r
+                  BS_NOTIFY | WS_CHILD | WS_VISIBLE |\r
+                 WS_TABSTOP | BS_PUSHBUTTON,\r
+                  0, "&Up", upbid);\r
+\r
+            r.left = left; r.right = wid;\r
+            r.top = cp->ypos + buttonpos + PUSHBTNHEIGHT + GAPBETWEEN;\r
+            r.bottom = PUSHBTNHEIGHT;\r
+            doctl(cp, r, "BUTTON",\r
+                  BS_NOTIFY | WS_CHILD | WS_VISIBLE |\r
+                 WS_TABSTOP | BS_PUSHBUTTON,\r
+                  0, "&Down", dnbid);\r
+\r
+            break;\r
+\r
+        }\r
+    }\r
+\r
+    cp->ypos += totalheight + GAPBETWEEN;\r
+\r
+}\r
+\r
+/*\r
+ * Helper function for prefslist: move item in list box.\r
+ */\r
+static void pl_moveitem(HWND hwnd, int listid, int src, int dst)\r
+{\r
+    int tlen, val;\r
+    char *txt;\r
+    /* Get the item's data. */\r
+    tlen = SendDlgItemMessage (hwnd, listid, LB_GETTEXTLEN, src, 0);\r
+    txt = snewn(tlen+1, char);\r
+    SendDlgItemMessage (hwnd, listid, LB_GETTEXT, src, (LPARAM) txt);\r
+    val = SendDlgItemMessage (hwnd, listid, LB_GETITEMDATA, src, 0);\r
+    /* Deselect old location. */\r
+    SendDlgItemMessage (hwnd, listid, LB_SETSEL, FALSE, src);\r
+    /* Delete it at the old location. */\r
+    SendDlgItemMessage (hwnd, listid, LB_DELETESTRING, src, 0);\r
+    /* Insert it at new location. */\r
+    SendDlgItemMessage (hwnd, listid, LB_INSERTSTRING, dst,\r
+                       (LPARAM) txt);\r
+    SendDlgItemMessage (hwnd, listid, LB_SETITEMDATA, dst,\r
+                       (LPARAM) val);\r
+    /* Set selection. */\r
+    SendDlgItemMessage (hwnd, listid, LB_SETCURSEL, dst, 0);\r
+    sfree (txt);\r
+}\r
+\r
+int pl_itemfrompt(HWND hwnd, POINT cursor, BOOL scroll)\r
+{\r
+    int ret;\r
+    POINT uppoint, downpoint;\r
+    int updist, downdist, upitem, downitem, i;\r
+\r
+    /*\r
+     * Ghastly hackery to try to figure out not which\r
+     * _item_, but which _gap between items_, the user\r
+     * is pointing at. We do this by first working out\r
+     * which list item is under the cursor, and then\r
+     * working out how far the cursor would have to\r
+     * move up or down before the answer was different.\r
+     * Then we put the insertion point _above_ the\r
+     * current item if the upper edge is closer than\r
+     * the lower edge, or _below_ it if vice versa.\r
+     */\r
+    ret = LBItemFromPt(hwnd, cursor, scroll);\r
+    if (ret == -1)\r
+       return ret;\r
+    ret = LBItemFromPt(hwnd, cursor, FALSE);\r
+    updist = downdist = 0;\r
+    for (i = 1; i < 4096 && (!updist || !downdist); i++) {\r
+       uppoint = downpoint = cursor;\r
+       uppoint.y -= i;\r
+       downpoint.y += i;\r
+       upitem = LBItemFromPt(hwnd, uppoint, FALSE);\r
+       downitem = LBItemFromPt(hwnd, downpoint, FALSE);\r
+       if (!updist && upitem != ret)\r
+           updist = i;\r
+       if (!downdist && downitem != ret)\r
+           downdist = i;\r
+    }\r
+    if (downdist < updist)\r
+       ret++;\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Handler for prefslist above.\r
+ * \r
+ * Return value has bit 0 set if the dialog box procedure needs to\r
+ * return TRUE from handling this message; it has bit 1 set if a\r
+ * change may have been made in the contents of the list.\r
+ */\r
+int handle_prefslist(struct prefslist *hdl,\r
+                     int *array, int maxmemb,\r
+                     int is_dlmsg, HWND hwnd,\r
+                    WPARAM wParam, LPARAM lParam)\r
+{\r
+    int i;\r
+    int ret = 0;\r
+\r
+    if (is_dlmsg) {\r
+\r
+        if ((int)wParam == hdl->listid) {\r
+            DRAGLISTINFO *dlm = (DRAGLISTINFO *)lParam;\r
+            int dest = 0;             /* initialise to placate gcc */\r
+            switch (dlm->uNotification) {\r
+              case DL_BEGINDRAG:\r
+               /* Add a dummy item to make pl_itemfrompt() work\r
+                * better.\r
+                * FIXME: this causes scrollbar glitches if the count of\r
+                *        listbox contains >= its height. */\r
+               hdl->dummyitem =\r
+                   SendDlgItemMessage(hwnd, hdl->listid,\r
+                                      LB_ADDSTRING, 0, (LPARAM) "");\r
+\r
+                hdl->srcitem = LBItemFromPt(dlm->hWnd, dlm->ptCursor, TRUE);\r
+               hdl->dragging = 0;\r
+               /* XXX hack Q183115 */\r
+               SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);\r
+                ret |= 1; break;\r
+              case DL_CANCELDRAG:\r
+               DrawInsert(hwnd, dlm->hWnd, -1);     /* Clear arrow */\r
+               SendDlgItemMessage(hwnd, hdl->listid,\r
+                                  LB_DELETESTRING, hdl->dummyitem, 0);\r
+               hdl->dragging = 0;\r
+                ret |= 1; break;\r
+              case DL_DRAGGING:\r
+               hdl->dragging = 1;\r
+               dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);\r
+               if (dest > hdl->dummyitem) dest = hdl->dummyitem;\r
+               DrawInsert (hwnd, dlm->hWnd, dest);\r
+               if (dest >= 0)\r
+                   SetWindowLongPtr(hwnd, DWLP_MSGRESULT, DL_MOVECURSOR);\r
+               else\r
+                   SetWindowLongPtr(hwnd, DWLP_MSGRESULT, DL_STOPCURSOR);\r
+                ret |= 1; break;\r
+              case DL_DROPPED:\r
+               if (hdl->dragging) {\r
+                   dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);\r
+                   if (dest > hdl->dummyitem) dest = hdl->dummyitem;\r
+                   DrawInsert (hwnd, dlm->hWnd, -1);\r
+               }\r
+               SendDlgItemMessage(hwnd, hdl->listid,\r
+                                  LB_DELETESTRING, hdl->dummyitem, 0);\r
+               if (hdl->dragging) {\r
+                   hdl->dragging = 0;\r
+                   if (dest >= 0) {\r
+                       /* Correct for "missing" item. */\r
+                       if (dest > hdl->srcitem) dest--;\r
+                       pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest);\r
+                   }\r
+                   ret |= 2;\r
+               }\r
+                ret |= 1; break;\r
+            }\r
+        }\r
+\r
+    } else {\r
+\r
+        if (((LOWORD(wParam) == hdl->upbid) ||\r
+             (LOWORD(wParam) == hdl->dnbid)) &&\r
+            ((HIWORD(wParam) == BN_CLICKED) ||\r
+             (HIWORD(wParam) == BN_DOUBLECLICKED))) {\r
+            /* Move an item up or down the list. */\r
+            /* Get the current selection, if any. */\r
+            int selection = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCURSEL, 0, 0);\r
+            if (selection == LB_ERR) {\r
+                MessageBeep(0);\r
+            } else {\r
+                int nitems;\r
+                /* Get the total number of items. */\r
+                nitems = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCOUNT, 0, 0);\r
+                /* Should we do anything? */\r
+               if (LOWORD(wParam) == hdl->upbid && (selection > 0))\r
+                   pl_moveitem(hwnd, hdl->listid, selection, selection - 1);\r
+               else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1))\r
+                   pl_moveitem(hwnd, hdl->listid, selection, selection + 1);\r
+               ret |= 2;\r
+            }\r
+\r
+        }\r
+\r
+    }\r
+\r
+    if (array) {\r
+       /* Update array to match the list box. */\r
+       for (i=0; i < maxmemb; i++)\r
+           array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA,\r
+                                          i, 0);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * A progress bar (from Common Controls). We like our progress bars\r
+ * to be smooth and unbroken, without those ugly divisions; some\r
+ * older compilers may not support that, but that's life.\r
+ */\r
+void progressbar(struct ctlpos *cp, int id)\r
+{\r
+    RECT r;\r
+\r
+    r.left = GAPBETWEEN;\r
+    r.top = cp->ypos;\r
+    r.right = cp->width;\r
+    r.bottom = PROGBARHEIGHT;\r
+    cp->ypos += r.bottom + GAPBETWEEN;\r
+\r
+    doctl(cp, r, PROGRESS_CLASS, WS_CHILD | WS_VISIBLE\r
+#ifdef PBS_SMOOTH\r
+         | PBS_SMOOTH\r
+#endif\r
+         , WS_EX_CLIENTEDGE, "", id);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Platform-specific side of portable dialog-box mechanism.\r
+ */\r
+\r
+/*\r
+ * This function takes a string, escapes all the ampersands, and\r
+ * places a single (unescaped) ampersand in front of the first\r
+ * occurrence of the given shortcut character (which may be\r
+ * NO_SHORTCUT).\r
+ * \r
+ * Return value is a malloc'ed copy of the processed version of the\r
+ * string.\r
+ */\r
+static char *shortcut_escape(const char *text, char shortcut)\r
+{\r
+    char *ret;\r
+    char const *p;\r
+    char *q;\r
+\r
+    if (!text)\r
+       return NULL;                   /* sfree won't choke on this */\r
+\r
+    ret = snewn(2*strlen(text)+1, char);   /* size potentially doubles! */\r
+    shortcut = tolower((unsigned char)shortcut);\r
+\r
+    p = text;\r
+    q = ret;\r
+    while (*p) {\r
+       if (shortcut != NO_SHORTCUT &&\r
+           tolower((unsigned char)*p) == shortcut) {\r
+           *q++ = '&';\r
+           shortcut = NO_SHORTCUT;    /* stop it happening twice */\r
+       } else if (*p == '&') {\r
+           *q++ = '&';\r
+       }\r
+       *q++ = *p++;\r
+    }\r
+    *q = '\0';\r
+    return ret;\r
+}\r
+\r
+void winctrl_add_shortcuts(struct dlgparam *dp, struct winctrl *c)\r
+{\r
+    int i;\r
+    for (i = 0; i < lenof(c->shortcuts); i++)\r
+       if (c->shortcuts[i] != NO_SHORTCUT) {\r
+           unsigned char s = tolower((unsigned char)c->shortcuts[i]);\r
+           assert(!dp->shortcuts[s]);\r
+           dp->shortcuts[s] = TRUE;\r
+       }\r
+}\r
+\r
+void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c)\r
+{\r
+    int i;\r
+    for (i = 0; i < lenof(c->shortcuts); i++)\r
+       if (c->shortcuts[i] != NO_SHORTCUT) {\r
+           unsigned char s = tolower((unsigned char)c->shortcuts[i]);\r
+           assert(dp->shortcuts[s]);\r
+           dp->shortcuts[s] = FALSE;\r
+       }\r
+}\r
+\r
+static int winctrl_cmp_byctrl(void *av, void *bv)\r
+{\r
+    struct winctrl *a = (struct winctrl *)av;\r
+    struct winctrl *b = (struct winctrl *)bv;\r
+    if (a->ctrl < b->ctrl)\r
+       return -1;\r
+    else if (a->ctrl > b->ctrl)\r
+       return +1;\r
+    else\r
+       return 0;\r
+}\r
+static int winctrl_cmp_byid(void *av, void *bv)\r
+{\r
+    struct winctrl *a = (struct winctrl *)av;\r
+    struct winctrl *b = (struct winctrl *)bv;\r
+    if (a->base_id < b->base_id)\r
+       return -1;\r
+    else if (a->base_id > b->base_id)\r
+       return +1;\r
+    else\r
+       return 0;\r
+}\r
+static int winctrl_cmp_byctrl_find(void *av, void *bv)\r
+{\r
+    union control *a = (union control *)av;\r
+    struct winctrl *b = (struct winctrl *)bv;\r
+    if (a < b->ctrl)\r
+       return -1;\r
+    else if (a > b->ctrl)\r
+       return +1;\r
+    else\r
+       return 0;\r
+}\r
+static int winctrl_cmp_byid_find(void *av, void *bv)\r
+{\r
+    int *a = (int *)av;\r
+    struct winctrl *b = (struct winctrl *)bv;\r
+    if (*a < b->base_id)\r
+       return -1;\r
+    else if (*a >= b->base_id + b->num_ids)\r
+       return +1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+void winctrl_init(struct winctrls *wc)\r
+{\r
+    wc->byctrl = newtree234(winctrl_cmp_byctrl);\r
+    wc->byid = newtree234(winctrl_cmp_byid);\r
+}\r
+void winctrl_cleanup(struct winctrls *wc)\r
+{\r
+    struct winctrl *c;\r
+\r
+    while ((c = index234(wc->byid, 0)) != NULL) {\r
+       winctrl_remove(wc, c);\r
+       sfree(c->data);\r
+       sfree(c);\r
+    }\r
+\r
+    freetree234(wc->byctrl);\r
+    freetree234(wc->byid);\r
+    wc->byctrl = wc->byid = NULL;\r
+}\r
+\r
+void winctrl_add(struct winctrls *wc, struct winctrl *c)\r
+{\r
+    struct winctrl *ret;\r
+    if (c->ctrl) {\r
+       ret = add234(wc->byctrl, c);\r
+       assert(ret == c);\r
+    }\r
+    ret = add234(wc->byid, c);\r
+    assert(ret == c);\r
+}\r
+\r
+void winctrl_remove(struct winctrls *wc, struct winctrl *c)\r
+{\r
+    struct winctrl *ret;\r
+    ret = del234(wc->byctrl, c);\r
+    ret = del234(wc->byid, c);\r
+    assert(ret == c);\r
+}\r
+\r
+struct winctrl *winctrl_findbyctrl(struct winctrls *wc, union control *ctrl)\r
+{\r
+    return find234(wc->byctrl, ctrl, winctrl_cmp_byctrl_find);\r
+}\r
+\r
+struct winctrl *winctrl_findbyid(struct winctrls *wc, int id)\r
+{\r
+    return find234(wc->byid, &id, winctrl_cmp_byid_find);\r
+}\r
+\r
+struct winctrl *winctrl_findbyindex(struct winctrls *wc, int index)\r
+{\r
+    return index234(wc->byid, index);\r
+}\r
+\r
+void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,\r
+                   struct ctlpos *cp, struct controlset *s, int *id)\r
+{\r
+    struct ctlpos columns[16];\r
+    int ncols, colstart, colspan;\r
+\r
+    struct ctlpos tabdelays[16];\r
+    union control *tabdelayed[16];\r
+    int ntabdelays;\r
+\r
+    struct ctlpos pos;\r
+\r
+    char shortcuts[MAX_SHORTCUTS_PER_CTRL];\r
+    int nshortcuts;\r
+    char *escaped;\r
+    int i, actual_base_id, base_id, num_ids;\r
+    void *data;\r
+\r
+    base_id = *id;\r
+\r
+    /* Start a containing box, if we have a boxname. */\r
+    if (s->boxname && *s->boxname) {\r
+       struct winctrl *c = snew(struct winctrl);\r
+       c->ctrl = NULL;\r
+       c->base_id = base_id;\r
+       c->num_ids = 1;\r
+       c->data = NULL;\r
+       memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts));\r
+       winctrl_add(wc, c);\r
+       beginbox(cp, s->boxtitle, base_id);\r
+       base_id++;\r
+    }\r
+\r
+    /* Draw a title, if we have one. */\r
+    if (!s->boxname && s->boxtitle) {\r
+       struct winctrl *c = snew(struct winctrl);\r
+       c->ctrl = NULL;\r
+       c->base_id = base_id;\r
+       c->num_ids = 1;\r
+       c->data = dupstr(s->boxtitle);\r
+       memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts));\r
+       winctrl_add(wc, c);\r
+       paneltitle(cp, base_id);\r
+       base_id++;\r
+    }\r
+\r
+    /* Initially we have just one column. */\r
+    ncols = 1;\r
+    columns[0] = *cp;                 /* structure copy */\r
+\r
+    /* And initially, there are no pending tab-delayed controls. */\r
+    ntabdelays = 0;\r
+\r
+    /* Loop over each control in the controlset. */\r
+    for (i = 0; i < s->ncontrols; i++) {\r
+       union control *ctrl = s->ctrls[i];\r
+\r
+       /*\r
+        * Generic processing that pertains to all control types.\r
+        * At the end of this if statement, we'll have produced\r
+        * `ctrl' (a pointer to the control we have to create, or\r
+        * think about creating, in this iteration of the loop),\r
+        * `pos' (a suitable ctlpos with which to position it), and\r
+        * `c' (a winctrl structure to receive details of the\r
+        * dialog IDs). Or we'll have done a `continue', if it was\r
+        * CTRL_COLUMNS and doesn't require any control creation at\r
+        * all.\r
+        */\r
+       if (ctrl->generic.type == CTRL_COLUMNS) {\r
+           assert((ctrl->columns.ncols == 1) ^ (ncols == 1));\r
+\r
+           if (ncols == 1) {\r
+               /*\r
+                * We're splitting into multiple columns.\r
+                */\r
+               int lpercent, rpercent, lx, rx, i;\r
+\r
+               ncols = ctrl->columns.ncols;\r
+               assert(ncols <= lenof(columns));\r
+               for (i = 1; i < ncols; i++)\r
+                   columns[i] = columns[0];   /* structure copy */\r
+\r
+               lpercent = 0;\r
+               for (i = 0; i < ncols; i++) {\r
+                   rpercent = lpercent + ctrl->columns.percentages[i];\r
+                   lx = columns[i].xoff + lpercent *\r
+                       (columns[i].width + GAPBETWEEN) / 100;\r
+                   rx = columns[i].xoff + rpercent *\r
+                       (columns[i].width + GAPBETWEEN) / 100;\r
+                   columns[i].xoff = lx;\r
+                   columns[i].width = rx - lx - GAPBETWEEN;\r
+                   lpercent = rpercent;\r
+               }\r
+           } else {\r
+               /*\r
+                * We're recombining the various columns into one.\r
+                */\r
+               int maxy = columns[0].ypos;\r
+               int i;\r
+               for (i = 1; i < ncols; i++)\r
+                   if (maxy < columns[i].ypos)\r
+                       maxy = columns[i].ypos;\r
+               ncols = 1;\r
+               columns[0] = *cp;      /* structure copy */\r
+               columns[0].ypos = maxy;\r
+           }\r
+\r
+           continue;\r
+       } else if (ctrl->generic.type == CTRL_TABDELAY) {\r
+           int i;\r
+\r
+           assert(!ctrl->generic.tabdelay);\r
+           ctrl = ctrl->tabdelay.ctrl;\r
+\r
+           for (i = 0; i < ntabdelays; i++)\r
+               if (tabdelayed[i] == ctrl)\r
+                   break;\r
+           assert(i < ntabdelays);    /* we have to have found it */\r
+\r
+           pos = tabdelays[i];        /* structure copy */\r
+\r
+           colstart = colspan = -1;   /* indicate this was tab-delayed */\r
+\r
+       } else {\r
+           /*\r
+            * If it wasn't one of those, it's a genuine control;\r
+            * so we'll have to compute a position for it now, by\r
+            * checking its column span.\r
+            */\r
+           int col;\r
+\r
+           colstart = COLUMN_START(ctrl->generic.column);\r
+           colspan = COLUMN_SPAN(ctrl->generic.column);\r
+\r
+           pos = columns[colstart];   /* structure copy */\r
+           pos.width = columns[colstart+colspan-1].width +\r
+               (columns[colstart+colspan-1].xoff - columns[colstart].xoff);\r
+\r
+           for (col = colstart; col < colstart+colspan; col++)\r
+               if (pos.ypos < columns[col].ypos)\r
+                   pos.ypos = columns[col].ypos;\r
+\r
+           /*\r
+            * If this control is to be tabdelayed, add it to the\r
+            * tabdelay list, and unset pos.hwnd to inhibit actual\r
+            * control creation.\r
+            */\r
+           if (ctrl->generic.tabdelay) {\r
+               assert(ntabdelays < lenof(tabdelays));\r
+               tabdelays[ntabdelays] = pos;   /* structure copy */\r
+               tabdelayed[ntabdelays] = ctrl;\r
+               ntabdelays++;\r
+               pos.hwnd = NULL;\r
+           }\r
+       }\r
+\r
+       /* Most controls don't need anything in c->data. */\r
+       data = NULL;\r
+\r
+       /* And they all start off with no shortcuts registered. */\r
+       memset(shortcuts, NO_SHORTCUT, lenof(shortcuts));\r
+       nshortcuts = 0;\r
+\r
+       /* Almost all controls start at base_id. */\r
+       actual_base_id = base_id;\r
+\r
+       /*\r
+        * Now we're ready to actually create the control, by\r
+        * switching on its type.\r
+        */\r
+       switch (ctrl->generic.type) {\r
+         case CTRL_TEXT:\r
+           {\r
+               char *wrapped, *escaped;\r
+               int lines;\r
+               num_ids = 1;\r
+               wrapped = staticwrap(&pos, cp->hwnd,\r
+                                    ctrl->generic.label, &lines);\r
+               escaped = shortcut_escape(wrapped, NO_SHORTCUT);\r
+               statictext(&pos, escaped, lines, base_id);\r
+               sfree(escaped);\r
+               sfree(wrapped);\r
+           }\r
+           break;\r
+         case CTRL_EDITBOX:\r
+           num_ids = 2;               /* static, edit */\r
+           escaped = shortcut_escape(ctrl->editbox.label,\r
+                                     ctrl->editbox.shortcut);\r
+           shortcuts[nshortcuts++] = ctrl->editbox.shortcut;\r
+           if (ctrl->editbox.percentwidth == 100) {\r
+               if (ctrl->editbox.has_list)\r
+                   combobox(&pos, escaped,\r
+                            base_id, base_id+1);\r
+               else\r
+                   editboxfw(&pos, ctrl->editbox.password, escaped,\r
+                             base_id, base_id+1);\r
+           } else {\r
+               if (ctrl->editbox.has_list) {\r
+                   staticcombo(&pos, escaped, base_id, base_id+1,\r
+                               ctrl->editbox.percentwidth);\r
+               } else {\r
+                   (ctrl->editbox.password ? staticpassedit : staticedit)\r
+                       (&pos, escaped, base_id, base_id+1,\r
+                        ctrl->editbox.percentwidth);\r
+               }\r
+           }\r
+           sfree(escaped);\r
+           break;\r
+         case CTRL_RADIO:\r
+           num_ids = ctrl->radio.nbuttons + 1;   /* label as well */\r
+           {\r
+               struct radio *buttons;\r
+               int i;\r
+\r
+               escaped = shortcut_escape(ctrl->radio.label,\r
+                                         ctrl->radio.shortcut);\r
+               shortcuts[nshortcuts++] = ctrl->radio.shortcut;\r
+\r
+               buttons = snewn(ctrl->radio.nbuttons, struct radio);\r
+\r
+               for (i = 0; i < ctrl->radio.nbuttons; i++) {\r
+                   buttons[i].text =\r
+                       shortcut_escape(ctrl->radio.buttons[i],\r
+                                       (char)(ctrl->radio.shortcuts ?\r
+                                              ctrl->radio.shortcuts[i] :\r
+                                              NO_SHORTCUT));\r
+                   buttons[i].id = base_id + 1 + i;\r
+                   if (ctrl->radio.shortcuts) {\r
+                       assert(nshortcuts < MAX_SHORTCUTS_PER_CTRL);\r
+                       shortcuts[nshortcuts++] = ctrl->radio.shortcuts[i];\r
+                   }\r
+               }\r
+\r
+               radioline_common(&pos, escaped, base_id,\r
+                                ctrl->radio.ncolumns,\r
+                                buttons, ctrl->radio.nbuttons);\r
+\r
+               for (i = 0; i < ctrl->radio.nbuttons; i++) {\r
+                   sfree(buttons[i].text);\r
+               }\r
+               sfree(buttons);\r
+               sfree(escaped);\r
+           }\r
+           break;\r
+         case CTRL_CHECKBOX:\r
+           num_ids = 1;\r
+           escaped = shortcut_escape(ctrl->checkbox.label,\r
+                                     ctrl->checkbox.shortcut);\r
+           shortcuts[nshortcuts++] = ctrl->checkbox.shortcut;\r
+           checkbox(&pos, escaped, base_id);\r
+           sfree(escaped);\r
+           break;\r
+         case CTRL_BUTTON:\r
+           escaped = shortcut_escape(ctrl->button.label,\r
+                                     ctrl->button.shortcut);\r
+           shortcuts[nshortcuts++] = ctrl->button.shortcut;\r
+           if (ctrl->button.iscancel)\r
+               actual_base_id = IDCANCEL;\r
+           num_ids = 1;\r
+           button(&pos, escaped, actual_base_id, ctrl->button.isdefault);\r
+           sfree(escaped);\r
+           break;\r
+         case CTRL_LISTBOX:\r
+           num_ids = 2;\r
+           escaped = shortcut_escape(ctrl->listbox.label,\r
+                                     ctrl->listbox.shortcut);\r
+           shortcuts[nshortcuts++] = ctrl->listbox.shortcut;\r
+           if (ctrl->listbox.draglist) {\r
+               data = snew(struct prefslist);\r
+               num_ids = 4;\r
+               prefslist(data, &pos, ctrl->listbox.height, escaped,\r
+                         base_id, base_id+1, base_id+2, base_id+3);\r
+               shortcuts[nshortcuts++] = 'u';   /* Up */\r
+               shortcuts[nshortcuts++] = 'd';   /* Down */\r
+           } else if (ctrl->listbox.height == 0) {\r
+               /* Drop-down list. */\r
+               if (ctrl->listbox.percentwidth == 100) {\r
+                   staticddlbig(&pos, escaped,\r
+                                base_id, base_id+1);\r
+               } else {\r
+                   staticddl(&pos, escaped, base_id,\r
+                             base_id+1, ctrl->listbox.percentwidth);\r
+               }\r
+           } else {\r
+               /* Ordinary list. */\r
+               listbox(&pos, escaped, base_id, base_id+1,\r
+                       ctrl->listbox.height, ctrl->listbox.multisel);\r
+           }\r
+           if (ctrl->listbox.ncols) {\r
+               /*\r
+                * This method of getting the box width is a bit of\r
+                * a hack; we'd do better to try to retrieve the\r
+                * actual width in dialog units from doctl() just\r
+                * before MapDialogRect. But that's going to be no\r
+                * fun, and this should be good enough accuracy.\r
+                */\r
+               int width = cp->width * ctrl->listbox.percentwidth;\r
+               int *tabarray;\r
+               int i, percent;\r
+\r
+               tabarray = snewn(ctrl->listbox.ncols-1, int);\r
+               percent = 0;\r
+               for (i = 0; i < ctrl->listbox.ncols-1; i++) {\r
+                   percent += ctrl->listbox.percentages[i];\r
+                   tabarray[i] = width * percent / 10000;\r
+               }\r
+               SendDlgItemMessage(cp->hwnd, base_id+1, LB_SETTABSTOPS,\r
+                                  ctrl->listbox.ncols-1, (LPARAM)tabarray);\r
+               sfree(tabarray);\r
+           }\r
+           sfree(escaped);\r
+           break;\r
+         case CTRL_FILESELECT:\r
+           num_ids = 3;\r
+           escaped = shortcut_escape(ctrl->fileselect.label,\r
+                                     ctrl->fileselect.shortcut);\r
+           shortcuts[nshortcuts++] = ctrl->fileselect.shortcut;\r
+           editbutton(&pos, escaped, base_id, base_id+1,\r
+                      "Bro&wse...", base_id+2);\r
+           shortcuts[nshortcuts++] = 'w';\r
+           sfree(escaped);\r
+           break;\r
+         case CTRL_FONTSELECT:\r
+           num_ids = 3;\r
+           escaped = shortcut_escape(ctrl->fontselect.label,\r
+                                     ctrl->fontselect.shortcut);\r
+           shortcuts[nshortcuts++] = ctrl->fontselect.shortcut;\r
+           statictext(&pos, escaped, 1, base_id);\r
+           staticbtn(&pos, "", base_id+1, "Change...", base_id+2);\r
+           sfree(escaped);\r
+           data = snew(FontSpec);\r
+           break;\r
+         default:\r
+           assert(!"Can't happen");\r
+           num_ids = 0;               /* placate gcc */\r
+           break;\r
+       }\r
+\r
+       /*\r
+        * Create a `struct winctrl' for this control, and advance\r
+        * the dialog ID counter, if it's actually been created\r
+        * (and isn't tabdelayed).\r
+        */\r
+       if (pos.hwnd) {\r
+           struct winctrl *c = snew(struct winctrl);\r
+\r
+           c->ctrl = ctrl;\r
+           c->base_id = actual_base_id;\r
+           c->num_ids = num_ids;\r
+           c->data = data;\r
+           memcpy(c->shortcuts, shortcuts, sizeof(shortcuts));\r
+           winctrl_add(wc, c);\r
+           winctrl_add_shortcuts(dp, c);\r
+           if (actual_base_id == base_id)\r
+               base_id += num_ids;\r
+       }\r
+\r
+       if (colstart >= 0) {\r
+           /*\r
+            * Update the ypos in all columns crossed by this\r
+            * control.\r
+            */\r
+           int i;\r
+           for (i = colstart; i < colstart+colspan; i++)\r
+               columns[i].ypos = pos.ypos;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * We've now finished laying out the controls; so now update\r
+     * the ctlpos and control ID that were passed in, terminate\r
+     * any containing box, and return.\r
+     */\r
+    for (i = 0; i < ncols; i++)\r
+       if (cp->ypos < columns[i].ypos)\r
+           cp->ypos = columns[i].ypos;\r
+    *id = base_id;\r
+\r
+    if (s->boxname && *s->boxname)\r
+       endbox(cp);\r
+}\r
+\r
+static void winctrl_set_focus(union control *ctrl, struct dlgparam *dp,\r
+                             int has_focus)\r
+{\r
+    if (has_focus) {\r
+       if (dp->focused)\r
+           dp->lastfocused = dp->focused;\r
+       dp->focused = ctrl;\r
+    } else if (!has_focus && dp->focused == ctrl) {\r
+       dp->lastfocused = dp->focused;\r
+       dp->focused = NULL;\r
+    }\r
+}\r
+\r
+union control *dlg_last_focused(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    return dp->focused == ctrl ? dp->lastfocused : dp->focused;\r
+}\r
+\r
+/*\r
+ * The dialog-box procedure calls this function to handle Windows\r
+ * messages on a control we manage.\r
+ */\r
+int winctrl_handle_command(struct dlgparam *dp, UINT msg,\r
+                          WPARAM wParam, LPARAM lParam)\r
+{\r
+    struct winctrl *c;\r
+    union control *ctrl;\r
+    int i, id, ret;\r
+    static UINT draglistmsg = WM_NULL;\r
+\r
+    /*\r
+     * Filter out pointless window messages. Our interest is in\r
+     * WM_COMMAND and the drag list message, and nothing else.\r
+     */\r
+    if (draglistmsg == WM_NULL)\r
+       draglistmsg = RegisterWindowMessage (DRAGLISTMSGSTRING);\r
+\r
+    if (msg != draglistmsg && msg != WM_COMMAND && msg != WM_DRAWITEM)\r
+       return 0;\r
+\r
+    /*\r
+     * Look up the control ID in our data.\r
+     */\r
+    c = NULL;\r
+    for (i = 0; i < dp->nctrltrees; i++) {\r
+       c = winctrl_findbyid(dp->controltrees[i], LOWORD(wParam));\r
+       if (c)\r
+           break;\r
+    }\r
+    if (!c)\r
+       return 0;                      /* we have nothing to do */\r
+\r
+    if (msg == WM_DRAWITEM) {\r
+       /*\r
+        * Owner-draw request for a panel title.\r
+        */\r
+       LPDRAWITEMSTRUCT di = (LPDRAWITEMSTRUCT) lParam;\r
+       HDC hdc = di->hDC;\r
+       RECT r = di->rcItem;\r
+       SIZE s;\r
+\r
+       SetMapMode(hdc, MM_TEXT);      /* ensure logical units == pixels */\r
+\r
+       GetTextExtentPoint32(hdc, (char *)c->data,\r
+                                strlen((char *)c->data), &s);\r
+       DrawEdge(hdc, &r, EDGE_ETCHED, BF_ADJUST | BF_RECT);\r
+       TextOut(hdc,\r
+               r.left + (r.right-r.left-s.cx)/2,\r
+               r.top + (r.bottom-r.top-s.cy)/2,\r
+               (char *)c->data, strlen((char *)c->data));\r
+\r
+       return TRUE;\r
+    }\r
+\r
+    ctrl = c->ctrl;\r
+    id = LOWORD(wParam) - c->base_id;\r
+\r
+    if (!ctrl || !ctrl->generic.handler)\r
+       return 0;                      /* nothing we can do here */\r
+\r
+    /*\r
+     * From here on we do not issue `return' statements until the\r
+     * very end of the dialog box: any event handler is entitled to\r
+     * ask for a colour selector, so we _must_ always allow control\r
+     * to reach the end of this switch statement so that the\r
+     * subsequent code can test dp->coloursel_wanted().\r
+     */\r
+    ret = 0;\r
+    dp->coloursel_wanted = FALSE;\r
+\r
+    /*\r
+     * Now switch on the control type and the message.\r
+     */\r
+    switch (ctrl->generic.type) {\r
+      case CTRL_EDITBOX:\r
+       if (msg == WM_COMMAND && !ctrl->editbox.has_list &&\r
+           (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS);\r
+       if (msg == WM_COMMAND && ctrl->editbox.has_list &&\r
+           (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS);\r
+\r
+       if (msg == WM_COMMAND && !ctrl->editbox.has_list &&\r
+           HIWORD(wParam) == EN_CHANGE)\r
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+       if (msg == WM_COMMAND &&\r
+           ctrl->editbox.has_list) {\r
+           if (HIWORD(wParam) == CBN_SELCHANGE) {\r
+               int index, len;\r
+               char *text;\r
+\r
+               index = SendDlgItemMessage(dp->hwnd, c->base_id+1,\r
+                                          CB_GETCURSEL, 0, 0);\r
+               len = SendDlgItemMessage(dp->hwnd, c->base_id+1,\r
+                                        CB_GETLBTEXTLEN, index, 0);\r
+               text = snewn(len+1, char);\r
+               SendDlgItemMessage(dp->hwnd, c->base_id+1, CB_GETLBTEXT,\r
+                                  index, (LPARAM)text);\r
+               SetDlgItemText(dp->hwnd, c->base_id+1, text);\r
+               sfree(text);\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+           } else if (HIWORD(wParam) == CBN_EDITCHANGE) {\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+           } else if (HIWORD(wParam) == CBN_KILLFOCUS) {\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);\r
+           }\r
+\r
+       }\r
+       break;\r
+      case CTRL_RADIO:\r
+       if (msg == WM_COMMAND &&\r
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);\r
+       /*\r
+        * We sometimes get spurious BN_CLICKED messages for the\r
+        * radio button that is just about to _lose_ selection, if\r
+        * we're switching using the arrow keys. Therefore we\r
+        * double-check that the button in wParam is actually\r
+        * checked before generating an event.\r
+        */\r
+       if (msg == WM_COMMAND &&\r
+           (HIWORD(wParam) == BN_CLICKED ||\r
+            HIWORD(wParam) == BN_DOUBLECLICKED) &&\r
+           IsDlgButtonChecked(dp->hwnd, LOWORD(wParam))) {\r
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+       }\r
+       break;\r
+      case CTRL_CHECKBOX:\r
+       if (msg == WM_COMMAND &&\r
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);\r
+       if (msg == WM_COMMAND &&\r
+           (HIWORD(wParam) == BN_CLICKED ||\r
+            HIWORD(wParam) == BN_DOUBLECLICKED)) {\r
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+       }\r
+       break;\r
+      case CTRL_BUTTON:\r
+       if (msg == WM_COMMAND &&\r
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);\r
+       if (msg == WM_COMMAND &&\r
+           (HIWORD(wParam) == BN_CLICKED ||\r
+            HIWORD(wParam) == BN_DOUBLECLICKED)) {\r
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION);\r
+       }\r
+       break;\r
+      case CTRL_LISTBOX:\r
+       if (msg == WM_COMMAND && ctrl->listbox.height != 0 &&\r
+           (HIWORD(wParam)==LBN_SETFOCUS || HIWORD(wParam)==LBN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == LBN_SETFOCUS);\r
+       if (msg == WM_COMMAND && ctrl->listbox.height == 0 &&\r
+           (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS);\r
+       if (msg == WM_COMMAND && id >= 2 &&\r
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);\r
+       if (ctrl->listbox.draglist) {\r
+           int pret;\r
+           pret = handle_prefslist(c->data, NULL, 0, (msg != WM_COMMAND),\r
+                                   dp->hwnd, wParam, lParam);\r
+           if (pret & 2)\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+           ret = pret & 1;\r
+       } else {\r
+           if (msg == WM_COMMAND && HIWORD(wParam) == LBN_DBLCLK) {\r
+               SetCapture(dp->hwnd);\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION);\r
+           } else if (msg == WM_COMMAND && HIWORD(wParam) == LBN_SELCHANGE) {\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_SELCHANGE);\r
+           }\r
+       }\r
+       break;\r
+      case CTRL_FILESELECT:\r
+       if (msg == WM_COMMAND && id == 1 &&\r
+           (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS);\r
+       if (msg == WM_COMMAND && id == 2 &&\r
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);\r
+       if (msg == WM_COMMAND && id == 1 && HIWORD(wParam) == EN_CHANGE)\r
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+       if (id == 2 &&\r
+           (msg == WM_COMMAND &&\r
+            (HIWORD(wParam) == BN_CLICKED ||\r
+             HIWORD(wParam) == BN_DOUBLECLICKED))) {\r
+           OPENFILENAME of;\r
+           char filename[FILENAME_MAX];\r
+\r
+           memset(&of, 0, sizeof(of));\r
+           of.hwndOwner = dp->hwnd;\r
+           if (ctrl->fileselect.filter)\r
+               of.lpstrFilter = ctrl->fileselect.filter;\r
+           else\r
+               of.lpstrFilter = "All Files (*.*)\0*\0\0\0";\r
+           of.lpstrCustomFilter = NULL;\r
+           of.nFilterIndex = 1;\r
+           of.lpstrFile = filename;\r
+           GetDlgItemText(dp->hwnd, c->base_id+1, filename, lenof(filename));\r
+           filename[lenof(filename)-1] = '\0';\r
+           of.nMaxFile = lenof(filename);\r
+           of.lpstrFileTitle = NULL;\r
+           of.lpstrTitle = ctrl->fileselect.title;\r
+           of.Flags = 0;\r
+           if (request_file(NULL, &of, FALSE, ctrl->fileselect.for_writing)) {\r
+               SetDlgItemText(dp->hwnd, c->base_id + 1, filename);\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+           }\r
+       }\r
+       break;\r
+      case CTRL_FONTSELECT:\r
+       if (msg == WM_COMMAND && id == 2 &&\r
+           (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS))\r
+           winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS);\r
+       if (id == 2 &&\r
+           (msg == WM_COMMAND &&\r
+            (HIWORD(wParam) == BN_CLICKED ||\r
+             HIWORD(wParam) == BN_DOUBLECLICKED))) {\r
+           CHOOSEFONT cf;\r
+           LOGFONT lf;\r
+           HDC hdc;\r
+           FontSpec fs = *(FontSpec *)c->data;\r
+           \r
+           hdc = GetDC(0);\r
+           lf.lfHeight = -MulDiv(fs.height,\r
+                                 GetDeviceCaps(hdc, LOGPIXELSY), 72);\r
+           ReleaseDC(0, hdc);\r
+           lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;\r
+           lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;\r
+           lf.lfWeight = (fs.isbold ? FW_BOLD : 0);\r
+           lf.lfCharSet = fs.charset;\r
+           lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
+           lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
+           lf.lfQuality = DEFAULT_QUALITY;\r
+           lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;\r
+           strncpy(lf.lfFaceName, fs.name,\r
+                   sizeof(lf.lfFaceName) - 1);\r
+           lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0';\r
+\r
+           cf.lStructSize = sizeof(cf);\r
+           cf.hwndOwner = dp->hwnd;\r
+           cf.lpLogFont = &lf;\r
+           cf.Flags = (dp->fixed_pitch_fonts ? CF_FIXEDPITCHONLY : 0) |\r
+                CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;\r
+\r
+           if (ChooseFont(&cf)) {\r
+               strncpy(fs.name, lf.lfFaceName,\r
+                       sizeof(fs.name) - 1);\r
+               fs.name[sizeof(fs.name) - 1] = '\0';\r
+               fs.isbold = (lf.lfWeight == FW_BOLD);\r
+               fs.charset = lf.lfCharSet;\r
+               fs.height = cf.iPointSize / 10;\r
+               dlg_fontsel_set(ctrl, dp, fs);\r
+               ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);\r
+           }\r
+       }\r
+       break;\r
+    }\r
+\r
+    /*\r
+     * If the above event handler has asked for a colour selector,\r
+     * now is the time to generate one.\r
+     */\r
+    if (dp->coloursel_wanted) {\r
+       static CHOOSECOLOR cc;\r
+       static DWORD custom[16] = { 0 };    /* zero initialisers */\r
+       cc.lStructSize = sizeof(cc);\r
+       cc.hwndOwner = dp->hwnd;\r
+       cc.hInstance = (HWND) hinst;\r
+       cc.lpCustColors = custom;\r
+       cc.rgbResult = RGB(dp->coloursel_result.r,\r
+                          dp->coloursel_result.g,\r
+                          dp->coloursel_result.b);\r
+       cc.Flags = CC_FULLOPEN | CC_RGBINIT;\r
+       if (ChooseColor(&cc)) {\r
+           dp->coloursel_result.r =\r
+               (unsigned char) (cc.rgbResult & 0xFF);\r
+           dp->coloursel_result.g =\r
+               (unsigned char) (cc.rgbResult >> 8) & 0xFF;\r
+           dp->coloursel_result.b =\r
+               (unsigned char) (cc.rgbResult >> 16) & 0xFF;\r
+           dp->coloursel_result.ok = TRUE;\r
+       } else\r
+           dp->coloursel_result.ok = FALSE;\r
+       ctrl->generic.handler(ctrl, dp, dp->data, EVENT_CALLBACK);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * This function can be called to produce context help on a\r
+ * control. Returns TRUE if it has actually launched some help.\r
+ */\r
+int winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id)\r
+{\r
+    int i;\r
+    struct winctrl *c;\r
+\r
+    /*\r
+     * Look up the control ID in our data.\r
+     */\r
+    c = NULL;\r
+    for (i = 0; i < dp->nctrltrees; i++) {\r
+       c = winctrl_findbyid(dp->controltrees[i], id);\r
+       if (c)\r
+           break;\r
+    }\r
+    if (!c)\r
+       return 0;                      /* we have nothing to do */\r
+\r
+    /*\r
+     * This is the Windows front end, so we're allowed to assume\r
+     * `helpctx.p' is a context string.\r
+     */\r
+    if (!c->ctrl || !c->ctrl->generic.helpctx.p)\r
+       return 0;                      /* no help available for this ctrl */\r
+\r
+    launch_help(hwnd, c->ctrl->generic.helpctx.p);\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Now the various functions that the platform-independent\r
+ * mechanism can call to access the dialog box entries.\r
+ */\r
+\r
+static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, union control *ctrl)\r
+{\r
+    int i;\r
+\r
+    for (i = 0; i < dp->nctrltrees; i++) {\r
+       struct winctrl *c = winctrl_findbyctrl(dp->controltrees[i], ctrl);\r
+       if (c)\r
+           return c;\r
+    }\r
+    return NULL;\r
+}\r
+\r
+void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_RADIO);\r
+    CheckRadioButton(dp->hwnd,\r
+                    c->base_id + 1,\r
+                    c->base_id + c->ctrl->radio.nbuttons,\r
+                    c->base_id + 1 + whichbutton);\r
+}\r
+\r
+int dlg_radiobutton_get(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int i;\r
+    assert(c && c->ctrl->generic.type == CTRL_RADIO);\r
+    for (i = 0; i < c->ctrl->radio.nbuttons; i++)\r
+       if (IsDlgButtonChecked(dp->hwnd, c->base_id + 1 + i))\r
+           return i;\r
+    assert(!"No radio button was checked?!");\r
+    return 0;\r
+}\r
+\r
+void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);\r
+    CheckDlgButton(dp->hwnd, c->base_id, (checked != 0));\r
+}\r
+\r
+int dlg_checkbox_get(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);\r
+    return 0 != IsDlgButtonChecked(dp->hwnd, c->base_id);\r
+}\r
+\r
+void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_EDITBOX);\r
+    SetDlgItemText(dp->hwnd, c->base_id+1, text);\r
+}\r
+\r
+void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_EDITBOX);\r
+    GetDlgItemText(dp->hwnd, c->base_id+1, buffer, length);\r
+    buffer[length-1] = '\0';\r
+}\r
+\r
+/* The `listbox' functions can also apply to combo boxes. */\r
+void dlg_listbox_clear(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int msg;\r
+    assert(c &&\r
+          (c->ctrl->generic.type == CTRL_LISTBOX ||\r
+           (c->ctrl->generic.type == CTRL_EDITBOX &&\r
+            c->ctrl->editbox.has_list)));\r
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?\r
+          LB_RESETCONTENT : CB_RESETCONTENT);\r
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0);\r
+}\r
+\r
+void dlg_listbox_del(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int msg;\r
+    assert(c &&\r
+          (c->ctrl->generic.type == CTRL_LISTBOX ||\r
+           (c->ctrl->generic.type == CTRL_EDITBOX &&\r
+            c->ctrl->editbox.has_list)));\r
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?\r
+          LB_DELETESTRING : CB_DELETESTRING);\r
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);\r
+}\r
+\r
+void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int msg;\r
+    assert(c &&\r
+          (c->ctrl->generic.type == CTRL_LISTBOX ||\r
+           (c->ctrl->generic.type == CTRL_EDITBOX &&\r
+            c->ctrl->editbox.has_list)));\r
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?\r
+          LB_ADDSTRING : CB_ADDSTRING);\r
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text);\r
+}\r
+\r
+/*\r
+ * Each listbox entry may have a numeric id associated with it.\r
+ * Note that some front ends only permit a string to be stored at\r
+ * each position, which means that _if_ you put two identical\r
+ * strings in any listbox then you MUST not assign them different\r
+ * IDs and expect to get meaningful results back.\r
+ */\r
+void dlg_listbox_addwithid(union control *ctrl, void *dlg,\r
+                          char const *text, int id)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int msg, msg2, index;\r
+    assert(c &&\r
+          (c->ctrl->generic.type == CTRL_LISTBOX ||\r
+           (c->ctrl->generic.type == CTRL_EDITBOX &&\r
+            c->ctrl->editbox.has_list)));\r
+    msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?\r
+          LB_ADDSTRING : CB_ADDSTRING);\r
+    msg2 = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?\r
+          LB_SETITEMDATA : CB_SETITEMDATA);\r
+    index = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text);\r
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg2, index, (LPARAM)id);\r
+}\r
+\r
+int dlg_listbox_getid(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int msg;\r
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX);\r
+    msg = (c->ctrl->listbox.height != 0 ? LB_GETITEMDATA : CB_GETITEMDATA);\r
+    return\r
+       SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);\r
+}\r
+\r
+/* dlg_listbox_index returns <0 if no single element is selected. */\r
+int dlg_listbox_index(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int msg, ret;\r
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX);\r
+    if (c->ctrl->listbox.multisel) {\r
+       assert(c->ctrl->listbox.height != 0); /* not combo box */\r
+       ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSELCOUNT, 0, 0);\r
+       if (ret == LB_ERR || ret > 1)\r
+           return -1;\r
+    }\r
+    msg = (c->ctrl->listbox.height != 0 ? LB_GETCURSEL : CB_GETCURSEL);\r
+    ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0);\r
+    if (ret == LB_ERR)\r
+       return -1;\r
+    else\r
+       return ret;\r
+}\r
+\r
+int dlg_listbox_issel(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&\r
+          c->ctrl->listbox.multisel &&\r
+          c->ctrl->listbox.height != 0);\r
+    return\r
+       SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSEL, index, 0);\r
+}\r
+\r
+void dlg_listbox_select(union control *ctrl, void *dlg, int index)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int msg;\r
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&\r
+          !c->ctrl->listbox.multisel);\r
+    msg = (c->ctrl->listbox.height != 0 ? LB_SETCURSEL : CB_SETCURSEL);\r
+    SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0);\r
+}\r
+\r
+void dlg_text_set(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_TEXT);\r
+    SetDlgItemText(dp->hwnd, c->base_id, text);\r
+}\r
+\r
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    char *escaped = NULL;\r
+    int id = -1;\r
+\r
+    assert(c);\r
+    switch (c->ctrl->generic.type) {\r
+      case CTRL_EDITBOX:\r
+       escaped = shortcut_escape(text, c->ctrl->editbox.shortcut);\r
+       id = c->base_id;\r
+       break;\r
+      case CTRL_RADIO:\r
+       escaped = shortcut_escape(text, c->ctrl->radio.shortcut);\r
+       id = c->base_id;\r
+       break;\r
+      case CTRL_CHECKBOX:\r
+       escaped = shortcut_escape(text, ctrl->checkbox.shortcut);\r
+       id = c->base_id;\r
+       break;\r
+      case CTRL_BUTTON:\r
+       escaped = shortcut_escape(text, ctrl->button.shortcut);\r
+       id = c->base_id;\r
+       break;\r
+      case CTRL_LISTBOX:\r
+       escaped = shortcut_escape(text, ctrl->listbox.shortcut);\r
+       id = c->base_id;\r
+       break;\r
+      case CTRL_FILESELECT:\r
+       escaped = shortcut_escape(text, ctrl->fileselect.shortcut);\r
+       id = c->base_id;\r
+       break;\r
+      case CTRL_FONTSELECT:\r
+       escaped = shortcut_escape(text, ctrl->fontselect.shortcut);\r
+       id = c->base_id;\r
+       break;\r
+      default:\r
+       assert(!"Can't happen");\r
+       break;\r
+    }\r
+    if (escaped) {\r
+       SetDlgItemText(dp->hwnd, id, escaped);\r
+       sfree(escaped);\r
+    }\r
+}\r
+\r
+void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_FILESELECT);\r
+    SetDlgItemText(dp->hwnd, c->base_id+1, fn.path);\r
+}\r
+\r
+void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_FILESELECT);\r
+    GetDlgItemText(dp->hwnd, c->base_id+1, fn->path, lenof(fn->path));\r
+    fn->path[lenof(fn->path)-1] = '\0';\r
+}\r
+\r
+void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs)\r
+{\r
+    char *buf, *boldstr;\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);\r
+\r
+    *(FontSpec *)c->data = fs;        /* structure copy */\r
+\r
+    boldstr = (fs.isbold ? "bold, " : "");\r
+    if (fs.height == 0)\r
+       buf = dupprintf("Font: %s, %sdefault height", fs.name, boldstr);\r
+    else\r
+       buf = dupprintf("Font: %s, %s%d-%s", fs.name, boldstr,\r
+                       (fs.height < 0 ? -fs.height : fs.height),\r
+                       (fs.height < 0 ? "pixel" : "point"));\r
+    SetDlgItemText(dp->hwnd, c->base_id+1, buf);\r
+    sfree(buf);\r
+\r
+    dlg_auto_set_fixed_pitch_flag(dp);\r
+}\r
+\r
+void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);\r
+    *fs = *(FontSpec *)c->data;               /* structure copy */\r
+}\r
+\r
+/*\r
+ * Bracketing a large set of updates in these two functions will\r
+ * cause the front end (if possible) to delay updating the screen\r
+ * until it's all complete, thus avoiding flicker.\r
+ */\r
+void dlg_update_start(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    if (c && c->ctrl->generic.type == CTRL_LISTBOX) {\r
+       SendDlgItemMessage(dp->hwnd, c->base_id+1, WM_SETREDRAW, FALSE, 0);\r
+    }\r
+}\r
+\r
+void dlg_update_done(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    if (c && c->ctrl->generic.type == CTRL_LISTBOX) {\r
+       HWND hw = GetDlgItem(dp->hwnd, c->base_id+1);\r
+       SendMessage(hw, WM_SETREDRAW, TRUE, 0);\r
+       InvalidateRect(hw, NULL, TRUE);\r
+    }\r
+}\r
+\r
+void dlg_set_focus(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct winctrl *c = dlg_findbyctrl(dp, ctrl);\r
+    int id;\r
+    HWND ctl;\r
+    switch (ctrl->generic.type) {\r
+      case CTRL_EDITBOX: id = c->base_id + 1; break;\r
+      case CTRL_RADIO:\r
+       for (id = c->base_id + ctrl->radio.nbuttons; id > 1; id--)\r
+           if (IsDlgButtonChecked(dp->hwnd, id))\r
+               break;\r
+       /*\r
+        * In the theoretically-unlikely case that no button was\r
+        * selected, id should come out of this as 1, which is a\r
+        * reasonable enough choice.\r
+        */\r
+       break;\r
+      case CTRL_CHECKBOX: id = c->base_id; break;\r
+      case CTRL_BUTTON: id = c->base_id; break;\r
+      case CTRL_LISTBOX: id = c->base_id + 1; break;\r
+      case CTRL_FILESELECT: id = c->base_id + 1; break;\r
+      case CTRL_FONTSELECT: id = c->base_id + 2; break;\r
+      default: id = c->base_id; break;\r
+    }\r
+    ctl = GetDlgItem(dp->hwnd, id);\r
+    SetFocus(ctl);\r
+}\r
+\r
+/*\r
+ * During event processing, you might well want to give an error\r
+ * indication to the user. dlg_beep() is a quick and easy generic\r
+ * error; dlg_error() puts up a message-box or equivalent.\r
+ */\r
+void dlg_beep(void *dlg)\r
+{\r
+    /* struct dlgparam *dp = (struct dlgparam *)dlg; */\r
+    MessageBeep(0);\r
+}\r
+\r
+void dlg_error_msg(void *dlg, char *msg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    MessageBox(dp->hwnd, msg,\r
+              dp->errtitle ? dp->errtitle : NULL,\r
+              MB_OK | MB_ICONERROR);\r
+}\r
+\r
+/*\r
+ * This function signals to the front end that the dialog's\r
+ * processing is completed, and passes an integer value (typically\r
+ * a success status).\r
+ */\r
+void dlg_end(void *dlg, int value)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    dp->ended = TRUE;\r
+    dp->endresult = value;\r
+}\r
+\r
+void dlg_refresh(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    int i, j;\r
+    struct winctrl *c;\r
+\r
+    if (!ctrl) {\r
+       /*\r
+        * Send EVENT_REFRESH to absolutely everything.\r
+        */\r
+       for (j = 0; j < dp->nctrltrees; j++) {\r
+           for (i = 0;\r
+                (c = winctrl_findbyindex(dp->controltrees[j], i)) != NULL;\r
+                i++) {\r
+               if (c->ctrl && c->ctrl->generic.handler != NULL)\r
+                   c->ctrl->generic.handler(c->ctrl, dp,\r
+                                            dp->data, EVENT_REFRESH);\r
+           }\r
+       }\r
+    } else {\r
+       /*\r
+        * Send EVENT_REFRESH to a specific control.\r
+        */\r
+       if (ctrl->generic.handler != NULL)\r
+           ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);\r
+    }\r
+}\r
+\r
+void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    dp->coloursel_wanted = TRUE;\r
+    dp->coloursel_result.r = r;\r
+    dp->coloursel_result.g = g;\r
+    dp->coloursel_result.b = b;\r
+}\r
+\r
+int dlg_coloursel_results(union control *ctrl, void *dlg,\r
+                         int *r, int *g, int *b)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    if (dp->coloursel_result.ok) {\r
+       *r = dp->coloursel_result.r;\r
+       *g = dp->coloursel_result.g;\r
+       *b = dp->coloursel_result.b;\r
+       return 1;\r
+    } else\r
+       return 0;\r
+}\r
+\r
+void dlg_auto_set_fixed_pitch_flag(void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    Config *cfg = (Config *)dp->data;\r
+    HFONT font;\r
+    HDC hdc;\r
+    TEXTMETRIC tm;\r
+    int is_var;\r
+\r
+    /*\r
+     * Attempt to load the current font, and see if it's\r
+     * variable-pitch. If so, start off the fixed-pitch flag for the\r
+     * dialog box as false.\r
+     *\r
+     * We assume here that any client of the dlg_* mechanism which is\r
+     * using font selectors at all is also using a normal 'Config *'\r
+     * as dp->data.\r
+     */\r
+\r
+    font = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,\r
+                      DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,\r
+                      CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg->font_quality),\r
+                      FIXED_PITCH | FF_DONTCARE, cfg->font.name);\r
+    hdc = GetDC(NULL);\r
+    if (font && hdc && SelectObject(hdc, font) && GetTextMetrics(hdc, &tm)) {\r
+        /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */\r
+        is_var = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH);\r
+    } else {\r
+        is_var = FALSE;                /* assume it's basically normal */\r
+    }\r
+    if (hdc)\r
+        ReleaseDC(NULL, hdc);\r
+    if (font)\r
+        DeleteObject(font);\r
+\r
+    if (is_var)\r
+        dp->fixed_pitch_fonts = FALSE;\r
+}\r
+\r
+int dlg_get_fixed_pitch_flag(void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    return dp->fixed_pitch_fonts;\r
+}\r
+\r
+void dlg_set_fixed_pitch_flag(void *dlg, int flag)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    dp->fixed_pitch_fonts = flag;\r
+}\r
+\r
+struct perctrl_privdata {\r
+    union control *ctrl;\r
+    void *data;\r
+    int needs_free;\r
+};\r
+\r
+static int perctrl_privdata_cmp(void *av, void *bv)\r
+{\r
+    struct perctrl_privdata *a = (struct perctrl_privdata *)av;\r
+    struct perctrl_privdata *b = (struct perctrl_privdata *)bv;\r
+    if (a->ctrl < b->ctrl)\r
+       return -1;\r
+    else if (a->ctrl > b->ctrl)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+void dp_init(struct dlgparam *dp)\r
+{\r
+    dp->nctrltrees = 0;\r
+    dp->data = NULL;\r
+    dp->ended = FALSE;\r
+    dp->focused = dp->lastfocused = NULL;\r
+    memset(dp->shortcuts, 0, sizeof(dp->shortcuts));\r
+    dp->hwnd = NULL;\r
+    dp->wintitle = dp->errtitle = NULL;\r
+    dp->privdata = newtree234(perctrl_privdata_cmp);\r
+    dp->fixed_pitch_fonts = TRUE;\r
+}\r
+\r
+void dp_add_tree(struct dlgparam *dp, struct winctrls *wc)\r
+{\r
+    assert(dp->nctrltrees < lenof(dp->controltrees));\r
+    dp->controltrees[dp->nctrltrees++] = wc;\r
+}\r
+\r
+void dp_cleanup(struct dlgparam *dp)\r
+{\r
+    struct perctrl_privdata *p;\r
+\r
+    if (dp->privdata) {\r
+       while ( (p = index234(dp->privdata, 0)) != NULL ) {\r
+           del234(dp->privdata, p);\r
+           if (p->needs_free)\r
+               sfree(p->data);\r
+           sfree(p);\r
+       }\r
+       freetree234(dp->privdata);\r
+       dp->privdata = NULL;\r
+    }\r
+    sfree(dp->wintitle);\r
+    sfree(dp->errtitle);\r
+}\r
+\r
+void *dlg_get_privdata(union control *ctrl, void *dlg)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct perctrl_privdata tmp, *p;\r
+    tmp.ctrl = ctrl;\r
+    p = find234(dp->privdata, &tmp, NULL);\r
+    if (p)\r
+       return p->data;\r
+    else\r
+       return NULL;\r
+}\r
+\r
+void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct perctrl_privdata tmp, *p;\r
+    tmp.ctrl = ctrl;\r
+    p = find234(dp->privdata, &tmp, NULL);\r
+    if (!p) {\r
+       p = snew(struct perctrl_privdata);\r
+       p->ctrl = ctrl;\r
+       p->needs_free = FALSE;\r
+       add234(dp->privdata, p);\r
+    }\r
+    p->data = ptr;\r
+}\r
+\r
+void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size)\r
+{\r
+    struct dlgparam *dp = (struct dlgparam *)dlg;\r
+    struct perctrl_privdata tmp, *p;\r
+    tmp.ctrl = ctrl;\r
+    p = find234(dp->privdata, &tmp, NULL);\r
+    if (!p) {\r
+       p = snew(struct perctrl_privdata);\r
+       p->ctrl = ctrl;\r
+       p->needs_free = FALSE;\r
+       add234(dp->privdata, p);\r
+    }\r
+    assert(!p->needs_free);\r
+    p->needs_free = TRUE;\r
+    /*\r
+     * This is an internal allocation routine, so it's allowed to\r
+     * use smalloc directly.\r
+     */\r
+    p->data = smalloc(size);\r
+    return p->data;\r
+}\r
diff --git a/putty/WINDOWS/WINDEFS.C b/putty/WINDOWS/WINDEFS.C
new file mode 100644 (file)
index 0000000..de01daf
--- /dev/null
@@ -0,0 +1,43 @@
+/*\r
+ * windefs.c: default settings that are specific to Windows.\r
+ */\r
+\r
+#include "putty.h"\r
+\r
+#include <commctrl.h>\r
+\r
+FontSpec platform_default_fontspec(const char *name)\r
+{\r
+    FontSpec ret;\r
+    if (!strcmp(name, "Font")) {\r
+       strcpy(ret.name, "Courier New");\r
+       ret.isbold = 0;\r
+       ret.charset = ANSI_CHARSET;\r
+       ret.height = 10;\r
+    } else {\r
+       ret.name[0] = '\0';\r
+    }\r
+    return ret;\r
+}\r
+\r
+Filename platform_default_filename(const char *name)\r
+{\r
+    Filename ret;\r
+    if (!strcmp(name, "LogFileName"))\r
+       strcpy(ret.path, "putty.log");\r
+    else\r
+       *ret.path = '\0';\r
+    return ret;\r
+}\r
+\r
+char *platform_default_s(const char *name)\r
+{\r
+    if (!strcmp(name, "SerialLine"))\r
+       return dupstr("COM1");\r
+    return NULL;\r
+}\r
+\r
+int platform_default_i(const char *name, int def)\r
+{\r
+    return def;\r
+}\r
diff --git a/putty/WINDOWS/WINDLG.C b/putty/WINDOWS/WINDLG.C
new file mode 100644 (file)
index 0000000..debc510
--- /dev/null
@@ -0,0 +1,926 @@
+/*\r
+ * windlg.c - dialogs for PuTTY(tel), including the configuration dialog.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <limits.h>\r
+#include <assert.h>\r
+#include <ctype.h>\r
+#include <time.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "win_res.h"\r
+#include "storage.h"\r
+#include "dialog.h"\r
+\r
+#include <commctrl.h>\r
+#include <commdlg.h>\r
+#include <shellapi.h>\r
+\r
+#ifdef MSVC4\r
+#define TVINSERTSTRUCT  TV_INSERTSTRUCT\r
+#define TVITEM          TV_ITEM\r
+#define ICON_BIG        1\r
+#endif\r
+\r
+/*\r
+ * These are the various bits of data required to handle the\r
+ * portable-dialog stuff in the config box. Having them at file\r
+ * scope in here isn't too bad a place to put them; if we were ever\r
+ * to need more than one config box per process we could always\r
+ * shift them to a per-config-box structure stored in GWL_USERDATA.\r
+ */\r
+static struct controlbox *ctrlbox;\r
+/*\r
+ * ctrls_base holds the OK and Cancel buttons: the controls which\r
+ * are present in all dialog panels. ctrls_panel holds the ones\r
+ * which change from panel to panel.\r
+ */\r
+static struct winctrls ctrls_base, ctrls_panel;\r
+static struct dlgparam dp;\r
+\r
+static char **events = NULL;\r
+static int nevents = 0, negsize = 0;\r
+\r
+extern Config cfg;                    /* defined in window.c */\r
+\r
+#define PRINTER_DISABLED_STRING "None (printing disabled)"\r
+\r
+void force_normal(HWND hwnd)\r
+{\r
+    static int recurse = 0;\r
+\r
+    WINDOWPLACEMENT wp;\r
+\r
+    if (recurse)\r
+       return;\r
+    recurse = 1;\r
+\r
+    wp.length = sizeof(wp);\r
+    if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {\r
+       wp.showCmd = SW_SHOWNORMAL;\r
+       SetWindowPlacement(hwnd, &wp);\r
+    }\r
+    recurse = 0;\r
+}\r
+\r
+static int CALLBACK LogProc(HWND hwnd, UINT msg,\r
+                           WPARAM wParam, LPARAM lParam)\r
+{\r
+    int i;\r
+\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       {\r
+           char *str = dupprintf("%s Event Log", appname);\r
+           SetWindowText(hwnd, str);\r
+           sfree(str);\r
+       }\r
+       {\r
+           static int tabs[4] = { 78, 108 };\r
+           SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,\r
+                              (LPARAM) tabs);\r
+       }\r
+       for (i = 0; i < nevents; i++)\r
+           SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,\r
+                              0, (LPARAM) events[i]);\r
+       return 1;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           logbox = NULL;\r
+           SetActiveWindow(GetParent(hwnd));\r
+           DestroyWindow(hwnd);\r
+           return 0;\r
+         case IDN_COPY:\r
+           if (HIWORD(wParam) == BN_CLICKED ||\r
+               HIWORD(wParam) == BN_DOUBLECLICKED) {\r
+               int selcount;\r
+               int *selitems;\r
+               selcount = SendDlgItemMessage(hwnd, IDN_LIST,\r
+                                             LB_GETSELCOUNT, 0, 0);\r
+               if (selcount == 0) {   /* don't even try to copy zero items */\r
+                   MessageBeep(0);\r
+                   break;\r
+               }\r
+\r
+               selitems = snewn(selcount, int);\r
+               if (selitems) {\r
+                   int count = SendDlgItemMessage(hwnd, IDN_LIST,\r
+                                                  LB_GETSELITEMS,\r
+                                                  selcount,\r
+                                                  (LPARAM) selitems);\r
+                   int i;\r
+                   int size;\r
+                   char *clipdata;\r
+                   static unsigned char sel_nl[] = SEL_NL;\r
+\r
+                   if (count == 0) {  /* can't copy zero stuff */\r
+                       MessageBeep(0);\r
+                       break;\r
+                   }\r
+\r
+                   size = 0;\r
+                   for (i = 0; i < count; i++)\r
+                       size +=\r
+                           strlen(events[selitems[i]]) + sizeof(sel_nl);\r
+\r
+                   clipdata = snewn(size, char);\r
+                   if (clipdata) {\r
+                       char *p = clipdata;\r
+                       for (i = 0; i < count; i++) {\r
+                           char *q = events[selitems[i]];\r
+                           int qlen = strlen(q);\r
+                           memcpy(p, q, qlen);\r
+                           p += qlen;\r
+                           memcpy(p, sel_nl, sizeof(sel_nl));\r
+                           p += sizeof(sel_nl);\r
+                       }\r
+                       write_aclip(NULL, clipdata, size, TRUE);\r
+                       sfree(clipdata);\r
+                   }\r
+                   sfree(selitems);\r
+\r
+                   for (i = 0; i < nevents; i++)\r
+                       SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,\r
+                                          FALSE, i);\r
+               }\r
+           }\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       logbox = NULL;\r
+       SetActiveWindow(GetParent(hwnd));\r
+       DestroyWindow(hwnd);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+static int CALLBACK LicenceProc(HWND hwnd, UINT msg,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       {\r
+           char *str = dupprintf("%s Licence", appname);\r
+           SetWindowText(hwnd, str);\r
+           sfree(str);\r
+       }\r
+       return 1;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           EndDialog(hwnd, 1);\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       EndDialog(hwnd, 1);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+static int CALLBACK AboutProc(HWND hwnd, UINT msg,\r
+                             WPARAM wParam, LPARAM lParam)\r
+{\r
+    char *str;\r
+\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       str = dupprintf("About %s", appname);\r
+       SetWindowText(hwnd, str);\r
+       sfree(str);\r
+       SetDlgItemText(hwnd, IDA_TEXT1, appname);\r
+       SetDlgItemText(hwnd, IDA_VERSION, ver);\r
+       return 1;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           EndDialog(hwnd, TRUE);\r
+           return 0;\r
+         case IDA_LICENCE:\r
+           EnableWindow(hwnd, 0);\r
+           DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),\r
+                     hwnd, LicenceProc);\r
+           EnableWindow(hwnd, 1);\r
+           SetActiveWindow(hwnd);\r
+           return 0;\r
+\r
+         case IDA_WEB:\r
+           /* Load web browser */\r
+           ShellExecute(hwnd, "open",\r
+                        "http://www.chiark.greenend.org.uk/~sgtatham/putty/",\r
+                        0, 0, SW_SHOWDEFAULT);\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       EndDialog(hwnd, TRUE);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+static int SaneDialogBox(HINSTANCE hinst,\r
+                        LPCTSTR tmpl,\r
+                        HWND hwndparent,\r
+                        DLGPROC lpDialogFunc)\r
+{\r
+    WNDCLASS wc;\r
+    HWND hwnd;\r
+    MSG msg;\r
+    int flags;\r
+    int ret;\r
+    int gm;\r
+\r
+    wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;\r
+    wc.lpfnWndProc = DefDlgProc;\r
+    wc.cbClsExtra = 0;\r
+    wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);\r
+    wc.hInstance = hinst;\r
+    wc.hIcon = NULL;\r
+    wc.hCursor = LoadCursor(NULL, IDC_ARROW);\r
+    wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);\r
+    wc.lpszMenuName = NULL;\r
+    wc.lpszClassName = "PuTTYConfigBox";\r
+    RegisterClass(&wc);\r
+\r
+    hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);\r
+\r
+    SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */\r
+    SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */\r
+\r
+    while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {\r
+       flags=GetWindowLongPtr(hwnd, BOXFLAGS);\r
+       if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))\r
+           DispatchMessage(&msg);\r
+       if (flags & DF_END)\r
+           break;\r
+    }\r
+\r
+    if (gm == 0)\r
+        PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */\r
+\r
+    ret=GetWindowLongPtr(hwnd, BOXRESULT);\r
+    DestroyWindow(hwnd);\r
+    return ret;\r
+}\r
+\r
+static void SaneEndDialog(HWND hwnd, int ret)\r
+{\r
+    SetWindowLongPtr(hwnd, BOXRESULT, ret);\r
+    SetWindowLongPtr(hwnd, BOXFLAGS, DF_END);\r
+}\r
+\r
+/*\r
+ * Null dialog procedure.\r
+ */\r
+static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    return 0;\r
+}\r
+\r
+enum {\r
+    IDCX_ABOUT = IDC_ABOUT,\r
+    IDCX_TVSTATIC,\r
+    IDCX_TREEVIEW,\r
+    IDCX_STDBASE,\r
+    IDCX_PANELBASE = IDCX_STDBASE + 32\r
+};\r
+\r
+struct treeview_faff {\r
+    HWND treeview;\r
+    HTREEITEM lastat[4];\r
+};\r
+\r
+static HTREEITEM treeview_insert(struct treeview_faff *faff,\r
+                                int level, char *text, char *path)\r
+{\r
+    TVINSERTSTRUCT ins;\r
+    int i;\r
+    HTREEITEM newitem;\r
+    ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);\r
+    ins.hInsertAfter = faff->lastat[level];\r
+#if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION\r
+#define INSITEM DUMMYUNIONNAME.item\r
+#else\r
+#define INSITEM item\r
+#endif\r
+    ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;\r
+    ins.INSITEM.pszText = text;\r
+    ins.INSITEM.cchTextMax = strlen(text)+1;\r
+    ins.INSITEM.lParam = (LPARAM)path;\r
+    newitem = TreeView_InsertItem(faff->treeview, &ins);\r
+    if (level > 0)\r
+       TreeView_Expand(faff->treeview, faff->lastat[level - 1],\r
+                       (level > 1 ? TVE_COLLAPSE : TVE_EXPAND));\r
+    faff->lastat[level] = newitem;\r
+    for (i = level + 1; i < 4; i++)\r
+       faff->lastat[i] = NULL;\r
+    return newitem;\r
+}\r
+\r
+/*\r
+ * Create the panelfuls of controls in the configuration box.\r
+ */\r
+static void create_controls(HWND hwnd, char *path)\r
+{\r
+    struct ctlpos cp;\r
+    int index;\r
+    int base_id;\r
+    struct winctrls *wc;\r
+\r
+    if (!path[0]) {\r
+       /*\r
+        * Here we must create the basic standard controls.\r
+        */\r
+       ctlposinit(&cp, hwnd, 3, 3, 235);\r
+       wc = &ctrls_base;\r
+       base_id = IDCX_STDBASE;\r
+    } else {\r
+       /*\r
+        * Otherwise, we're creating the controls for a particular\r
+        * panel.\r
+        */\r
+       ctlposinit(&cp, hwnd, 100, 3, 13);\r
+       wc = &ctrls_panel;\r
+       base_id = IDCX_PANELBASE;\r
+    }\r
+\r
+    for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {\r
+       struct controlset *s = ctrlbox->ctrlsets[index];\r
+       winctrl_layout(&dp, wc, &cp, s, &base_id);\r
+    }\r
+}\r
+\r
+/*\r
+ * This function is the configuration box.\r
+ * (Being a dialog procedure, in general it returns 0 if the default\r
+ * dialog processing should be performed, and 1 if it should not.)\r
+ */\r
+static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,\r
+                                      WPARAM wParam, LPARAM lParam)\r
+{\r
+    HWND hw, treeview;\r
+    struct treeview_faff tvfaff;\r
+    int ret;\r
+\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       dp.hwnd = hwnd;\r
+       create_controls(hwnd, "");     /* Open and Cancel buttons etc */\r
+       SetWindowText(hwnd, dp.wintitle);\r
+       SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);\r
+        if (has_help())\r
+            SetWindowLongPtr(hwnd, GWL_EXSTYLE,\r
+                            GetWindowLongPtr(hwnd, GWL_EXSTYLE) |\r
+                            WS_EX_CONTEXTHELP);\r
+        else {\r
+            HWND item = GetDlgItem(hwnd, IDC_HELPBTN);\r
+            if (item)\r
+                DestroyWindow(item);\r
+        }\r
+       SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,\r
+                   (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));\r
+       /*\r
+        * Centre the window.\r
+        */\r
+       {                              /* centre the window */\r
+           RECT rs, rd;\r
+\r
+           hw = GetDesktopWindow();\r
+           if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
+               MoveWindow(hwnd,\r
+                          (rs.right + rs.left + rd.left - rd.right) / 2,\r
+                          (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
+                          rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
+       }\r
+\r
+       /*\r
+        * Create the tree view.\r
+        */\r
+       {\r
+           RECT r;\r
+           WPARAM font;\r
+           HWND tvstatic;\r
+\r
+           r.left = 3;\r
+           r.right = r.left + 95;\r
+           r.top = 3;\r
+           r.bottom = r.top + 10;\r
+           MapDialogRect(hwnd, &r);\r
+           tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",\r
+                                     WS_CHILD | WS_VISIBLE,\r
+                                     r.left, r.top,\r
+                                     r.right - r.left, r.bottom - r.top,\r
+                                     hwnd, (HMENU) IDCX_TVSTATIC, hinst,\r
+                                     NULL);\r
+           font = SendMessage(hwnd, WM_GETFONT, 0, 0);\r
+           SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));\r
+\r
+           r.left = 3;\r
+           r.right = r.left + 95;\r
+           r.top = 13;\r
+           r.bottom = r.top + 219;\r
+           MapDialogRect(hwnd, &r);\r
+           treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",\r
+                                     WS_CHILD | WS_VISIBLE |\r
+                                     WS_TABSTOP | TVS_HASLINES |\r
+                                     TVS_DISABLEDRAGDROP | TVS_HASBUTTONS\r
+                                     | TVS_LINESATROOT |\r
+                                     TVS_SHOWSELALWAYS, r.left, r.top,\r
+                                     r.right - r.left, r.bottom - r.top,\r
+                                     hwnd, (HMENU) IDCX_TREEVIEW, hinst,\r
+                                     NULL);\r
+           font = SendMessage(hwnd, WM_GETFONT, 0, 0);\r
+           SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));\r
+           tvfaff.treeview = treeview;\r
+           memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));\r
+       }\r
+\r
+       /*\r
+        * Set up the tree view contents.\r
+        */\r
+       {\r
+           HTREEITEM hfirst = NULL;\r
+           int i;\r
+           char *path = NULL;\r
+\r
+           for (i = 0; i < ctrlbox->nctrlsets; i++) {\r
+               struct controlset *s = ctrlbox->ctrlsets[i];\r
+               HTREEITEM item;\r
+               int j;\r
+               char *c;\r
+\r
+               if (!s->pathname[0])\r
+                   continue;\r
+               j = path ? ctrl_path_compare(s->pathname, path) : 0;\r
+               if (j == INT_MAX)\r
+                   continue;          /* same path, nothing to add to tree */\r
+\r
+               /*\r
+                * We expect never to find an implicit path\r
+                * component. For example, we expect never to see\r
+                * A/B/C followed by A/D/E, because that would\r
+                * _implicitly_ create A/D. All our path prefixes\r
+                * are expected to contain actual controls and be\r
+                * selectable in the treeview; so we would expect\r
+                * to see A/D _explicitly_ before encountering\r
+                * A/D/E.\r
+                */\r
+               assert(j == ctrl_path_elements(s->pathname) - 1);\r
+\r
+               c = strrchr(s->pathname, '/');\r
+               if (!c)\r
+                       c = s->pathname;\r
+               else\r
+                       c++;\r
+\r
+               item = treeview_insert(&tvfaff, j, c, s->pathname);\r
+               if (!hfirst)\r
+                   hfirst = item;\r
+\r
+               path = s->pathname;\r
+           }\r
+\r
+           /*\r
+            * Put the treeview selection on to the Session panel.\r
+            * This should also cause creation of the relevant\r
+            * controls.\r
+            */\r
+           TreeView_SelectItem(treeview, hfirst);\r
+       }\r
+\r
+       /*\r
+        * Set focus into the first available control.\r
+        */\r
+       {\r
+           int i;\r
+           struct winctrl *c;\r
+\r
+           for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;\r
+                i++) {\r
+               if (c->ctrl) {\r
+                   dlg_set_focus(c->ctrl, &dp);\r
+                   break;\r
+               }\r
+           }\r
+       }\r
+\r
+       SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);\r
+       return 0;\r
+      case WM_LBUTTONUP:\r
+       /*\r
+        * Button release should trigger WM_OK if there was a\r
+        * previous double click on the session list.\r
+        */\r
+       ReleaseCapture();\r
+       if (dp.ended)\r
+           SaneEndDialog(hwnd, dp.endresult ? 1 : 0);\r
+       break;\r
+      case WM_NOTIFY:\r
+       if (LOWORD(wParam) == IDCX_TREEVIEW &&\r
+           ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {\r
+           HTREEITEM i =\r
+               TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);\r
+           TVITEM item;\r
+           char buffer[64];\r
\r
+           SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);\r
\r
+           item.hItem = i;\r
+           item.pszText = buffer;\r
+           item.cchTextMax = sizeof(buffer);\r
+           item.mask = TVIF_TEXT | TVIF_PARAM;\r
+           TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);\r
+           {\r
+               /* Destroy all controls in the currently visible panel. */\r
+               int k;\r
+               HWND item;\r
+               struct winctrl *c;\r
+\r
+               while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {\r
+                   for (k = 0; k < c->num_ids; k++) {\r
+                       item = GetDlgItem(hwnd, c->base_id + k);\r
+                       if (item)\r
+                           DestroyWindow(item);\r
+                   }\r
+                   winctrl_rem_shortcuts(&dp, c);\r
+                   winctrl_remove(&ctrls_panel, c);\r
+                   sfree(c->data);\r
+                   sfree(c);\r
+               }\r
+           }\r
+           create_controls(hwnd, (char *)item.lParam);\r
+\r
+           dlg_refresh(NULL, &dp);    /* set up control values */\r
\r
+           SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);\r
+           InvalidateRect (hwnd, NULL, TRUE);\r
+\r
+           SetFocus(((LPNMHDR) lParam)->hwndFrom);     /* ensure focus stays */\r
+           return 0;\r
+       }\r
+       break;\r
+      case WM_COMMAND:\r
+      case WM_DRAWITEM:\r
+      default:                        /* also handle drag list msg here */\r
+       /*\r
+        * Only process WM_COMMAND once the dialog is fully formed.\r
+        */\r
+       if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {\r
+           ret = winctrl_handle_command(&dp, msg, wParam, lParam);\r
+           if (dp.ended && GetCapture() != hwnd)\r
+               SaneEndDialog(hwnd, dp.endresult ? 1 : 0);\r
+       } else\r
+           ret = 0;\r
+       return ret;\r
+      case WM_HELP:\r
+       if (!winctrl_context_help(&dp, hwnd,\r
+                                ((LPHELPINFO)lParam)->iCtrlId))\r
+           MessageBeep(0);\r
+        break;\r
+      case WM_CLOSE:\r
+       quit_help(hwnd);\r
+       SaneEndDialog(hwnd, 0);\r
+       return 0;\r
+\r
+       /* Grrr Explorer will maximize Dialogs! */\r
+      case WM_SIZE:\r
+       if (wParam == SIZE_MAXIMIZED)\r
+           force_normal(hwnd);\r
+       return 0;\r
+\r
+    }\r
+    return 0;\r
+}\r
+\r
+void modal_about_box(HWND hwnd)\r
+{\r
+    EnableWindow(hwnd, 0);\r
+    DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);\r
+    EnableWindow(hwnd, 1);\r
+    SetActiveWindow(hwnd);\r
+}\r
+\r
+void show_help(HWND hwnd)\r
+{\r
+    launch_help(hwnd, NULL);\r
+}\r
+\r
+void defuse_showwindow(void)\r
+{\r
+    /*\r
+     * Work around the fact that the app's first call to ShowWindow\r
+     * will ignore the default in favour of the shell-provided\r
+     * setting.\r
+     */\r
+    {\r
+       HWND hwnd;\r
+       hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),\r
+                           NULL, NullDlgProc);\r
+       ShowWindow(hwnd, SW_HIDE);\r
+       SetActiveWindow(hwnd);\r
+       DestroyWindow(hwnd);\r
+    }\r
+}\r
+\r
+int do_config(void)\r
+{\r
+    int ret;\r
+\r
+    ctrlbox = ctrl_new_box();\r
+    setup_config_box(ctrlbox, FALSE, 0, 0);\r
+    win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), FALSE, 0);\r
+    dp_init(&dp);\r
+    winctrl_init(&ctrls_base);\r
+    winctrl_init(&ctrls_panel);\r
+    dp_add_tree(&dp, &ctrls_base);\r
+    dp_add_tree(&dp, &ctrls_panel);\r
+    dp.wintitle = dupprintf("%s Configuration", appname);\r
+    dp.errtitle = dupprintf("%s Error", appname);\r
+    dp.data = &cfg;\r
+    dlg_auto_set_fixed_pitch_flag(&dp);\r
+    dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */\r
+\r
+    ret =\r
+       SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,\r
+                 GenericMainDlgProc);\r
+\r
+    ctrl_free_box(ctrlbox);\r
+    winctrl_cleanup(&ctrls_panel);\r
+    winctrl_cleanup(&ctrls_base);\r
+    dp_cleanup(&dp);\r
+\r
+    return ret;\r
+}\r
+\r
+int do_reconfig(HWND hwnd, int protcfginfo)\r
+{\r
+    Config backup_cfg;\r
+    int ret;\r
+\r
+    backup_cfg = cfg;                 /* structure copy */\r
+\r
+    ctrlbox = ctrl_new_box();\r
+    setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo);\r
+    win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE,\r
+                         cfg.protocol);\r
+    dp_init(&dp);\r
+    winctrl_init(&ctrls_base);\r
+    winctrl_init(&ctrls_panel);\r
+    dp_add_tree(&dp, &ctrls_base);\r
+    dp_add_tree(&dp, &ctrls_panel);\r
+    dp.wintitle = dupprintf("%s Reconfiguration", appname);\r
+    dp.errtitle = dupprintf("%s Error", appname);\r
+    dp.data = &cfg;\r
+    dlg_auto_set_fixed_pitch_flag(&dp);\r
+    dp.shortcuts['g'] = TRUE;         /* the treeview: `Cate&gory' */\r
+\r
+    ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,\r
+                 GenericMainDlgProc);\r
+\r
+    ctrl_free_box(ctrlbox);\r
+    winctrl_cleanup(&ctrls_base);\r
+    winctrl_cleanup(&ctrls_panel);\r
+    dp_cleanup(&dp);\r
+\r
+    if (!ret)\r
+       cfg = backup_cfg;              /* structure copy */\r
+\r
+    return ret;\r
+}\r
+\r
+void logevent(void *frontend, const char *string)\r
+{\r
+    char timebuf[40];\r
+    struct tm tm;\r
+\r
+    log_eventlog(logctx, string);\r
+\r
+    if (nevents >= negsize) {\r
+       negsize += 64;\r
+       events = sresize(events, negsize, char *);\r
+    }\r
+\r
+    tm=ltime();\r
+    strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);\r
+\r
+    events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);\r
+    strcpy(events[nevents], timebuf);\r
+    strcat(events[nevents], string);\r
+    if (logbox) {\r
+       int count;\r
+       SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,\r
+                          0, (LPARAM) events[nevents]);\r
+       count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);\r
+       SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);\r
+    }\r
+    nevents++;\r
+}\r
+\r
+void showeventlog(HWND hwnd)\r
+{\r
+    if (!logbox) {\r
+       logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),\r
+                             hwnd, LogProc);\r
+       ShowWindow(logbox, SW_SHOWNORMAL);\r
+    }\r
+    SetActiveWindow(logbox);\r
+}\r
+\r
+void showabout(HWND hwnd)\r
+{\r
+    DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);\r
+}\r
+\r
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
+                        char *keystr, char *fingerprint,\r
+                        void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    int ret;\r
+\r
+    static const char absentmsg[] =\r
+       "The server's host key is not cached in the registry. You\n"\r
+       "have no guarantee that the server is the computer you\n"\r
+       "think it is.\n"\r
+       "The server's %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you trust this host, hit Yes to add the key to\n"\r
+       "%s's cache and carry on connecting.\n"\r
+       "If you want to carry on connecting just once, without\n"\r
+       "adding the key to the cache, hit No.\n"\r
+       "If you do not trust this host, hit Cancel to abandon the\n"\r
+       "connection.\n";\r
+\r
+    static const char wrongmsg[] =\r
+       "WARNING - POTENTIAL SECURITY BREACH!\n"\r
+       "\n"\r
+       "The server's host key does not match the one %s has\n"\r
+       "cached in the registry. This means that either the\n"\r
+       "server administrator has changed the host key, or you\n"\r
+       "have actually connected to another computer pretending\n"\r
+       "to be the server.\n"\r
+       "The new %s key fingerprint is:\n"\r
+       "%s\n"\r
+       "If you were expecting this change and trust the new key,\n"\r
+       "hit Yes to update %s's cache and continue connecting.\n"\r
+       "If you want to carry on connecting but without updating\n"\r
+       "the cache, hit No.\n"\r
+       "If you want to abandon the connection completely, hit\n"\r
+       "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";\r
+\r
+    static const char mbtitle[] = "%s Security Alert";\r
+\r
+    /*\r
+     * Verify the key against the registry.\r
+     */\r
+    ret = verify_host_key(host, port, keytype, keystr);\r
+\r
+    if (ret == 0)                     /* success - key matched OK */\r
+       return 1;\r
+    else if (ret == 2) {              /* key was different */\r
+       int mbret;\r
+       char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,\r
+                              appname);\r
+       char *caption = dupprintf(mbtitle, appname);\r
+       mbret = message_box(text, caption,\r
+                           MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,\r
+                           HELPCTXID(errors_hostkey_changed));\r
+       assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);\r
+       sfree(text);\r
+       sfree(caption);\r
+       if (mbret == IDYES) {\r
+           store_host_key(host, port, keytype, keystr);\r
+           return 1;\r
+       } else if (mbret == IDNO)\r
+           return 1;\r
+    } else if (ret == 1) {            /* key was absent */\r
+       int mbret;\r
+       char *text = dupprintf(absentmsg, keytype, fingerprint, appname);\r
+       char *caption = dupprintf(mbtitle, appname);\r
+       mbret = message_box(text, caption,\r
+                           MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,\r
+                           HELPCTXID(errors_hostkey_absent));\r
+       assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);\r
+       sfree(text);\r
+       sfree(caption);\r
+       if (mbret == IDYES) {\r
+           store_host_key(host, port, keytype, keystr);\r
+           return 1;\r
+       } else if (mbret == IDNO)\r
+           return 1;\r
+    }\r
+    return 0;  /* abandon the connection */\r
+}\r
+\r
+/*\r
+ * Ask whether the selected algorithm is acceptable (since it was\r
+ * below the configured 'warn' threshold).\r
+ */\r
+int askalg(void *frontend, const char *algtype, const char *algname,\r
+          void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char mbtitle[] = "%s Security Alert";\r
+    static const char msg[] =\r
+       "The first %s supported by the server\n"\r
+       "is %.64s, which is below the configured\n"\r
+       "warning threshold.\n"\r
+       "Do you want to continue with this connection?\n";\r
+    char *message, *title;\r
+    int mbret;\r
+\r
+    message = dupprintf(msg, algtype, algname);\r
+    title = dupprintf(mbtitle, appname);\r
+    mbret = MessageBox(NULL, message, title,\r
+                      MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);\r
+    socket_reselect_all();\r
+    sfree(message);\r
+    sfree(title);\r
+    if (mbret == IDYES)\r
+       return 1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+/*\r
+ * Ask whether to wipe a session log file before writing to it.\r
+ * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).\r
+ */\r
+int askappend(void *frontend, Filename filename,\r
+             void (*callback)(void *ctx, int result), void *ctx)\r
+{\r
+    static const char msgtemplate[] =\r
+       "The session log file \"%.*s\" already exists.\n"\r
+       "You can overwrite it with a new session log,\n"\r
+       "append your session log to the end of it,\n"\r
+       "or disable session logging for this session.\n"\r
+       "Hit Yes to wipe the file, No to append to it,\n"\r
+       "or Cancel to disable logging.";\r
+    char *message;\r
+    char *mbtitle;\r
+    int mbret;\r
+\r
+    message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);\r
+    mbtitle = dupprintf("%s Log to File", appname);\r
+\r
+    mbret = MessageBox(NULL, message, mbtitle,\r
+                      MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);\r
+\r
+    socket_reselect_all();\r
+\r
+    sfree(message);\r
+    sfree(mbtitle);\r
+\r
+    if (mbret == IDYES)\r
+       return 2;\r
+    else if (mbret == IDNO)\r
+       return 1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+/*\r
+ * Warn about the obsolescent key file format.\r
+ * \r
+ * Uniquely among these functions, this one does _not_ expect a\r
+ * frontend handle. This means that if PuTTY is ported to a\r
+ * platform which requires frontend handles, this function will be\r
+ * an anomaly. Fortunately, the problem it addresses will not have\r
+ * been present on that platform, so it can plausibly be\r
+ * implemented as an empty function.\r
+ */\r
+void old_keyfile_warning(void)\r
+{\r
+    static const char mbtitle[] = "%s Key File Warning";\r
+    static const char message[] =\r
+       "You are loading an SSH-2 private key which has an\n"\r
+       "old version of the file format. This means your key\n"\r
+       "file is not fully tamperproof. Future versions of\n"\r
+       "%s may stop supporting this private key format,\n"\r
+       "so we recommend you convert your key to the new\n"\r
+       "format.\n"\r
+       "\n"\r
+       "You can perform this conversion by loading the key\n"\r
+       "into PuTTYgen and then saving it again.";\r
+\r
+    char *msg, *title;\r
+    msg = dupprintf(message, appname);\r
+    title = dupprintf(mbtitle, appname);\r
+\r
+    MessageBox(NULL, msg, title, MB_OK);\r
+\r
+    socket_reselect_all();\r
+\r
+    sfree(msg);\r
+    sfree(title);\r
+}\r
diff --git a/putty/WINDOWS/WINDOW.C b/putty/WINDOWS/WINDOW.C
new file mode 100644 (file)
index 0000000..cac7672
--- /dev/null
@@ -0,0 +1,5547 @@
+/*\r
+ * window.c - the PuTTY(tel) main program, which runs a PuTTY terminal\r
+ * emulator and backend in a window.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <time.h>\r
+#include <limits.h>\r
+#include <assert.h>\r
+\r
+#ifndef NO_MULTIMON\r
+#define COMPILE_MULTIMON_STUBS\r
+#endif\r
+\r
+#define PUTTY_DO_GLOBALS              /* actually _define_ globals */\r
+#include "putty.h"\r
+#include "terminal.h"\r
+#include "storage.h"\r
+#include "win_res.h"\r
+\r
+#ifndef NO_MULTIMON\r
+#include <multimon.h>\r
+#endif\r
+\r
+#include <imm.h>\r
+#include <commctrl.h>\r
+#include <richedit.h>\r
+#include <mmsystem.h>\r
+\r
+/* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of\r
+ * wParam are used by Windows, and should be masked off, so we shouldn't\r
+ * attempt to store information in them. Hence all these identifiers have\r
+ * the low 4 bits clear. Also, identifiers should < 0xF000. */\r
+\r
+#define IDM_SHOWLOG   0x0010\r
+#define IDM_NEWSESS   0x0020\r
+#define IDM_DUPSESS   0x0030\r
+#define IDM_RESTART   0x0040\r
+#define IDM_RECONF    0x0050\r
+#define IDM_CLRSB     0x0060\r
+#define IDM_RESET     0x0070\r
+#define IDM_HELP      0x0140\r
+#define IDM_ABOUT     0x0150\r
+#define IDM_SAVEDSESS 0x0160\r
+#define IDM_COPYALL   0x0170\r
+#define IDM_FULLSCREEN 0x0180\r
+#define IDM_PASTE     0x0190\r
+#define IDM_SPECIALSEP 0x0200\r
+\r
+#define IDM_SPECIAL_MIN 0x0400\r
+#define IDM_SPECIAL_MAX 0x0800\r
+\r
+#define IDM_SAVED_MIN 0x1000\r
+#define IDM_SAVED_MAX 0x5000\r
+#define MENU_SAVED_STEP 16\r
+/* Maximum number of sessions on saved-session submenu */\r
+#define MENU_SAVED_MAX ((IDM_SAVED_MAX-IDM_SAVED_MIN) / MENU_SAVED_STEP)\r
+\r
+#define WM_IGNORE_CLIP (WM_APP + 2)\r
+#define WM_FULLSCR_ON_MAX (WM_APP + 3)\r
+#define WM_AGENT_CALLBACK (WM_APP + 4)\r
+#define WM_GOT_CLIPDATA (WM_APP + 6)\r
+\r
+/* Needed for Chinese support and apparently not always defined. */\r
+#ifndef VK_PROCESSKEY\r
+#define VK_PROCESSKEY 0xE5\r
+#endif\r
+\r
+/* Mouse wheel support. */\r
+#ifndef WM_MOUSEWHEEL\r
+#define WM_MOUSEWHEEL 0x020A          /* not defined in earlier SDKs */\r
+#endif\r
+#ifndef WHEEL_DELTA\r
+#define WHEEL_DELTA 120\r
+#endif\r
+\r
+static Mouse_Button translate_button(Mouse_Button button);\r
+static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);\r
+static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,\r
+                       unsigned char *output);\r
+static void cfgtopalette(void);\r
+static void systopalette(void);\r
+static void init_palette(void);\r
+static void init_fonts(int, int);\r
+static void another_font(int);\r
+static void deinit_fonts(void);\r
+static void set_input_locale(HKL);\r
+static void update_savedsess_menu(void);\r
+static void init_flashwindow(void);\r
+\r
+static int is_full_screen(void);\r
+static void make_full_screen(void);\r
+static void clear_full_screen(void);\r
+static void flip_full_screen(void);\r
+static int process_clipdata(HGLOBAL clipdata, int unicode);\r
+\r
+/* Window layout information */\r
+static void reset_window(int);\r
+static int extra_width, extra_height;\r
+static int font_width, font_height, font_dualwidth, font_varpitch;\r
+static int offset_width, offset_height;\r
+static int was_zoomed = 0;\r
+static int prev_rows, prev_cols;\r
+  \r
+static int pending_netevent = 0;\r
+static WPARAM pend_netevent_wParam = 0;\r
+static LPARAM pend_netevent_lParam = 0;\r
+static void enact_pending_netevent(void);\r
+static void flash_window(int mode);\r
+static void sys_cursor_update(void);\r
+static int get_fullscreen_rect(RECT * ss);\r
+\r
+static int caret_x = -1, caret_y = -1;\r
+\r
+static int kbd_codepage;\r
+\r
+static void *ldisc;\r
+static Backend *back;\r
+static void *backhandle;\r
+\r
+static struct unicode_data ucsdata;\r
+static int must_close_session, session_closed;\r
+static int reconfiguring = FALSE;\r
+\r
+static const struct telnet_special *specials = NULL;\r
+static HMENU specials_menu = NULL;\r
+static int n_specials = 0;\r
+\r
+static wchar_t *clipboard_contents;\r
+static size_t clipboard_length;\r
+\r
+#define TIMING_TIMER_ID 1234\r
+static long timing_next_time;\r
+\r
+static struct {\r
+    HMENU menu;\r
+} popup_menus[2];\r
+enum { SYSMENU, CTXMENU };\r
+static HMENU savedsess_menu;\r
+\r
+Config cfg;                           /* exported to windlg.c */\r
+\r
+static struct sesslist sesslist;       /* for saved-session menu */\r
+\r
+struct agent_callback {\r
+    void (*callback)(void *, void *, int);\r
+    void *callback_ctx;\r
+    void *data;\r
+    int len;\r
+};\r
+\r
+#define FONT_NORMAL 0\r
+#define FONT_BOLD 1\r
+#define FONT_UNDERLINE 2\r
+#define FONT_BOLDUND 3\r
+#define FONT_WIDE      0x04\r
+#define FONT_HIGH      0x08\r
+#define FONT_NARROW    0x10\r
+\r
+#define FONT_OEM       0x20\r
+#define FONT_OEMBOLD   0x21\r
+#define FONT_OEMUND    0x22\r
+#define FONT_OEMBOLDUND 0x23\r
+\r
+#define FONT_MAXNO     0x2F\r
+#define FONT_SHIFT     5\r
+static HFONT fonts[FONT_MAXNO];\r
+static LOGFONT lfont;\r
+static int fontflag[FONT_MAXNO];\r
+static enum {\r
+    BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT\r
+} bold_mode;\r
+static enum {\r
+    UND_LINE, UND_FONT\r
+} und_mode;\r
+static int descent;\r
+\r
+#define NCFGCOLOURS 22\r
+#define NEXTCOLOURS 240\r
+#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)\r
+static COLORREF colours[NALLCOLOURS];\r
+static HPALETTE pal;\r
+static LPLOGPALETTE logpal;\r
+static RGBTRIPLE defpal[NALLCOLOURS];\r
+\r
+static HBITMAP caretbm;\r
+\r
+static int dbltime, lasttime, lastact;\r
+static Mouse_Button lastbtn;\r
+\r
+/* this allows xterm-style mouse handling. */\r
+static int send_raw_mouse = 0;\r
+static int wheel_accumulator = 0;\r
+\r
+static int busy_status = BUSY_NOT;\r
+\r
+static char *window_name, *icon_name;\r
+\r
+static int compose_state = 0;\r
+\r
+static UINT wm_mousewheel = WM_MOUSEWHEEL;\r
+\r
+/* Dummy routine, only required in plink. */\r
+void ldisc_update(void *frontend, int echo, int edit)\r
+{\r
+}\r
+\r
+char *get_ttymode(void *frontend, const char *mode)\r
+{\r
+    return term_get_ttymode(term, mode);\r
+}\r
+\r
+static void start_backend(void)\r
+{\r
+    const char *error;\r
+    char msg[1024], *title;\r
+    char *realhost;\r
+    int i;\r
+\r
+    /*\r
+     * Select protocol. This is farmed out into a table in a\r
+     * separate file to enable an ssh-free variant.\r
+     */\r
+    back = backend_from_proto(cfg.protocol);\r
+    if (back == NULL) {\r
+       char *str = dupprintf("%s Internal Error", appname);\r
+       MessageBox(NULL, "Unsupported protocol number found",\r
+                  str, MB_OK | MB_ICONEXCLAMATION);\r
+       sfree(str);\r
+       cleanup_exit(1);\r
+    }\r
+\r
+    error = back->init(NULL, &backhandle, &cfg,\r
+                      cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,\r
+                      cfg.tcp_keepalives);\r
+    back->provide_logctx(backhandle, logctx);\r
+    if (error) {\r
+       char *str = dupprintf("%s Error", appname);\r
+       sprintf(msg, "Unable to open connection to\n"\r
+               "%.800s\n" "%s", cfg_dest(&cfg), error);\r
+       MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);\r
+       sfree(str);\r
+       exit(0);\r
+    }\r
+    window_name = icon_name = NULL;\r
+    if (*cfg.wintitle) {\r
+       title = cfg.wintitle;\r
+    } else {\r
+       sprintf(msg, "%s - %s", realhost, appname);\r
+       title = msg;\r
+    }\r
+    sfree(realhost);\r
+    set_title(NULL, title);\r
+    set_icon(NULL, title);\r
+\r
+    /*\r
+     * Connect the terminal to the backend for resize purposes.\r
+     */\r
+    term_provide_resize_fn(term, back->size, backhandle);\r
+\r
+    /*\r
+     * Set up a line discipline.\r
+     */\r
+    ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);\r
+\r
+    /*\r
+     * Destroy the Restart Session menu item. (This will return\r
+     * failure if it's already absent, as it will be the very first\r
+     * time we call this function. We ignore that, because as long\r
+     * as the menu item ends up not being there, we don't care\r
+     * whether it was us who removed it or not!)\r
+     */\r
+    for (i = 0; i < lenof(popup_menus); i++) {\r
+       DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);\r
+    }\r
+\r
+    must_close_session = FALSE;\r
+    session_closed = FALSE;\r
+}\r
+\r
+static void close_session(void)\r
+{\r
+    char morestuff[100];\r
+    int i;\r
+\r
+    session_closed = TRUE;\r
+    sprintf(morestuff, "%.70s (inactive)", appname);\r
+    set_icon(NULL, morestuff);\r
+    set_title(NULL, morestuff);\r
+\r
+    if (ldisc) {\r
+       ldisc_free(ldisc);\r
+       ldisc = NULL;\r
+    }\r
+    if (back) {\r
+       back->free(backhandle);\r
+       backhandle = NULL;\r
+       back = NULL;\r
+        term_provide_resize_fn(term, NULL, NULL);\r
+       update_specials_menu(NULL);\r
+    }\r
+\r
+    /*\r
+     * Show the Restart Session menu item. Do a precautionary\r
+     * delete first to ensure we never end up with more than one.\r
+     */\r
+    for (i = 0; i < lenof(popup_menus); i++) {\r
+       DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);\r
+       InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED,\r
+                  IDM_RESTART, "&Restart Session");\r
+    }\r
+\r
+    /*\r
+     * Unset the 'must_close_session' flag, or else we'll come\r
+     * straight back here the next time we go round the main message\r
+     * loop - which, worse still, will be immediately (without\r
+     * blocking) because we've just triggered a WM_SETTEXT by the\r
+     * window title change above.\r
+     */\r
+    must_close_session = FALSE;\r
+}\r
+\r
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)\r
+{\r
+    WNDCLASS wndclass;\r
+    MSG msg;\r
+    HRESULT hr;\r
+    int guess_width, guess_height;\r
+\r
+    hinst = inst;\r
+    hwnd = NULL;\r
+    flags = FLAG_VERBOSE | FLAG_INTERACTIVE;\r
+\r
+    sk_init();\r
+\r
+    InitCommonControls();\r
+\r
+    /* Ensure a Maximize setting in Explorer doesn't maximise the\r
+     * config box. */\r
+    defuse_showwindow();\r
+\r
+    if (!init_winver())\r
+    {\r
+       char *str = dupprintf("%s Fatal Error", appname);\r
+       MessageBox(NULL, "Windows refuses to report a version",\r
+                  str, MB_OK | MB_ICONEXCLAMATION);\r
+       sfree(str);\r
+       return 1;\r
+    }\r
+\r
+    /*\r
+     * If we're running a version of Windows that doesn't support\r
+     * WM_MOUSEWHEEL, find out what message number we should be\r
+     * using instead.\r
+     */\r
+    if (osVersion.dwMajorVersion < 4 ||\r
+       (osVersion.dwMajorVersion == 4 && \r
+        osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))\r
+       wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");\r
+\r
+    init_help();\r
+\r
+    init_flashwindow();\r
+\r
+    /*\r
+     * Initialize COM.\r
+     */\r
+    hr = CoInitialize(NULL);\r
+    if (hr != S_OK && hr != S_FALSE) {\r
+        char *str = dupprintf("%s Fatal Error", appname);\r
+       MessageBox(NULL, "Failed to initialize COM subsystem",\r
+                  str, MB_OK | MB_ICONEXCLAMATION);\r
+       sfree(str);\r
+       return 1;\r
+    }\r
+\r
+    /*\r
+     * Process the command line.\r
+     */\r
+    {\r
+       char *p;\r
+       int got_host = 0;\r
+       /* By default, we bring up the config dialog, rather than launching\r
+        * a session. This gets set to TRUE if something happens to change\r
+        * that (e.g., a hostname is specified on the command-line). */\r
+       int allow_launch = FALSE;\r
+\r
+       default_protocol = be_default_protocol;\r
+       /* Find the appropriate default port. */\r
+       {\r
+           Backend *b = backend_from_proto(default_protocol);\r
+           default_port = 0; /* illegal */\r
+           if (b)\r
+               default_port = b->default_port;\r
+       }\r
+       cfg.logtype = LGTYP_NONE;\r
+\r
+       do_defaults(NULL, &cfg);\r
+\r
+       p = cmdline;\r
+\r
+       /*\r
+        * Process a couple of command-line options which are more\r
+        * easily dealt with before the line is broken up into words.\r
+        * These are the old-fashioned but convenient @sessionname and\r
+        * the internal-use-only &sharedmemoryhandle, neither of which\r
+        * are combined with anything else.\r
+        */\r
+       while (*p && isspace(*p))\r
+           p++;\r
+       if (*p == '@') {\r
+            /*\r
+             * An initial @ means that the whole of the rest of the\r
+             * command line should be treated as the name of a saved\r
+             * session, with _no quoting or escaping_. This makes it a\r
+             * very convenient means of automated saved-session\r
+             * launching, via IDM_SAVEDSESS or Windows 7 jump lists.\r
+             */\r
+           int i = strlen(p);\r
+           while (i > 1 && isspace(p[i - 1]))\r
+               i--;\r
+           p[i] = '\0';\r
+           do_defaults(p + 1, &cfg);\r
+           if (!cfg_launchable(&cfg) && !do_config()) {\r
+               cleanup_exit(0);\r
+           }\r
+           allow_launch = TRUE;    /* allow it to be launched directly */\r
+       } else if (*p == '&') {\r
+           /*\r
+            * An initial & means we've been given a command line\r
+            * containing the hex value of a HANDLE for a file\r
+            * mapping object, which we must then extract as a\r
+            * config.\r
+            */\r
+           HANDLE filemap;\r
+           Config *cp;\r
+           if (sscanf(p + 1, "%p", &filemap) == 1 &&\r
+               (cp = MapViewOfFile(filemap, FILE_MAP_READ,\r
+                                   0, 0, sizeof(Config))) != NULL) {\r
+               cfg = *cp;\r
+               UnmapViewOfFile(cp);\r
+               CloseHandle(filemap);\r
+           } else if (!do_config()) {\r
+               cleanup_exit(0);\r
+           }\r
+           allow_launch = TRUE;\r
+       } else {\r
+           /*\r
+            * Otherwise, break up the command line and deal with\r
+            * it sensibly.\r
+            */\r
+           int argc, i;\r
+           char **argv;\r
+           \r
+           split_into_argv(cmdline, &argc, &argv, NULL);\r
+\r
+           for (i = 0; i < argc; i++) {\r
+               char *p = argv[i];\r
+               int ret;\r
+\r
+               ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,\r
+                                           1, &cfg);\r
+               if (ret == -2) {\r
+                   cmdline_error("option \"%s\" requires an argument", p);\r
+               } else if (ret == 2) {\r
+                   i++;               /* skip next argument */\r
+               } else if (ret == 1) {\r
+                   continue;          /* nothing further needs doing */\r
+               } else if (!strcmp(p, "-cleanup") ||\r
+                          !strcmp(p, "-cleanup-during-uninstall")) {\r
+                   /*\r
+                    * `putty -cleanup'. Remove all registry\r
+                    * entries associated with PuTTY, and also find\r
+                    * and delete the random seed file.\r
+                    */\r
+                   char *s1, *s2;\r
+                   /* Are we being invoked from an uninstaller? */\r
+                   if (!strcmp(p, "-cleanup-during-uninstall")) {\r
+                       s1 = dupprintf("Remove saved sessions and random seed file?\n"\r
+                                      "\n"\r
+                                      "If you hit Yes, ALL Registry entries associated\n"\r
+                                      "with %s will be removed, as well as the\n"\r
+                                      "random seed file. THIS PROCESS WILL\n"\r
+                                      "DESTROY YOUR SAVED SESSIONS.\n"\r
+                                      "(This only affects the currently logged-in user.)\n"\r
+                                      "\n"\r
+                                      "If you hit No, uninstallation will proceed, but\n"\r
+                                      "saved sessions etc will be left on the machine.",\r
+                                      appname);\r
+                       s2 = dupprintf("%s Uninstallation", appname);\r
+                   } else {\r
+                       s1 = dupprintf("This procedure will remove ALL Registry entries\n"\r
+                                      "associated with %s, and will also remove\n"\r
+                                      "the random seed file. (This only affects the\n"\r
+                                      "currently logged-in user.)\n"\r
+                                      "\n"\r
+                                      "THIS PROCESS WILL DESTROY YOUR SAVED SESSIONS.\n"\r
+                                      "Are you really sure you want to continue?",\r
+                                      appname);\r
+                       s2 = dupprintf("%s Warning", appname);\r
+                   }\r
+                   if (message_box(s1, s2,\r
+                                   MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2,\r
+                                   HELPCTXID(option_cleanup)) == IDYES) {\r
+                       cleanup_all();\r
+                   }\r
+                   sfree(s1);\r
+                   sfree(s2);\r
+                   exit(0);\r
+               } else if (!strcmp(p, "-pgpfp")) {\r
+                   pgp_fingerprints();\r
+                   exit(1);\r
+               } else if (*p != '-') {\r
+                   char *q = p;\r
+                   if (got_host) {\r
+                       /*\r
+                        * If we already have a host name, treat\r
+                        * this argument as a port number. NB we\r
+                        * have to treat this as a saved -P\r
+                        * argument, so that it will be deferred\r
+                        * until it's a good moment to run it.\r
+                        */\r
+                       int ret = cmdline_process_param("-P", p, 1, &cfg);\r
+                       assert(ret == 2);\r
+                   } else if (!strncmp(q, "telnet:", 7)) {\r
+                       /*\r
+                        * If the hostname starts with "telnet:",\r
+                        * set the protocol to Telnet and process\r
+                        * the string as a Telnet URL.\r
+                        */\r
+                       char c;\r
+\r
+                       q += 7;\r
+                       if (q[0] == '/' && q[1] == '/')\r
+                           q += 2;\r
+                       cfg.protocol = PROT_TELNET;\r
+                       p = q;\r
+                       while (*p && *p != ':' && *p != '/')\r
+                           p++;\r
+                       c = *p;\r
+                       if (*p)\r
+                           *p++ = '\0';\r
+                       if (c == ':')\r
+                           cfg.port = atoi(p);\r
+                       else\r
+                           cfg.port = -1;\r
+                       strncpy(cfg.host, q, sizeof(cfg.host) - 1);\r
+                       cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+                       got_host = 1;\r
+                   } else {\r
+                       /*\r
+                        * Otherwise, treat this argument as a host\r
+                        * name.\r
+                        */\r
+                       while (*p && !isspace(*p))\r
+                           p++;\r
+                       if (*p)\r
+                           *p++ = '\0';\r
+                       strncpy(cfg.host, q, sizeof(cfg.host) - 1);\r
+                       cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+                       got_host = 1;\r
+                   }\r
+               } else {\r
+                   cmdline_error("unknown option \"%s\"", p);\r
+               }\r
+           }\r
+       }\r
+\r
+       cmdline_run_saved(&cfg);\r
+\r
+       if (loaded_session || got_host)\r
+           allow_launch = TRUE;\r
+\r
+       if ((!allow_launch || !cfg_launchable(&cfg)) && !do_config()) {\r
+           cleanup_exit(0);\r
+       }\r
+\r
+       /*\r
+        * Trim leading whitespace off the hostname if it's there.\r
+        */\r
+       {\r
+           int space = strspn(cfg.host, " \t");\r
+           memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
+       }\r
+\r
+       /* See if host is of the form user@host */\r
+       if (cfg.host[0] != '\0') {\r
+           char *atsign = strrchr(cfg.host, '@');\r
+           /* Make sure we're not overflowing the user field */\r
+           if (atsign) {\r
+               if (atsign - cfg.host < sizeof cfg.username) {\r
+                   strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
+                   cfg.username[atsign - cfg.host] = '\0';\r
+               }\r
+               memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Trim a colon suffix off the hostname if it's there. In\r
+        * order to protect IPv6 address literals against this\r
+        * treatment, we do not do this if there's _more_ than one\r
+        * colon.\r
+        */\r
+       {\r
+           char *c = strchr(cfg.host, ':');\r
+\r
+           if (c) {\r
+               char *d = strchr(c+1, ':');\r
+               if (!d)\r
+                   *c = '\0';\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Remove any remaining whitespace from the hostname.\r
+        */\r
+       {\r
+           int p1 = 0, p2 = 0;\r
+           while (cfg.host[p2] != '\0') {\r
+               if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
+                   cfg.host[p1] = cfg.host[p2];\r
+                   p1++;\r
+               }\r
+               p2++;\r
+           }\r
+           cfg.host[p1] = '\0';\r
+       }\r
+    }\r
+\r
+    if (!prev) {\r
+       wndclass.style = 0;\r
+       wndclass.lpfnWndProc = WndProc;\r
+       wndclass.cbClsExtra = 0;\r
+       wndclass.cbWndExtra = 0;\r
+       wndclass.hInstance = inst;\r
+       wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));\r
+       wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);\r
+       wndclass.hbrBackground = NULL;\r
+       wndclass.lpszMenuName = NULL;\r
+       wndclass.lpszClassName = appname;\r
+\r
+       RegisterClass(&wndclass);\r
+    }\r
+\r
+    memset(&ucsdata, 0, sizeof(ucsdata));\r
+\r
+    cfgtopalette();\r
+\r
+    /*\r
+     * Guess some defaults for the window size. This all gets\r
+     * updated later, so we don't really care too much. However, we\r
+     * do want the font width/height guesses to correspond to a\r
+     * large font rather than a small one...\r
+     */\r
+\r
+    font_width = 10;\r
+    font_height = 20;\r
+    extra_width = 25;\r
+    extra_height = 28;\r
+    guess_width = extra_width + font_width * cfg.width;\r
+    guess_height = extra_height + font_height * cfg.height;\r
+    {\r
+       RECT r;\r
+       get_fullscreen_rect(&r);\r
+       if (guess_width > r.right - r.left)\r
+           guess_width = r.right - r.left;\r
+       if (guess_height > r.bottom - r.top)\r
+           guess_height = r.bottom - r.top;\r
+    }\r
+\r
+    {\r
+       int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;\r
+       int exwinmode = 0;\r
+       if (!cfg.scrollbar)\r
+           winmode &= ~(WS_VSCROLL);\r
+       if (cfg.resize_action == RESIZE_DISABLED)\r
+           winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);\r
+       if (cfg.alwaysontop)\r
+           exwinmode |= WS_EX_TOPMOST;\r
+       if (cfg.sunken_edge)\r
+           exwinmode |= WS_EX_CLIENTEDGE;\r
+       hwnd = CreateWindowEx(exwinmode, appname, appname,\r
+                             winmode, CW_USEDEFAULT, CW_USEDEFAULT,\r
+                             guess_width, guess_height,\r
+                             NULL, NULL, inst, NULL);\r
+    }\r
+\r
+    /*\r
+     * Initialise the terminal. (We have to do this _after_\r
+     * creating the window, since the terminal is the first thing\r
+     * which will call schedule_timer(), which will in turn call\r
+     * timer_change_notify() which will expect hwnd to exist.)\r
+     */\r
+    term = term_init(&cfg, &ucsdata, NULL);\r
+    logctx = log_init(NULL, &cfg);\r
+    term_provide_logctx(term, logctx);\r
+    term_size(term, cfg.height, cfg.width, cfg.savelines);\r
+\r
+    /*\r
+     * Initialise the fonts, simultaneously correcting the guesses\r
+     * for font_{width,height}.\r
+     */\r
+    init_fonts(0,0);\r
+\r
+    /*\r
+     * Correct the guesses for extra_{width,height}.\r
+     */\r
+    {\r
+       RECT cr, wr;\r
+       GetWindowRect(hwnd, &wr);\r
+       GetClientRect(hwnd, &cr);\r
+       offset_width = offset_height = cfg.window_border;\r
+       extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;\r
+       extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;\r
+    }\r
+\r
+    /*\r
+     * Resize the window, now we know what size we _really_ want it\r
+     * to be.\r
+     */\r
+    guess_width = extra_width + font_width * term->cols;\r
+    guess_height = extra_height + font_height * term->rows;\r
+    SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,\r
+                SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);\r
+\r
+    /*\r
+     * Set up a caret bitmap, with no content.\r
+     */\r
+    {\r
+       char *bits;\r
+       int size = (font_width + 15) / 16 * 2 * font_height;\r
+       bits = snewn(size, char);\r
+       memset(bits, 0, size);\r
+       caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);\r
+       sfree(bits);\r
+    }\r
+    CreateCaret(hwnd, caretbm, font_width, font_height);\r
+\r
+    /*\r
+     * Initialise the scroll bar.\r
+     */\r
+    {\r
+       SCROLLINFO si;\r
+\r
+       si.cbSize = sizeof(si);\r
+       si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;\r
+       si.nMin = 0;\r
+       si.nMax = term->rows - 1;\r
+       si.nPage = term->rows;\r
+       si.nPos = 0;\r
+       SetScrollInfo(hwnd, SB_VERT, &si, FALSE);\r
+    }\r
+\r
+    /*\r
+     * Prepare the mouse handler.\r
+     */\r
+    lastact = MA_NOTHING;\r
+    lastbtn = MBT_NOTHING;\r
+    dbltime = GetDoubleClickTime();\r
+\r
+    /*\r
+     * Set up the session-control options on the system menu.\r
+     */\r
+    {\r
+       HMENU m;\r
+       int j;\r
+       char *str;\r
+\r
+       popup_menus[SYSMENU].menu = GetSystemMenu(hwnd, FALSE);\r
+       popup_menus[CTXMENU].menu = CreatePopupMenu();\r
+       AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_PASTE, "&Paste");\r
+\r
+       savedsess_menu = CreateMenu();\r
+       get_sesslist(&sesslist, TRUE);\r
+       update_savedsess_menu();\r
+\r
+       for (j = 0; j < lenof(popup_menus); j++) {\r
+           m = popup_menus[j].menu;\r
+\r
+           AppendMenu(m, MF_SEPARATOR, 0, 0);\r
+           AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");\r
+           AppendMenu(m, MF_SEPARATOR, 0, 0);\r
+           AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");\r
+           AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");\r
+           AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) savedsess_menu,\r
+                      "Sa&ved Sessions");\r
+           AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");\r
+           AppendMenu(m, MF_SEPARATOR, 0, 0);\r
+           AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");\r
+           AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");\r
+           AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");\r
+           AppendMenu(m, MF_SEPARATOR, 0, 0);\r
+           AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?\r
+                      MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");\r
+           AppendMenu(m, MF_SEPARATOR, 0, 0);\r
+           if (has_help())\r
+               AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");\r
+           str = dupprintf("&About %s", appname);\r
+           AppendMenu(m, MF_ENABLED, IDM_ABOUT, str);\r
+           sfree(str);\r
+       }\r
+    }\r
+\r
+    start_backend();\r
+\r
+    /*\r
+     * Set up the initial input locale.\r
+     */\r
+    set_input_locale(GetKeyboardLayout(0));\r
+\r
+    /*\r
+     * Finally show the window!\r
+     */\r
+    ShowWindow(hwnd, show);\r
+    SetForegroundWindow(hwnd);\r
+\r
+    /*\r
+     * Set the palette up.\r
+     */\r
+    pal = NULL;\r
+    logpal = NULL;\r
+    init_palette();\r
+\r
+    term_set_focus(term, GetForegroundWindow() == hwnd);\r
+    UpdateWindow(hwnd);\r
+\r
+    while (1) {\r
+       HANDLE *handles;\r
+       int nhandles, n;\r
+\r
+       handles = handle_get_events(&nhandles);\r
+\r
+       n = MsgWaitForMultipleObjects(nhandles, handles, FALSE, INFINITE,\r
+                                     QS_ALLINPUT);\r
+\r
+       if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {\r
+           handle_got_event(handles[n - WAIT_OBJECT_0]);\r
+           sfree(handles);\r
+           if (must_close_session)\r
+               close_session();\r
+       } else\r
+           sfree(handles);\r
+\r
+       while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {\r
+           if (msg.message == WM_QUIT)\r
+               goto finished;         /* two-level break */\r
+\r
+           if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))\r
+               DispatchMessage(&msg);\r
+           /* Send the paste buffer if there's anything to send */\r
+           term_paste(term);\r
+           /* If there's nothing new in the queue then we can do everything\r
+            * we've delayed, reading the socket, writing, and repainting\r
+            * the window.\r
+            */\r
+           if (must_close_session)\r
+               close_session();\r
+       }\r
+\r
+       /* The messages seem unreliable; especially if we're being tricky */\r
+       term_set_focus(term, GetForegroundWindow() == hwnd);\r
+\r
+       if (pending_netevent)\r
+           enact_pending_netevent();\r
+\r
+       net_pending_errors();\r
+    }\r
+\r
+    finished:\r
+    cleanup_exit(msg.wParam);         /* this doesn't return... */\r
+    return msg.wParam;                /* ... but optimiser doesn't know */\r
+}\r
+\r
+/*\r
+ * Clean up and exit.\r
+ */\r
+void cleanup_exit(int code)\r
+{\r
+    /*\r
+     * Clean up.\r
+     */\r
+    deinit_fonts();\r
+    sfree(logpal);\r
+    if (pal)\r
+       DeleteObject(pal);\r
+    sk_cleanup();\r
+\r
+    if (cfg.protocol == PROT_SSH) {\r
+       random_save_seed();\r
+#ifdef MSCRYPTOAPI\r
+       crypto_wrapup();\r
+#endif\r
+    }\r
+    shutdown_help();\r
+\r
+    /* Clean up COM. */\r
+    CoUninitialize();\r
+\r
+    exit(code);\r
+}\r
+\r
+/*\r
+ * Set up, or shut down, an AsyncSelect. Called from winnet.c.\r
+ */\r
+char *do_select(SOCKET skt, int startup)\r
+{\r
+    int msg, events;\r
+    if (startup) {\r
+       msg = WM_NETEVENT;\r
+       events = (FD_CONNECT | FD_READ | FD_WRITE |\r
+                 FD_OOB | FD_CLOSE | FD_ACCEPT);\r
+    } else {\r
+       msg = events = 0;\r
+    }\r
+    if (!hwnd)\r
+       return "do_select(): internal error (hwnd==NULL)";\r
+    if (p_WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {\r
+       switch (p_WSAGetLastError()) {\r
+         case WSAENETDOWN:\r
+           return "Network is down";\r
+         default:\r
+           return "WSAAsyncSelect(): unknown error";\r
+       }\r
+    }\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Refresh the saved-session submenu from `sesslist'.\r
+ */\r
+static void update_savedsess_menu(void)\r
+{\r
+    int i;\r
+    while (DeleteMenu(savedsess_menu, 0, MF_BYPOSITION)) ;\r
+    /* skip sesslist.sessions[0] == Default Settings */\r
+    for (i = 1;\r
+        i < ((sesslist.nsessions <= MENU_SAVED_MAX+1) ? sesslist.nsessions\r
+                                                      : MENU_SAVED_MAX+1);\r
+        i++)\r
+       AppendMenu(savedsess_menu, MF_ENABLED,\r
+                  IDM_SAVED_MIN + (i-1)*MENU_SAVED_STEP,\r
+                  sesslist.sessions[i]);\r
+    if (sesslist.nsessions <= 1)\r
+       AppendMenu(savedsess_menu, MF_GRAYED, IDM_SAVED_MIN, "(No sessions)");\r
+}\r
+\r
+/*\r
+ * Update the Special Commands submenu.\r
+ */\r
+void update_specials_menu(void *frontend)\r
+{\r
+    HMENU new_menu;\r
+    int i, j;\r
+\r
+    if (back)\r
+       specials = back->get_specials(backhandle);\r
+    else\r
+       specials = NULL;\r
+\r
+    if (specials) {\r
+       /* We can't use Windows to provide a stack for submenus, so\r
+        * here's a lame "stack" that will do for now. */\r
+       HMENU saved_menu = NULL;\r
+       int nesting = 1;\r
+       new_menu = CreatePopupMenu();\r
+       for (i = 0; nesting > 0; i++) {\r
+           assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);\r
+           switch (specials[i].code) {\r
+             case TS_SEP:\r
+               AppendMenu(new_menu, MF_SEPARATOR, 0, 0);\r
+               break;\r
+             case TS_SUBMENU:\r
+               assert(nesting < 2);\r
+               nesting++;\r
+               saved_menu = new_menu; /* XXX lame stacking */\r
+               new_menu = CreatePopupMenu();\r
+               AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,\r
+                          (UINT) new_menu, specials[i].name);\r
+               break;\r
+             case TS_EXITMENU:\r
+               nesting--;\r
+               if (nesting) {\r
+                   new_menu = saved_menu; /* XXX lame stacking */\r
+                   saved_menu = NULL;\r
+               }\r
+               break;\r
+             default:\r
+               AppendMenu(new_menu, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,\r
+                          specials[i].name);\r
+               break;\r
+           }\r
+       }\r
+       /* Squirrel the highest special. */\r
+       n_specials = i - 1;\r
+    } else {\r
+       new_menu = NULL;\r
+       n_specials = 0;\r
+    }\r
+\r
+    for (j = 0; j < lenof(popup_menus); j++) {\r
+       if (specials_menu) {\r
+           /* XXX does this free up all submenus? */\r
+           DeleteMenu(popup_menus[j].menu, (UINT)specials_menu, MF_BYCOMMAND);\r
+           DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND);\r
+       }\r
+       if (new_menu) {\r
+           InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,\r
+                      MF_BYCOMMAND | MF_POPUP | MF_ENABLED,\r
+                      (UINT) new_menu, "S&pecial Command");\r
+           InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,\r
+                      MF_BYCOMMAND | MF_SEPARATOR, IDM_SPECIALSEP, 0);\r
+       }\r
+    }\r
+    specials_menu = new_menu;\r
+}\r
+\r
+static void update_mouse_pointer(void)\r
+{\r
+    LPTSTR curstype;\r
+    int force_visible = FALSE;\r
+    static int forced_visible = FALSE;\r
+    switch (busy_status) {\r
+      case BUSY_NOT:\r
+       if (send_raw_mouse)\r
+           curstype = IDC_ARROW;\r
+       else\r
+           curstype = IDC_IBEAM;\r
+       break;\r
+      case BUSY_WAITING:\r
+       curstype = IDC_APPSTARTING; /* this may be an abuse */\r
+       force_visible = TRUE;\r
+       break;\r
+      case BUSY_CPU:\r
+       curstype = IDC_WAIT;\r
+       force_visible = TRUE;\r
+       break;\r
+      default:\r
+       assert(0);\r
+    }\r
+    {\r
+       HCURSOR cursor = LoadCursor(NULL, curstype);\r
+       SetClassLongPtr(hwnd, GCLP_HCURSOR, (LONG_PTR)cursor);\r
+       SetCursor(cursor); /* force redraw of cursor at current posn */\r
+    }\r
+    if (force_visible != forced_visible) {\r
+       /* We want some cursor shapes to be visible always.\r
+        * Along with show_mouseptr(), this manages the ShowCursor()\r
+        * counter such that if we switch back to a non-force_visible\r
+        * cursor, the previous visibility state is restored. */\r
+       ShowCursor(force_visible);\r
+       forced_visible = force_visible;\r
+    }\r
+}\r
+\r
+void set_busy_status(void *frontend, int status)\r
+{\r
+    busy_status = status;\r
+    update_mouse_pointer();\r
+}\r
+\r
+/*\r
+ * set or clear the "raw mouse message" mode\r
+ */\r
+void set_raw_mouse_mode(void *frontend, int activate)\r
+{\r
+    activate = activate && !cfg.no_mouse_rep;\r
+    send_raw_mouse = activate;\r
+    update_mouse_pointer();\r
+}\r
+\r
+/*\r
+ * Print a message box and close the connection.\r
+ */\r
+void connection_fatal(void *frontend, char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *stuff, morestuff[100];\r
+\r
+    va_start(ap, fmt);\r
+    stuff = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    sprintf(morestuff, "%.70s Fatal Error", appname);\r
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);\r
+    sfree(stuff);\r
+\r
+    if (cfg.close_on_exit == FORCE_ON)\r
+       PostQuitMessage(1);\r
+    else {\r
+       must_close_session = TRUE;\r
+    }\r
+}\r
+\r
+/*\r
+ * Report an error at the command-line parsing stage.\r
+ */\r
+void cmdline_error(char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *stuff, morestuff[100];\r
+\r
+    va_start(ap, fmt);\r
+    stuff = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    sprintf(morestuff, "%.70s Command Line Error", appname);\r
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);\r
+    sfree(stuff);\r
+    exit(1);\r
+}\r
+\r
+/*\r
+ * Actually do the job requested by a WM_NETEVENT\r
+ */\r
+static void enact_pending_netevent(void)\r
+{\r
+    static int reentering = 0;\r
+    extern int select_result(WPARAM, LPARAM);\r
+\r
+    if (reentering)\r
+       return;                        /* don't unpend the pending */\r
+\r
+    pending_netevent = FALSE;\r
+\r
+    reentering = 1;\r
+    select_result(pend_netevent_wParam, pend_netevent_lParam);\r
+    reentering = 0;\r
+}\r
+\r
+/*\r
+ * Copy the colour palette from the configuration data into defpal.\r
+ * This is non-trivial because the colour indices are different.\r
+ */\r
+static void cfgtopalette(void)\r
+{\r
+    int i;\r
+    static const int ww[] = {\r
+       256, 257, 258, 259, 260, 261,\r
+       0, 8, 1, 9, 2, 10, 3, 11,\r
+       4, 12, 5, 13, 6, 14, 7, 15\r
+    };\r
+\r
+    for (i = 0; i < 22; i++) {\r
+       int w = ww[i];\r
+       defpal[w].rgbtRed = cfg.colours[i][0];\r
+       defpal[w].rgbtGreen = cfg.colours[i][1];\r
+       defpal[w].rgbtBlue = cfg.colours[i][2];\r
+    }\r
+    for (i = 0; i < NEXTCOLOURS; i++) {\r
+       if (i < 216) {\r
+           int r = i / 36, g = (i / 6) % 6, b = i % 6;\r
+           defpal[i+16].rgbtRed = r ? r * 40 + 55 : 0;\r
+           defpal[i+16].rgbtGreen = g ? g * 40 + 55 : 0;\r
+           defpal[i+16].rgbtBlue = b ? b * 40 + 55 : 0;\r
+       } else {\r
+           int shade = i - 216;\r
+           shade = shade * 10 + 8;\r
+           defpal[i+16].rgbtRed = defpal[i+16].rgbtGreen =\r
+               defpal[i+16].rgbtBlue = shade;\r
+       }\r
+    }\r
+\r
+    /* Override with system colours if appropriate */\r
+    if (cfg.system_colour)\r
+        systopalette();\r
+}\r
+\r
+/*\r
+ * Override bit of defpal with colours from the system.\r
+ * (NB that this takes a copy the system colours at the time this is called,\r
+ * so subsequent colour scheme changes don't take effect. To fix that we'd\r
+ * probably want to be using GetSysColorBrush() and the like.)\r
+ */\r
+static void systopalette(void)\r
+{\r
+    int i;\r
+    static const struct { int nIndex; int norm; int bold; } or[] =\r
+    {\r
+       { COLOR_WINDOWTEXT,     256, 257 }, /* Default Foreground */\r
+       { COLOR_WINDOW,         258, 259 }, /* Default Background */\r
+       { COLOR_HIGHLIGHTTEXT,  260, 260 }, /* Cursor Text */\r
+       { COLOR_HIGHLIGHT,      261, 261 }, /* Cursor Colour */\r
+    };\r
+\r
+    for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) {\r
+       COLORREF colour = GetSysColor(or[i].nIndex);\r
+       defpal[or[i].norm].rgbtRed =\r
+          defpal[or[i].bold].rgbtRed = GetRValue(colour);\r
+       defpal[or[i].norm].rgbtGreen =\r
+          defpal[or[i].bold].rgbtGreen = GetGValue(colour);\r
+       defpal[or[i].norm].rgbtBlue =\r
+          defpal[or[i].bold].rgbtBlue = GetBValue(colour);\r
+    }\r
+}\r
+\r
+/*\r
+ * Set up the colour palette.\r
+ */\r
+static void init_palette(void)\r
+{\r
+    int i;\r
+    HDC hdc = GetDC(hwnd);\r
+    if (hdc) {\r
+       if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {\r
+           /*\r
+            * This is a genuine case where we must use smalloc\r
+            * because the snew macros can't cope.\r
+            */\r
+           logpal = smalloc(sizeof(*logpal)\r
+                            - sizeof(logpal->palPalEntry)\r
+                            + NALLCOLOURS * sizeof(PALETTEENTRY));\r
+           logpal->palVersion = 0x300;\r
+           logpal->palNumEntries = NALLCOLOURS;\r
+           for (i = 0; i < NALLCOLOURS; i++) {\r
+               logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;\r
+               logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;\r
+               logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;\r
+               logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;\r
+           }\r
+           pal = CreatePalette(logpal);\r
+           if (pal) {\r
+               SelectPalette(hdc, pal, FALSE);\r
+               RealizePalette(hdc);\r
+               SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);\r
+           }\r
+       }\r
+       ReleaseDC(hwnd, hdc);\r
+    }\r
+    if (pal)\r
+       for (i = 0; i < NALLCOLOURS; i++)\r
+           colours[i] = PALETTERGB(defpal[i].rgbtRed,\r
+                                   defpal[i].rgbtGreen,\r
+                                   defpal[i].rgbtBlue);\r
+    else\r
+       for (i = 0; i < NALLCOLOURS; i++)\r
+           colours[i] = RGB(defpal[i].rgbtRed,\r
+                            defpal[i].rgbtGreen, defpal[i].rgbtBlue);\r
+}\r
+\r
+/*\r
+ * This is a wrapper to ExtTextOut() to force Windows to display\r
+ * the precise glyphs we give it. Otherwise it would do its own\r
+ * bidi and Arabic shaping, and we would end up uncertain which\r
+ * characters it had put where.\r
+ */\r
+static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,\r
+                         unsigned short *lpString, UINT cbCount,\r
+                         CONST INT *lpDx, int opaque)\r
+{\r
+#ifdef __LCC__\r
+    /*\r
+     * The LCC include files apparently don't supply the\r
+     * GCP_RESULTSW type, but we can make do with GCP_RESULTS\r
+     * proper: the differences aren't important to us (the only\r
+     * variable-width string parameter is one we don't use anyway).\r
+     */\r
+    GCP_RESULTS gcpr;\r
+#else\r
+    GCP_RESULTSW gcpr;\r
+#endif\r
+    char *buffer = snewn(cbCount*2+2, char);\r
+    char *classbuffer = snewn(cbCount, char);\r
+    memset(&gcpr, 0, sizeof(gcpr));\r
+    memset(buffer, 0, cbCount*2+2);\r
+    memset(classbuffer, GCPCLASS_NEUTRAL, cbCount);\r
+\r
+    gcpr.lStructSize = sizeof(gcpr);\r
+    gcpr.lpGlyphs = (void *)buffer;\r
+    gcpr.lpClass = (void *)classbuffer;\r
+    gcpr.nGlyphs = cbCount;\r
+    GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr,\r
+                          FLI_MASK | GCP_CLASSIN | GCP_DIACRITIC);\r
+\r
+    ExtTextOut(hdc, x, y,\r
+              ETO_GLYPH_INDEX | ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),\r
+              lprc, buffer, cbCount, lpDx);\r
+}\r
+\r
+/*\r
+ * The exact_textout() wrapper, unfortunately, destroys the useful\r
+ * Windows `font linking' behaviour: automatic handling of Unicode\r
+ * code points not supported in this font by falling back to a font\r
+ * which does contain them. Therefore, we adopt a multi-layered\r
+ * approach: for any potentially-bidi text, we use exact_textout(),\r
+ * and for everything else we use a simple ExtTextOut as we did\r
+ * before exact_textout() was introduced.\r
+ */\r
+static void general_textout(HDC hdc, int x, int y, CONST RECT *lprc,\r
+                           unsigned short *lpString, UINT cbCount,\r
+                           CONST INT *lpDx, int opaque)\r
+{\r
+    int i, j, xp, xn;\r
+    int bkmode = 0, got_bkmode = FALSE;\r
+\r
+    xp = xn = x;\r
+\r
+    for (i = 0; i < (int)cbCount ;) {\r
+       int rtl = is_rtl(lpString[i]);\r
+\r
+       xn += lpDx[i];\r
+\r
+       for (j = i+1; j < (int)cbCount; j++) {\r
+           if (rtl != is_rtl(lpString[j]))\r
+               break;\r
+           xn += lpDx[j];\r
+       }\r
+\r
+       /*\r
+        * Now [i,j) indicates a maximal substring of lpString\r
+        * which should be displayed using the same textout\r
+        * function.\r
+        */\r
+       if (rtl) {\r
+           exact_textout(hdc, xp, y, lprc, lpString+i, j-i,\r
+                          font_varpitch ? NULL : lpDx+i, opaque);\r
+       } else {\r
+           ExtTextOutW(hdc, xp, y, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),\r
+                       lprc, lpString+i, j-i,\r
+                        font_varpitch ? NULL : lpDx+i);\r
+       }\r
+\r
+       i = j;\r
+       xp = xn;\r
+\r
+        bkmode = GetBkMode(hdc);\r
+        got_bkmode = TRUE;\r
+        SetBkMode(hdc, TRANSPARENT);\r
+        opaque = FALSE;\r
+    }\r
+\r
+    if (got_bkmode)\r
+        SetBkMode(hdc, bkmode);\r
+}\r
+\r
+static int get_font_width(HDC hdc, const TEXTMETRIC *tm)\r
+{\r
+    int ret;\r
+    /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */\r
+    if (!(tm->tmPitchAndFamily & TMPF_FIXED_PITCH)) {\r
+        ret = tm->tmAveCharWidth;\r
+    } else {\r
+#define FIRST '0'\r
+#define LAST '9'\r
+        ABCFLOAT widths[LAST-FIRST + 1];\r
+        int j;\r
+\r
+        font_varpitch = TRUE;\r
+        font_dualwidth = TRUE;\r
+        if (GetCharABCWidthsFloat(hdc, FIRST, LAST, widths)) {\r
+            ret = 0;\r
+            for (j = 0; j < lenof(widths); j++) {\r
+                int width = (int)(0.5 + widths[j].abcfA +\r
+                                  widths[j].abcfB + widths[j].abcfC);\r
+                if (ret < width)\r
+                    ret = width;\r
+            }\r
+        } else {\r
+            ret = tm->tmMaxCharWidth;\r
+        }\r
+#undef FIRST\r
+#undef LAST\r
+    }\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Initialise all the fonts we will need initially. There may be as many as\r
+ * three or as few as one.  The other (potentially) twenty-one fonts are done\r
+ * if/when they are needed.\r
+ *\r
+ * We also:\r
+ *\r
+ * - check the font width and height, correcting our guesses if\r
+ *   necessary.\r
+ *\r
+ * - verify that the bold font is the same width as the ordinary\r
+ *   one, and engage shadow bolding if not.\r
+ * \r
+ * - verify that the underlined font is the same width as the\r
+ *   ordinary one (manual underlining by means of line drawing can\r
+ *   be done in a pinch).\r
+ */\r
+static void init_fonts(int pick_width, int pick_height)\r
+{\r
+    TEXTMETRIC tm;\r
+    CPINFO cpinfo;\r
+    int fontsize[3];\r
+    int i;\r
+    HDC hdc;\r
+    int fw_dontcare, fw_bold;\r
+\r
+    for (i = 0; i < FONT_MAXNO; i++)\r
+       fonts[i] = NULL;\r
+\r
+    bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;\r
+    und_mode = UND_FONT;\r
+\r
+    if (cfg.font.isbold) {\r
+       fw_dontcare = FW_BOLD;\r
+       fw_bold = FW_HEAVY;\r
+    } else {\r
+       fw_dontcare = FW_DONTCARE;\r
+       fw_bold = FW_BOLD;\r
+    }\r
+\r
+    hdc = GetDC(hwnd);\r
+\r
+    if (pick_height)\r
+       font_height = pick_height;\r
+    else {\r
+       font_height = cfg.font.height;\r
+       if (font_height > 0) {\r
+           font_height =\r
+               -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);\r
+       }\r
+    }\r
+    font_width = pick_width;\r
+\r
+#define f(i,c,w,u) \\r
+    fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \\r
+                          c, OUT_DEFAULT_PRECIS, \\r
+                          CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality), \\r
+                          FIXED_PITCH | FF_DONTCARE, cfg.font.name)\r
+\r
+    f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);\r
+\r
+    SelectObject(hdc, fonts[FONT_NORMAL]);\r
+    GetTextMetrics(hdc, &tm);\r
+\r
+    GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont);\r
+\r
+    /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */\r
+    if (!(tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) {\r
+        font_varpitch = FALSE;\r
+        font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);\r
+    } else {\r
+        font_varpitch = TRUE;\r
+        font_dualwidth = TRUE;\r
+    }\r
+    if (pick_width == 0 || pick_height == 0) {\r
+       font_height = tm.tmHeight;\r
+        font_width = get_font_width(hdc, &tm);\r
+    }\r
+\r
+#ifdef RDB_DEBUG_PATCH\r
+    debug(23, "Primary font H=%d, AW=%d, MW=%d",\r
+           tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);\r
+#endif\r
+\r
+    {\r
+       CHARSETINFO info;\r
+       DWORD cset = tm.tmCharSet;\r
+       memset(&info, 0xFF, sizeof(info));\r
+\r
+       /* !!! Yes the next line is right */\r
+       if (cset == OEM_CHARSET)\r
+           ucsdata.font_codepage = GetOEMCP();\r
+       else\r
+           if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET))\r
+               ucsdata.font_codepage = info.ciACP;\r
+       else\r
+           ucsdata.font_codepage = -1;\r
+\r
+       GetCPInfo(ucsdata.font_codepage, &cpinfo);\r
+       ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);\r
+    }\r
+\r
+    f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE);\r
+\r
+    /*\r
+     * Some fonts, e.g. 9-pt Courier, draw their underlines\r
+     * outside their character cell. We successfully prevent\r
+     * screen corruption by clipping the text output, but then\r
+     * we lose the underline completely. Here we try to work\r
+     * out whether this is such a font, and if it is, we set a\r
+     * flag that causes underlines to be drawn by hand.\r
+     *\r
+     * Having tried other more sophisticated approaches (such\r
+     * as examining the TEXTMETRIC structure or requesting the\r
+     * height of a string), I think we'll do this the brute\r
+     * force way: we create a small bitmap, draw an underlined\r
+     * space on it, and test to see whether any pixels are\r
+     * foreground-coloured. (Since we expect the underline to\r
+     * go all the way across the character cell, we only search\r
+     * down a single column of the bitmap, half way across.)\r
+     */\r
+    {\r
+       HDC und_dc;\r
+       HBITMAP und_bm, und_oldbm;\r
+       int i, gotit;\r
+       COLORREF c;\r
+\r
+       und_dc = CreateCompatibleDC(hdc);\r
+       und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);\r
+       und_oldbm = SelectObject(und_dc, und_bm);\r
+       SelectObject(und_dc, fonts[FONT_UNDERLINE]);\r
+       SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);\r
+       SetTextColor(und_dc, RGB(255, 255, 255));\r
+       SetBkColor(und_dc, RGB(0, 0, 0));\r
+       SetBkMode(und_dc, OPAQUE);\r
+       ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);\r
+       gotit = FALSE;\r
+       for (i = 0; i < font_height; i++) {\r
+           c = GetPixel(und_dc, font_width / 2, i);\r
+           if (c != RGB(0, 0, 0))\r
+               gotit = TRUE;\r
+       }\r
+       SelectObject(und_dc, und_oldbm);\r
+       DeleteObject(und_bm);\r
+       DeleteDC(und_dc);\r
+       if (!gotit) {\r
+           und_mode = UND_LINE;\r
+           DeleteObject(fonts[FONT_UNDERLINE]);\r
+           fonts[FONT_UNDERLINE] = 0;\r
+       }\r
+    }\r
+\r
+    if (bold_mode == BOLD_FONT) {\r
+       f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE);\r
+    }\r
+#undef f\r
+\r
+    descent = tm.tmAscent + 1;\r
+    if (descent >= font_height)\r
+       descent = font_height - 1;\r
+\r
+    for (i = 0; i < 3; i++) {\r
+       if (fonts[i]) {\r
+           if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))\r
+               fontsize[i] = get_font_width(hdc, &tm) + 256 * tm.tmHeight;\r
+           else\r
+               fontsize[i] = -i;\r
+       } else\r
+           fontsize[i] = -i;\r
+    }\r
+\r
+    ReleaseDC(hwnd, hdc);\r
+\r
+    if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {\r
+       und_mode = UND_LINE;\r
+       DeleteObject(fonts[FONT_UNDERLINE]);\r
+       fonts[FONT_UNDERLINE] = 0;\r
+    }\r
+\r
+    if (bold_mode == BOLD_FONT &&\r
+       fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {\r
+       bold_mode = BOLD_SHADOW;\r
+       DeleteObject(fonts[FONT_BOLD]);\r
+       fonts[FONT_BOLD] = 0;\r
+    }\r
+    fontflag[0] = fontflag[1] = fontflag[2] = 1;\r
+\r
+    init_ucs(&cfg, &ucsdata);\r
+}\r
+\r
+static void another_font(int fontno)\r
+{\r
+    int basefont;\r
+    int fw_dontcare, fw_bold;\r
+    int c, u, w, x;\r
+    char *s;\r
+\r
+    if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])\r
+       return;\r
+\r
+    basefont = (fontno & ~(FONT_BOLDUND));\r
+    if (basefont != fontno && !fontflag[basefont])\r
+       another_font(basefont);\r
+\r
+    if (cfg.font.isbold) {\r
+       fw_dontcare = FW_BOLD;\r
+       fw_bold = FW_HEAVY;\r
+    } else {\r
+       fw_dontcare = FW_DONTCARE;\r
+       fw_bold = FW_BOLD;\r
+    }\r
+\r
+    c = cfg.font.charset;\r
+    w = fw_dontcare;\r
+    u = FALSE;\r
+    s = cfg.font.name;\r
+    x = font_width;\r
+\r
+    if (fontno & FONT_WIDE)\r
+       x *= 2;\r
+    if (fontno & FONT_NARROW)\r
+       x = (x+1)/2;\r
+    if (fontno & FONT_OEM)\r
+       c = OEM_CHARSET;\r
+    if (fontno & FONT_BOLD)\r
+       w = fw_bold;\r
+    if (fontno & FONT_UNDERLINE)\r
+       u = TRUE;\r
+\r
+    fonts[fontno] =\r
+       CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,\r
+                  FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,\r
+                  CLIP_DEFAULT_PRECIS, FONT_QUALITY(cfg.font_quality),\r
+                  DEFAULT_PITCH | FF_DONTCARE, s);\r
+\r
+    fontflag[fontno] = 1;\r
+}\r
+\r
+static void deinit_fonts(void)\r
+{\r
+    int i;\r
+    for (i = 0; i < FONT_MAXNO; i++) {\r
+       if (fonts[i])\r
+           DeleteObject(fonts[i]);\r
+       fonts[i] = 0;\r
+       fontflag[i] = 0;\r
+    }\r
+}\r
+\r
+void request_resize(void *frontend, int w, int h)\r
+{\r
+    int width, height;\r
+\r
+    /* If the window is maximized supress resizing attempts */\r
+    if (IsZoomed(hwnd)) {\r
+       if (cfg.resize_action == RESIZE_TERM)\r
+           return;\r
+    }\r
+\r
+    if (cfg.resize_action == RESIZE_DISABLED) return;\r
+    if (h == term->rows && w == term->cols) return;\r
+\r
+    /* Sanity checks ... */\r
+    {\r
+       static int first_time = 1;\r
+       static RECT ss;\r
+\r
+       switch (first_time) {\r
+         case 1:\r
+           /* Get the size of the screen */\r
+           if (get_fullscreen_rect(&ss))\r
+               /* first_time = 0 */ ;\r
+           else {\r
+               first_time = 2;\r
+               break;\r
+           }\r
+         case 0:\r
+           /* Make sure the values are sane */\r
+           width = (ss.right - ss.left - extra_width) / 4;\r
+           height = (ss.bottom - ss.top - extra_height) / 6;\r
+\r
+           if (w > width || h > height)\r
+               return;\r
+           if (w < 15)\r
+               w = 15;\r
+           if (h < 1)\r
+               h = 1;\r
+       }\r
+    }\r
+\r
+    term_size(term, h, w, cfg.savelines);\r
+\r
+    if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {\r
+       width = extra_width + font_width * w;\r
+       height = extra_height + font_height * h;\r
+\r
+       SetWindowPos(hwnd, NULL, 0, 0, width, height,\r
+           SWP_NOACTIVATE | SWP_NOCOPYBITS |\r
+           SWP_NOMOVE | SWP_NOZORDER);\r
+    } else\r
+       reset_window(0);\r
+\r
+    InvalidateRect(hwnd, NULL, TRUE);\r
+}\r
+\r
+static void reset_window(int reinit) {\r
+    /*\r
+     * This function decides how to resize or redraw when the \r
+     * user changes something. \r
+     *\r
+     * This function doesn't like to change the terminal size but if the\r
+     * font size is locked that may be it's only soluion.\r
+     */\r
+    int win_width, win_height;\r
+    RECT cr, wr;\r
+\r
+#ifdef RDB_DEBUG_PATCH\r
+    debug((27, "reset_window()"));\r
+#endif\r
+\r
+    /* Current window sizes ... */\r
+    GetWindowRect(hwnd, &wr);\r
+    GetClientRect(hwnd, &cr);\r
+\r
+    win_width  = cr.right - cr.left;\r
+    win_height = cr.bottom - cr.top;\r
+\r
+    if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;\r
+\r
+    /* Are we being forced to reload the fonts ? */\r
+    if (reinit>1) {\r
+#ifdef RDB_DEBUG_PATCH\r
+       debug((27, "reset_window() -- Forced deinit"));\r
+#endif\r
+       deinit_fonts();\r
+       init_fonts(0,0);\r
+    }\r
+\r
+    /* Oh, looks like we're minimised */\r
+    if (win_width == 0 || win_height == 0)\r
+       return;\r
+\r
+    /* Is the window out of position ? */\r
+    if ( !reinit && \r
+           (offset_width != (win_width-font_width*term->cols)/2 ||\r
+            offset_height != (win_height-font_height*term->rows)/2) ){\r
+       offset_width = (win_width-font_width*term->cols)/2;\r
+       offset_height = (win_height-font_height*term->rows)/2;\r
+       InvalidateRect(hwnd, NULL, TRUE);\r
+#ifdef RDB_DEBUG_PATCH\r
+       debug((27, "reset_window() -> Reposition terminal"));\r
+#endif\r
+    }\r
+\r
+    if (IsZoomed(hwnd)) {\r
+       /* We're fullscreen, this means we must not change the size of\r
+        * the window so it's the font size or the terminal itself.\r
+        */\r
+\r
+       extra_width = wr.right - wr.left - cr.right + cr.left;\r
+       extra_height = wr.bottom - wr.top - cr.bottom + cr.top;\r
+\r
+       if (cfg.resize_action != RESIZE_TERM) {\r
+           if (  font_width != win_width/term->cols || \r
+                 font_height != win_height/term->rows) {\r
+               deinit_fonts();\r
+               init_fonts(win_width/term->cols, win_height/term->rows);\r
+               offset_width = (win_width-font_width*term->cols)/2;\r
+               offset_height = (win_height-font_height*term->rows)/2;\r
+               InvalidateRect(hwnd, NULL, TRUE);\r
+#ifdef RDB_DEBUG_PATCH\r
+               debug((25, "reset_window() -> Z font resize to (%d, %d)",\r
+                       font_width, font_height));\r
+#endif\r
+           }\r
+       } else {\r
+           if (  font_width * term->cols != win_width || \r
+                 font_height * term->rows != win_height) {\r
+               /* Our only choice at this point is to change the \r
+                * size of the terminal; Oh well.\r
+                */\r
+               term_size(term, win_height/font_height, win_width/font_width,\r
+                         cfg.savelines);\r
+               offset_width = (win_width-font_width*term->cols)/2;\r
+               offset_height = (win_height-font_height*term->rows)/2;\r
+               InvalidateRect(hwnd, NULL, TRUE);\r
+#ifdef RDB_DEBUG_PATCH\r
+               debug((27, "reset_window() -> Zoomed term_size"));\r
+#endif\r
+           }\r
+       }\r
+       return;\r
+    }\r
+\r
+    /* Hmm, a force re-init means we should ignore the current window\r
+     * so we resize to the default font size.\r
+     */\r
+    if (reinit>0) {\r
+#ifdef RDB_DEBUG_PATCH\r
+       debug((27, "reset_window() -> Forced re-init"));\r
+#endif\r
+\r
+       offset_width = offset_height = cfg.window_border;\r
+       extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;\r
+       extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;\r
+\r
+       if (win_width != font_width*term->cols + offset_width*2 ||\r
+           win_height != font_height*term->rows + offset_height*2) {\r
+\r
+           /* If this is too large windows will resize it to the maximum\r
+            * allowed window size, we will then be back in here and resize\r
+            * the font or terminal to fit.\r
+            */\r
+           SetWindowPos(hwnd, NULL, 0, 0, \r
+                        font_width*term->cols + extra_width, \r
+                        font_height*term->rows + extra_height,\r
+                        SWP_NOMOVE | SWP_NOZORDER);\r
+       }\r
+\r
+       InvalidateRect(hwnd, NULL, TRUE);\r
+       return;\r
+    }\r
+\r
+    /* Okay the user doesn't want us to change the font so we try the \r
+     * window. But that may be too big for the screen which forces us\r
+     * to change the terminal.\r
+     */\r
+    if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||\r
+        (cfg.resize_action == RESIZE_EITHER && reinit<0) ||\r
+           reinit>0) {\r
+       offset_width = offset_height = cfg.window_border;\r
+       extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;\r
+       extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;\r
+\r
+       if (win_width != font_width*term->cols + offset_width*2 ||\r
+           win_height != font_height*term->rows + offset_height*2) {\r
+\r
+           static RECT ss;\r
+           int width, height;\r
+               \r
+               get_fullscreen_rect(&ss);\r
+\r
+           width = (ss.right - ss.left - extra_width) / font_width;\r
+           height = (ss.bottom - ss.top - extra_height) / font_height;\r
+\r
+           /* Grrr too big */\r
+           if ( term->rows > height || term->cols > width ) {\r
+               if (cfg.resize_action == RESIZE_EITHER) {\r
+                   /* Make the font the biggest we can */\r
+                   if (term->cols > width)\r
+                       font_width = (ss.right - ss.left - extra_width)\r
+                           / term->cols;\r
+                   if (term->rows > height)\r
+                       font_height = (ss.bottom - ss.top - extra_height)\r
+                           / term->rows;\r
+\r
+                   deinit_fonts();\r
+                   init_fonts(font_width, font_height);\r
+\r
+                   width = (ss.right - ss.left - extra_width) / font_width;\r
+                   height = (ss.bottom - ss.top - extra_height) / font_height;\r
+               } else {\r
+                   if ( height > term->rows ) height = term->rows;\r
+                   if ( width > term->cols )  width = term->cols;\r
+                   term_size(term, height, width, cfg.savelines);\r
+#ifdef RDB_DEBUG_PATCH\r
+                   debug((27, "reset_window() -> term resize to (%d,%d)",\r
+                              height, width));\r
+#endif\r
+               }\r
+           }\r
+           \r
+           SetWindowPos(hwnd, NULL, 0, 0, \r
+                        font_width*term->cols + extra_width, \r
+                        font_height*term->rows + extra_height,\r
+                        SWP_NOMOVE | SWP_NOZORDER);\r
+\r
+           InvalidateRect(hwnd, NULL, TRUE);\r
+#ifdef RDB_DEBUG_PATCH\r
+           debug((27, "reset_window() -> window resize to (%d,%d)",\r
+                       font_width*term->cols + extra_width,\r
+                       font_height*term->rows + extra_height));\r
+#endif\r
+       }\r
+       return;\r
+    }\r
+\r
+    /* We're allowed to or must change the font but do we want to ?  */\r
+\r
+    if (font_width != (win_width-cfg.window_border*2)/term->cols || \r
+       font_height != (win_height-cfg.window_border*2)/term->rows) {\r
+\r
+       deinit_fonts();\r
+       init_fonts((win_width-cfg.window_border*2)/term->cols, \r
+                  (win_height-cfg.window_border*2)/term->rows);\r
+       offset_width = (win_width-font_width*term->cols)/2;\r
+       offset_height = (win_height-font_height*term->rows)/2;\r
+\r
+       extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;\r
+       extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;\r
+\r
+       InvalidateRect(hwnd, NULL, TRUE);\r
+#ifdef RDB_DEBUG_PATCH\r
+       debug((25, "reset_window() -> font resize to (%d,%d)", \r
+                  font_width, font_height));\r
+#endif\r
+    }\r
+}\r
+\r
+static void set_input_locale(HKL kl)\r
+{\r
+    char lbuf[20];\r
+\r
+    GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,\r
+                 lbuf, sizeof(lbuf));\r
+\r
+    kbd_codepage = atoi(lbuf);\r
+}\r
+\r
+static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)\r
+{\r
+    int thistime = GetMessageTime();\r
+\r
+    if (send_raw_mouse && !(cfg.mouse_override && shift)) {\r
+       lastbtn = MBT_NOTHING;\r
+       term_mouse(term, b, translate_button(b), MA_CLICK,\r
+                  x, y, shift, ctrl, alt);\r
+       return;\r
+    }\r
+\r
+    if (lastbtn == b && thistime - lasttime < dbltime) {\r
+       lastact = (lastact == MA_CLICK ? MA_2CLK :\r
+                  lastact == MA_2CLK ? MA_3CLK :\r
+                  lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);\r
+    } else {\r
+       lastbtn = b;\r
+       lastact = MA_CLICK;\r
+    }\r
+    if (lastact != MA_NOTHING)\r
+       term_mouse(term, b, translate_button(b), lastact,\r
+                  x, y, shift, ctrl, alt);\r
+    lasttime = thistime;\r
+}\r
+\r
+/*\r
+ * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)\r
+ * into a cooked one (SELECT, EXTEND, PASTE).\r
+ */\r
+static Mouse_Button translate_button(Mouse_Button button)\r
+{\r
+    if (button == MBT_LEFT)\r
+       return MBT_SELECT;\r
+    if (button == MBT_MIDDLE)\r
+       return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND;\r
+    if (button == MBT_RIGHT)\r
+       return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE;\r
+    return 0;                         /* shouldn't happen */\r
+}\r
+\r
+static void show_mouseptr(int show)\r
+{\r
+    /* NB that the counter in ShowCursor() is also frobbed by\r
+     * update_mouse_pointer() */\r
+    static int cursor_visible = 1;\r
+    if (!cfg.hide_mouseptr)           /* override if this feature disabled */\r
+       show = 1;\r
+    if (cursor_visible && !show)\r
+       ShowCursor(FALSE);\r
+    else if (!cursor_visible && show)\r
+       ShowCursor(TRUE);\r
+    cursor_visible = show;\r
+}\r
+\r
+static int is_alt_pressed(void)\r
+{\r
+    BYTE keystate[256];\r
+    int r = GetKeyboardState(keystate);\r
+    if (!r)\r
+       return FALSE;\r
+    if (keystate[VK_MENU] & 0x80)\r
+       return TRUE;\r
+    if (keystate[VK_RMENU] & 0x80)\r
+       return TRUE;\r
+    return FALSE;\r
+}\r
+\r
+static int resizing;\r
+\r
+void notify_remote_exit(void *fe)\r
+{\r
+    int exitcode;\r
+\r
+    if (!session_closed &&\r
+        (exitcode = back->exitcode(backhandle)) >= 0) {\r
+       /* Abnormal exits will already have set session_closed and taken\r
+        * appropriate action. */\r
+       if (cfg.close_on_exit == FORCE_ON ||\r
+           (cfg.close_on_exit == AUTO && exitcode != INT_MAX)) {\r
+           PostQuitMessage(0);\r
+       } else {\r
+           must_close_session = TRUE;\r
+           session_closed = TRUE;\r
+           /* exitcode == INT_MAX indicates that the connection was closed\r
+            * by a fatal error, so an error box will be coming our way and\r
+            * we should not generate this informational one. */\r
+           if (exitcode != INT_MAX)\r
+               MessageBox(hwnd, "Connection closed by remote host",\r
+                          appname, MB_OK | MB_ICONINFORMATION);\r
+       }\r
+    }\r
+}\r
+\r
+void timer_change_notify(long next)\r
+{\r
+    long ticks = next - GETTICKCOUNT();\r
+    if (ticks <= 0) ticks = 1;        /* just in case */\r
+    KillTimer(hwnd, TIMING_TIMER_ID);\r
+    SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL);\r
+    timing_next_time = next;\r
+}\r
+\r
+static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    HDC hdc;\r
+    static int ignore_clip = FALSE;\r
+    static int need_backend_resize = FALSE;\r
+    static int fullscr_on_max = FALSE;\r
+    static int processed_resize = FALSE;\r
+    static UINT last_mousemove = 0;\r
+\r
+    switch (message) {\r
+      case WM_TIMER:\r
+       if ((UINT_PTR)wParam == TIMING_TIMER_ID) {\r
+           long next;\r
+\r
+           KillTimer(hwnd, TIMING_TIMER_ID);\r
+           if (run_timers(timing_next_time, &next)) {\r
+               timer_change_notify(next);\r
+           } else {\r
+           }\r
+       }\r
+       return 0;\r
+      case WM_CREATE:\r
+       break;\r
+      case WM_CLOSE:\r
+       {\r
+           char *str;\r
+           show_mouseptr(1);\r
+           str = dupprintf("%s Exit Confirmation", appname);\r
+           if (!cfg.warn_on_close || session_closed ||\r
+               MessageBox(hwnd,\r
+                          "Are you sure you want to close this session?",\r
+                          str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1)\r
+               == IDOK)\r
+               DestroyWindow(hwnd);\r
+           sfree(str);\r
+       }\r
+       return 0;\r
+      case WM_DESTROY:\r
+       show_mouseptr(1);\r
+       PostQuitMessage(0);\r
+       return 0;\r
+      case WM_INITMENUPOPUP:\r
+       if ((HMENU)wParam == savedsess_menu) {\r
+           /* About to pop up Saved Sessions sub-menu.\r
+            * Refresh the session list. */\r
+           get_sesslist(&sesslist, FALSE); /* free */\r
+           get_sesslist(&sesslist, TRUE);\r
+           update_savedsess_menu();\r
+           return 0;\r
+       }\r
+       break;\r
+      case WM_COMMAND:\r
+      case WM_SYSCOMMAND:\r
+       switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */\r
+         case IDM_SHOWLOG:\r
+           showeventlog(hwnd);\r
+           break;\r
+         case IDM_NEWSESS:\r
+         case IDM_DUPSESS:\r
+         case IDM_SAVEDSESS:\r
+           {\r
+               char b[2048];\r
+               char c[30], *cl;\r
+               int freecl = FALSE;\r
+               BOOL inherit_handles;\r
+               STARTUPINFO si;\r
+               PROCESS_INFORMATION pi;\r
+               HANDLE filemap = NULL;\r
+\r
+               if (wParam == IDM_DUPSESS) {\r
+                   /*\r
+                    * Allocate a file-mapping memory chunk for the\r
+                    * config structure.\r
+                    */\r
+                   SECURITY_ATTRIBUTES sa;\r
+                   Config *p;\r
+\r
+                   sa.nLength = sizeof(sa);\r
+                   sa.lpSecurityDescriptor = NULL;\r
+                   sa.bInheritHandle = TRUE;\r
+                   filemap = CreateFileMapping(INVALID_HANDLE_VALUE,\r
+                                               &sa,\r
+                                               PAGE_READWRITE,\r
+                                               0, sizeof(Config), NULL);\r
+                   if (filemap && filemap != INVALID_HANDLE_VALUE) {\r
+                       p = (Config *) MapViewOfFile(filemap,\r
+                                                    FILE_MAP_WRITE,\r
+                                                    0, 0, sizeof(Config));\r
+                       if (p) {\r
+                           *p = cfg;  /* structure copy */\r
+                           UnmapViewOfFile(p);\r
+                       }\r
+                   }\r
+                   inherit_handles = TRUE;\r
+                   sprintf(c, "putty &%p", filemap);\r
+                   cl = c;\r
+               } else if (wParam == IDM_SAVEDSESS) {\r
+                   unsigned int sessno = ((lParam - IDM_SAVED_MIN)\r
+                                          / MENU_SAVED_STEP) + 1;\r
+                   if (sessno < (unsigned)sesslist.nsessions) {\r
+                       char *session = sesslist.sessions[sessno];\r
+                       cl = dupprintf("putty @%s", session);\r
+                       inherit_handles = FALSE;\r
+                       freecl = TRUE;\r
+                   } else\r
+                       break;\r
+               } else /* IDM_NEWSESS */ {\r
+                   cl = NULL;\r
+                   inherit_handles = FALSE;\r
+               }\r
+\r
+               GetModuleFileName(NULL, b, sizeof(b) - 1);\r
+               si.cb = sizeof(si);\r
+               si.lpReserved = NULL;\r
+               si.lpDesktop = NULL;\r
+               si.lpTitle = NULL;\r
+               si.dwFlags = 0;\r
+               si.cbReserved2 = 0;\r
+               si.lpReserved2 = NULL;\r
+               CreateProcess(b, cl, NULL, NULL, inherit_handles,\r
+                             NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);\r
+\r
+               if (filemap)\r
+                   CloseHandle(filemap);\r
+               if (freecl)\r
+                   sfree(cl);\r
+           }\r
+           break;\r
+         case IDM_RESTART:\r
+           if (!back) {\r
+               logevent(NULL, "----- Session restarted -----");\r
+               term_pwron(term, FALSE);\r
+               start_backend();\r
+           }\r
+\r
+           break;\r
+         case IDM_RECONF:\r
+           {\r
+               Config prev_cfg;\r
+               int init_lvl = 1;\r
+               int reconfig_result;\r
+\r
+               if (reconfiguring)\r
+                   break;\r
+               else\r
+                   reconfiguring = TRUE;\r
+\r
+               GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));\r
+               prev_cfg = cfg;\r
+\r
+               reconfig_result =\r
+                   do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0);\r
+               reconfiguring = FALSE;\r
+               if (!reconfig_result)\r
+                   break;\r
+\r
+               {\r
+                   /* Disable full-screen if resizing forbidden */\r
+                   int i;\r
+                   for (i = 0; i < lenof(popup_menus); i++)\r
+                       EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN,\r
+                                      MF_BYCOMMAND | \r
+                                      (cfg.resize_action == RESIZE_DISABLED)\r
+                                      ? MF_GRAYED : MF_ENABLED);\r
+                   /* Gracefully unzoom if necessary */\r
+                   if (IsZoomed(hwnd) &&\r
+                       (cfg.resize_action == RESIZE_DISABLED)) {\r
+                       ShowWindow(hwnd, SW_RESTORE);\r
+                   }\r
+               }\r
+\r
+               /* Pass new config data to the logging module */\r
+               log_reconfig(logctx, &cfg);\r
+\r
+               sfree(logpal);\r
+               /*\r
+                * Flush the line discipline's edit buffer in the\r
+                * case where local editing has just been disabled.\r
+                */\r
+               if (ldisc)\r
+                   ldisc_send(ldisc, NULL, 0, 0);\r
+               if (pal)\r
+                   DeleteObject(pal);\r
+               logpal = NULL;\r
+               pal = NULL;\r
+               cfgtopalette();\r
+               init_palette();\r
+\r
+               /* Pass new config data to the terminal */\r
+               term_reconfig(term, &cfg);\r
+\r
+               /* Pass new config data to the back end */\r
+               if (back)\r
+                   back->reconfig(backhandle, &cfg);\r
+\r
+               /* Screen size changed ? */\r
+               if (cfg.height != prev_cfg.height ||\r
+                   cfg.width != prev_cfg.width ||\r
+                   cfg.savelines != prev_cfg.savelines ||\r
+                   cfg.resize_action == RESIZE_FONT ||\r
+                   (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||\r
+                   cfg.resize_action == RESIZE_DISABLED)\r
+                   term_size(term, cfg.height, cfg.width, cfg.savelines);\r
+\r
+               /* Enable or disable the scroll bar, etc */\r
+               {\r
+                   LONG nflg, flag = GetWindowLongPtr(hwnd, GWL_STYLE);\r
+                   LONG nexflag, exflag =\r
+                       GetWindowLongPtr(hwnd, GWL_EXSTYLE);\r
+\r
+                   nexflag = exflag;\r
+                   if (cfg.alwaysontop != prev_cfg.alwaysontop) {\r
+                       if (cfg.alwaysontop) {\r
+                           nexflag |= WS_EX_TOPMOST;\r
+                           SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,\r
+                                        SWP_NOMOVE | SWP_NOSIZE);\r
+                       } else {\r
+                           nexflag &= ~(WS_EX_TOPMOST);\r
+                           SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,\r
+                                        SWP_NOMOVE | SWP_NOSIZE);\r
+                       }\r
+                   }\r
+                   if (cfg.sunken_edge)\r
+                       nexflag |= WS_EX_CLIENTEDGE;\r
+                   else\r
+                       nexflag &= ~(WS_EX_CLIENTEDGE);\r
+\r
+                   nflg = flag;\r
+                   if (is_full_screen() ?\r
+                       cfg.scrollbar_in_fullscreen : cfg.scrollbar)\r
+                       nflg |= WS_VSCROLL;\r
+                   else\r
+                       nflg &= ~WS_VSCROLL;\r
+\r
+                   if (cfg.resize_action == RESIZE_DISABLED ||\r
+                        is_full_screen())\r
+                       nflg &= ~WS_THICKFRAME;\r
+                   else\r
+                       nflg |= WS_THICKFRAME;\r
+\r
+                   if (cfg.resize_action == RESIZE_DISABLED)\r
+                       nflg &= ~WS_MAXIMIZEBOX;\r
+                   else\r
+                       nflg |= WS_MAXIMIZEBOX;\r
+\r
+                   if (nflg != flag || nexflag != exflag) {\r
+                       if (nflg != flag)\r
+                           SetWindowLongPtr(hwnd, GWL_STYLE, nflg);\r
+                       if (nexflag != exflag)\r
+                           SetWindowLongPtr(hwnd, GWL_EXSTYLE, nexflag);\r
+\r
+                       SetWindowPos(hwnd, NULL, 0, 0, 0, 0,\r
+                                    SWP_NOACTIVATE | SWP_NOCOPYBITS |\r
+                                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |\r
+                                    SWP_FRAMECHANGED);\r
+\r
+                       init_lvl = 2;\r
+                   }\r
+               }\r
+\r
+               /* Oops */\r
+               if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {\r
+                   force_normal(hwnd);\r
+                   init_lvl = 2;\r
+               }\r
+\r
+               set_title(NULL, cfg.wintitle);\r
+               if (IsIconic(hwnd)) {\r
+                   SetWindowText(hwnd,\r
+                                 cfg.win_name_always ? window_name :\r
+                                 icon_name);\r
+               }\r
+\r
+               if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||\r
+                   strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||\r
+                   cfg.font.isbold != prev_cfg.font.isbold ||\r
+                   cfg.font.height != prev_cfg.font.height ||\r
+                   cfg.font.charset != prev_cfg.font.charset ||\r
+                   cfg.font_quality != prev_cfg.font_quality ||\r
+                   cfg.vtmode != prev_cfg.vtmode ||\r
+                   cfg.bold_colour != prev_cfg.bold_colour ||\r
+                   cfg.resize_action == RESIZE_DISABLED ||\r
+                   cfg.resize_action == RESIZE_EITHER ||\r
+                   (cfg.resize_action != prev_cfg.resize_action))\r
+                   init_lvl = 2;\r
+\r
+               InvalidateRect(hwnd, NULL, TRUE);\r
+               reset_window(init_lvl);\r
+               net_pending_errors();\r
+           }\r
+           break;\r
+         case IDM_COPYALL:\r
+           term_copyall(term);\r
+           break;\r
+         case IDM_PASTE:\r
+           request_paste(NULL);\r
+           break;\r
+         case IDM_CLRSB:\r
+           term_clrsb(term);\r
+           break;\r
+         case IDM_RESET:\r
+           term_pwron(term, TRUE);\r
+           if (ldisc)\r
+               ldisc_send(ldisc, NULL, 0, 0);\r
+           break;\r
+         case IDM_ABOUT:\r
+           showabout(hwnd);\r
+           break;\r
+         case IDM_HELP:\r
+           launch_help(hwnd, NULL);\r
+           break;\r
+         case SC_MOUSEMENU:\r
+           /*\r
+            * We get this if the System menu has been activated\r
+            * using the mouse.\r
+            */\r
+           show_mouseptr(1);\r
+           break;\r
+          case SC_KEYMENU:\r
+           /*\r
+            * We get this if the System menu has been activated\r
+            * using the keyboard. This might happen from within\r
+            * TranslateKey, in which case it really wants to be\r
+            * followed by a `space' character to actually _bring\r
+            * the menu up_ rather than just sitting there in\r
+            * `ready to appear' state.\r
+            */\r
+           show_mouseptr(1);          /* make sure pointer is visible */\r
+           if( lParam == 0 )\r
+               PostMessage(hwnd, WM_CHAR, ' ', 0);\r
+           break;\r
+         case IDM_FULLSCREEN:\r
+           flip_full_screen();\r
+           break;\r
+         default:\r
+           if (wParam >= IDM_SAVED_MIN && wParam < IDM_SAVED_MAX) {\r
+               SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);\r
+           }\r
+           if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {\r
+               int i = (wParam - IDM_SPECIAL_MIN) / 0x10;\r
+               /*\r
+                * Ensure we haven't been sent a bogus SYSCOMMAND\r
+                * which would cause us to reference invalid memory\r
+                * and crash. Perhaps I'm just too paranoid here.\r
+                */\r
+               if (i >= n_specials)\r
+                   break;\r
+               if (back)\r
+                   back->special(backhandle, specials[i].code);\r
+               net_pending_errors();\r
+           }\r
+       }\r
+       break;\r
+\r
+#define X_POS(l) ((int)(short)LOWORD(l))\r
+#define Y_POS(l) ((int)(short)HIWORD(l))\r
+\r
+#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)\r
+#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)\r
+      case WM_LBUTTONDOWN:\r
+      case WM_MBUTTONDOWN:\r
+      case WM_RBUTTONDOWN:\r
+      case WM_LBUTTONUP:\r
+      case WM_MBUTTONUP:\r
+      case WM_RBUTTONUP:\r
+       if (message == WM_RBUTTONDOWN &&\r
+           ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) {\r
+           POINT cursorpos;\r
+\r
+           show_mouseptr(1);          /* make sure pointer is visible */\r
+           GetCursorPos(&cursorpos);\r
+           TrackPopupMenu(popup_menus[CTXMENU].menu,\r
+                          TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,\r
+                          cursorpos.x, cursorpos.y,\r
+                          0, hwnd, NULL);\r
+           break;\r
+       }\r
+       {\r
+           int button, press;\r
+\r
+           switch (message) {\r
+             case WM_LBUTTONDOWN:\r
+               button = MBT_LEFT;\r
+               wParam |= MK_LBUTTON;\r
+               press = 1;\r
+               break;\r
+             case WM_MBUTTONDOWN:\r
+               button = MBT_MIDDLE;\r
+               wParam |= MK_MBUTTON;\r
+               press = 1;\r
+               break;\r
+             case WM_RBUTTONDOWN:\r
+               button = MBT_RIGHT;\r
+               wParam |= MK_RBUTTON;\r
+               press = 1;\r
+               break;\r
+             case WM_LBUTTONUP:\r
+               button = MBT_LEFT;\r
+               wParam &= ~MK_LBUTTON;\r
+               press = 0;\r
+               break;\r
+             case WM_MBUTTONUP:\r
+               button = MBT_MIDDLE;\r
+               wParam &= ~MK_MBUTTON;\r
+               press = 0;\r
+               break;\r
+             case WM_RBUTTONUP:\r
+               button = MBT_RIGHT;\r
+               wParam &= ~MK_RBUTTON;\r
+               press = 0;\r
+               break;\r
+             default:\r
+               button = press = 0;    /* shouldn't happen */\r
+           }\r
+           show_mouseptr(1);\r
+           /*\r
+            * Special case: in full-screen mode, if the left\r
+            * button is clicked in the very top left corner of the\r
+            * window, we put up the System menu instead of doing\r
+            * selection.\r
+            */\r
+           {\r
+               char mouse_on_hotspot = 0;\r
+               POINT pt;\r
+\r
+               GetCursorPos(&pt);\r
+#ifndef NO_MULTIMON\r
+               {\r
+                   HMONITOR mon;\r
+                   MONITORINFO mi;\r
+\r
+                   mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);\r
+\r
+                   if (mon != NULL) {\r
+                       mi.cbSize = sizeof(MONITORINFO);\r
+                       GetMonitorInfo(mon, &mi);\r
+\r
+                       if (mi.rcMonitor.left == pt.x &&\r
+                           mi.rcMonitor.top == pt.y) {\r
+                           mouse_on_hotspot = 1;\r
+                       }\r
+                   }\r
+               }\r
+#else\r
+               if (pt.x == 0 && pt.y == 0) {\r
+                   mouse_on_hotspot = 1;\r
+               }\r
+#endif\r
+               if (is_full_screen() && press &&\r
+                   button == MBT_LEFT && mouse_on_hotspot) {\r
+                   SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU,\r
+                               MAKELPARAM(pt.x, pt.y));\r
+                   return 0;\r
+               }\r
+           }\r
+\r
+           if (press) {\r
+               click(button,\r
+                     TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),\r
+                     wParam & MK_SHIFT, wParam & MK_CONTROL,\r
+                     is_alt_pressed());\r
+               SetCapture(hwnd);\r
+           } else {\r
+               term_mouse(term, button, translate_button(button), MA_RELEASE,\r
+                          TO_CHR_X(X_POS(lParam)),\r
+                          TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,\r
+                          wParam & MK_CONTROL, is_alt_pressed());\r
+               if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)))\r
+                   ReleaseCapture();\r
+           }\r
+       }\r
+       return 0;\r
+      case WM_MOUSEMOVE:\r
+       {\r
+           /*\r
+            * Windows seems to like to occasionally send MOUSEMOVE\r
+            * events even if the mouse hasn't moved. Don't unhide\r
+            * the mouse pointer in this case.\r
+            */\r
+           static WPARAM wp = 0;\r
+           static LPARAM lp = 0;\r
+           if (wParam != wp || lParam != lp ||\r
+               last_mousemove != WM_MOUSEMOVE) {\r
+               show_mouseptr(1);\r
+               wp = wParam; lp = lParam;\r
+               last_mousemove = WM_MOUSEMOVE;\r
+           }\r
+       }\r
+       /*\r
+        * Add the mouse position and message time to the random\r
+        * number noise.\r
+        */\r
+       noise_ultralight(lParam);\r
+\r
+       if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&\r
+           GetCapture() == hwnd) {\r
+           Mouse_Button b;\r
+           if (wParam & MK_LBUTTON)\r
+               b = MBT_LEFT;\r
+           else if (wParam & MK_MBUTTON)\r
+               b = MBT_MIDDLE;\r
+           else\r
+               b = MBT_RIGHT;\r
+           term_mouse(term, b, translate_button(b), MA_DRAG,\r
+                      TO_CHR_X(X_POS(lParam)),\r
+                      TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,\r
+                      wParam & MK_CONTROL, is_alt_pressed());\r
+       }\r
+       return 0;\r
+      case WM_NCMOUSEMOVE:\r
+       {\r
+           static WPARAM wp = 0;\r
+           static LPARAM lp = 0;\r
+           if (wParam != wp || lParam != lp ||\r
+               last_mousemove != WM_NCMOUSEMOVE) {\r
+               show_mouseptr(1);\r
+               wp = wParam; lp = lParam;\r
+               last_mousemove = WM_NCMOUSEMOVE;\r
+           }\r
+       }\r
+       noise_ultralight(lParam);\r
+       break;\r
+      case WM_IGNORE_CLIP:\r
+       ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */\r
+       break;\r
+      case WM_DESTROYCLIPBOARD:\r
+       if (!ignore_clip)\r
+           term_deselect(term);\r
+       ignore_clip = FALSE;\r
+       return 0;\r
+      case WM_PAINT:\r
+       {\r
+           PAINTSTRUCT p;\r
+\r
+           HideCaret(hwnd);\r
+           hdc = BeginPaint(hwnd, &p);\r
+           if (pal) {\r
+               SelectPalette(hdc, pal, TRUE);\r
+               RealizePalette(hdc);\r
+           }\r
+\r
+           /*\r
+            * We have to be careful about term_paint(). It will\r
+            * set a bunch of character cells to INVALID and then\r
+            * call do_paint(), which will redraw those cells and\r
+            * _then mark them as done_. This may not be accurate:\r
+            * when painting in WM_PAINT context we are restricted\r
+            * to the rectangle which has just been exposed - so if\r
+            * that only covers _part_ of a character cell and the\r
+            * rest of it was already visible, that remainder will\r
+            * not be redrawn at all. Accordingly, we must not\r
+            * paint any character cell in a WM_PAINT context which\r
+            * already has a pending update due to terminal output.\r
+            * The simplest solution to this - and many, many\r
+            * thanks to Hung-Te Lin for working all this out - is\r
+            * not to do any actual painting at _all_ if there's a\r
+            * pending terminal update: just mark the relevant\r
+            * character cells as INVALID and wait for the\r
+            * scheduled full update to sort it out.\r
+            * \r
+            * I have a suspicion this isn't the _right_ solution.\r
+            * An alternative approach would be to have terminal.c\r
+            * separately track what _should_ be on the terminal\r
+            * screen and what _is_ on the terminal screen, and\r
+            * have two completely different types of redraw (one\r
+            * for full updates, which syncs the former with the\r
+            * terminal itself, and one for WM_PAINT which syncs\r
+            * the latter with the former); yet another possibility\r
+            * would be to have the Windows front end do what the\r
+            * GTK one already does, and maintain a bitmap of the\r
+            * current terminal appearance so that WM_PAINT becomes\r
+            * completely trivial. However, this should do for now.\r
+            */\r
+           term_paint(term, hdc, \r
+                      (p.rcPaint.left-offset_width)/font_width,\r
+                      (p.rcPaint.top-offset_height)/font_height,\r
+                      (p.rcPaint.right-offset_width-1)/font_width,\r
+                      (p.rcPaint.bottom-offset_height-1)/font_height,\r
+                      !term->window_update_pending);\r
+\r
+           if (p.fErase ||\r
+               p.rcPaint.left  < offset_width  ||\r
+               p.rcPaint.top   < offset_height ||\r
+               p.rcPaint.right >= offset_width + font_width*term->cols ||\r
+               p.rcPaint.bottom>= offset_height + font_height*term->rows)\r
+           {\r
+               HBRUSH fillcolour, oldbrush;\r
+               HPEN   edge, oldpen;\r
+               fillcolour = CreateSolidBrush (\r
+                                   colours[ATTR_DEFBG>>ATTR_BGSHIFT]);\r
+               oldbrush = SelectObject(hdc, fillcolour);\r
+               edge = CreatePen(PS_SOLID, 0, \r
+                                   colours[ATTR_DEFBG>>ATTR_BGSHIFT]);\r
+               oldpen = SelectObject(hdc, edge);\r
+\r
+               /*\r
+                * Jordan Russell reports that this apparently\r
+                * ineffectual IntersectClipRect() call masks a\r
+                * Windows NT/2K bug causing strange display\r
+                * problems when the PuTTY window is taller than\r
+                * the primary monitor. It seems harmless enough...\r
+                */\r
+               IntersectClipRect(hdc,\r
+                       p.rcPaint.left, p.rcPaint.top,\r
+                       p.rcPaint.right, p.rcPaint.bottom);\r
+\r
+               ExcludeClipRect(hdc, \r
+                       offset_width, offset_height,\r
+                       offset_width+font_width*term->cols,\r
+                       offset_height+font_height*term->rows);\r
+\r
+               Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, \r
+                         p.rcPaint.right, p.rcPaint.bottom);\r
+\r
+               /* SelectClipRgn(hdc, NULL); */\r
+\r
+               SelectObject(hdc, oldbrush);\r
+               DeleteObject(fillcolour);\r
+               SelectObject(hdc, oldpen);\r
+               DeleteObject(edge);\r
+           }\r
+           SelectObject(hdc, GetStockObject(SYSTEM_FONT));\r
+           SelectObject(hdc, GetStockObject(WHITE_PEN));\r
+           EndPaint(hwnd, &p);\r
+           ShowCaret(hwnd);\r
+       }\r
+       return 0;\r
+      case WM_NETEVENT:\r
+       /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc\r
+        * but the only one that's likely to try to overload us is FD_READ.\r
+        * This means buffering just one is fine.\r
+        */\r
+       if (pending_netevent)\r
+           enact_pending_netevent();\r
+\r
+       pending_netevent = TRUE;\r
+       pend_netevent_wParam = wParam;\r
+       pend_netevent_lParam = lParam;\r
+       if (WSAGETSELECTEVENT(lParam) != FD_READ)\r
+           enact_pending_netevent();\r
+\r
+       net_pending_errors();\r
+       return 0;\r
+      case WM_SETFOCUS:\r
+       term_set_focus(term, TRUE);\r
+       CreateCaret(hwnd, caretbm, font_width, font_height);\r
+       ShowCaret(hwnd);\r
+       flash_window(0);               /* stop */\r
+       compose_state = 0;\r
+       term_update(term);\r
+       break;\r
+      case WM_KILLFOCUS:\r
+       show_mouseptr(1);\r
+       term_set_focus(term, FALSE);\r
+       DestroyCaret();\r
+       caret_x = caret_y = -1;        /* ensure caret is replaced next time */\r
+       term_update(term);\r
+       break;\r
+      case WM_ENTERSIZEMOVE:\r
+#ifdef RDB_DEBUG_PATCH\r
+       debug((27, "WM_ENTERSIZEMOVE"));\r
+#endif\r
+       EnableSizeTip(1);\r
+       resizing = TRUE;\r
+       need_backend_resize = FALSE;\r
+       break;\r
+      case WM_EXITSIZEMOVE:\r
+       EnableSizeTip(0);\r
+       resizing = FALSE;\r
+#ifdef RDB_DEBUG_PATCH\r
+       debug((27, "WM_EXITSIZEMOVE"));\r
+#endif\r
+       if (need_backend_resize) {\r
+           term_size(term, cfg.height, cfg.width, cfg.savelines);\r
+           InvalidateRect(hwnd, NULL, TRUE);\r
+       }\r
+       break;\r
+      case WM_SIZING:\r
+       /*\r
+        * This does two jobs:\r
+        * 1) Keep the sizetip uptodate\r
+        * 2) Make sure the window size is _stepped_ in units of the font size.\r
+        */\r
+        if (cfg.resize_action == RESIZE_TERM ||\r
+            (cfg.resize_action == RESIZE_EITHER && !is_alt_pressed())) {\r
+           int width, height, w, h, ew, eh;\r
+           LPRECT r = (LPRECT) lParam;\r
+\r
+           if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&\r
+                   (cfg.height != term->rows || cfg.width != term->cols )) {\r
+               /* \r
+                * Great! It seems that both the terminal size and the\r
+                * font size have been changed and the user is now dragging.\r
+                * \r
+                * It will now be difficult to get back to the configured\r
+                * font size!\r
+                *\r
+                * This would be easier but it seems to be too confusing.\r
+\r
+               term_size(term, cfg.height, cfg.width, cfg.savelines);\r
+               reset_window(2);\r
+                */\r
+               cfg.height=term->rows; cfg.width=term->cols;\r
+\r
+               InvalidateRect(hwnd, NULL, TRUE);\r
+               need_backend_resize = TRUE;\r
+           }\r
+\r
+           width = r->right - r->left - extra_width;\r
+           height = r->bottom - r->top - extra_height;\r
+           w = (width + font_width / 2) / font_width;\r
+           if (w < 1)\r
+               w = 1;\r
+           h = (height + font_height / 2) / font_height;\r
+           if (h < 1)\r
+               h = 1;\r
+           UpdateSizeTip(hwnd, w, h);\r
+           ew = width - w * font_width;\r
+           eh = height - h * font_height;\r
+           if (ew != 0) {\r
+               if (wParam == WMSZ_LEFT ||\r
+                   wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)\r
+                   r->left += ew;\r
+               else\r
+                   r->right -= ew;\r
+           }\r
+           if (eh != 0) {\r
+               if (wParam == WMSZ_TOP ||\r
+                   wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)\r
+                   r->top += eh;\r
+               else\r
+                   r->bottom -= eh;\r
+           }\r
+           if (ew || eh)\r
+               return 1;\r
+           else\r
+               return 0;\r
+       } else {\r
+           int width, height, w, h, rv = 0;\r
+           int ex_width = extra_width + (cfg.window_border - offset_width) * 2;\r
+           int ex_height = extra_height + (cfg.window_border - offset_height) * 2;\r
+           LPRECT r = (LPRECT) lParam;\r
+\r
+           width = r->right - r->left - ex_width;\r
+           height = r->bottom - r->top - ex_height;\r
+\r
+           w = (width + term->cols/2)/term->cols;\r
+           h = (height + term->rows/2)/term->rows;\r
+           if ( r->right != r->left + w*term->cols + ex_width)\r
+               rv = 1;\r
+\r
+           if (wParam == WMSZ_LEFT ||\r
+               wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)\r
+               r->left = r->right - w*term->cols - ex_width;\r
+           else\r
+               r->right = r->left + w*term->cols + ex_width;\r
+\r
+           if (r->bottom != r->top + h*term->rows + ex_height)\r
+               rv = 1;\r
+\r
+           if (wParam == WMSZ_TOP ||\r
+               wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)\r
+               r->top = r->bottom - h*term->rows - ex_height;\r
+           else\r
+               r->bottom = r->top + h*term->rows + ex_height;\r
+\r
+           return rv;\r
+       }\r
+       /* break;  (never reached) */\r
+      case WM_FULLSCR_ON_MAX:\r
+       fullscr_on_max = TRUE;\r
+       break;\r
+      case WM_MOVE:\r
+       sys_cursor_update();\r
+       break;\r
+      case WM_SIZE:\r
+#ifdef RDB_DEBUG_PATCH\r
+       debug((27, "WM_SIZE %s (%d,%d)",\r
+               (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":\r
+               (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":\r
+               (wParam == SIZE_RESTORED && resizing) ? "to":\r
+               (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":\r
+               "...",\r
+           LOWORD(lParam), HIWORD(lParam)));\r
+#endif\r
+       if (wParam == SIZE_MINIMIZED)\r
+           SetWindowText(hwnd,\r
+                         cfg.win_name_always ? window_name : icon_name);\r
+       if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)\r
+           SetWindowText(hwnd, window_name);\r
+        if (wParam == SIZE_RESTORED) {\r
+            processed_resize = FALSE;\r
+            clear_full_screen();\r
+            if (processed_resize) {\r
+                /*\r
+                 * Inhibit normal processing of this WM_SIZE; a\r
+                 * secondary one was triggered just now by\r
+                 * clear_full_screen which contained the correct\r
+                 * client area size.\r
+                 */\r
+                return 0;\r
+            }\r
+        }\r
+        if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {\r
+            fullscr_on_max = FALSE;\r
+            processed_resize = FALSE;\r
+            make_full_screen();\r
+            if (processed_resize) {\r
+                /*\r
+                 * Inhibit normal processing of this WM_SIZE; a\r
+                 * secondary one was triggered just now by\r
+                 * make_full_screen which contained the correct client\r
+                 * area size.\r
+                 */\r
+                return 0;\r
+            }\r
+        }\r
+\r
+        processed_resize = TRUE;\r
+\r
+       if (cfg.resize_action == RESIZE_DISABLED) {\r
+           /* A resize, well it better be a minimize. */\r
+           reset_window(-1);\r
+       } else {\r
+\r
+           int width, height, w, h;\r
+\r
+           width = LOWORD(lParam);\r
+           height = HIWORD(lParam);\r
+\r
+            if (wParam == SIZE_MAXIMIZED && !was_zoomed) {\r
+                was_zoomed = 1;\r
+                prev_rows = term->rows;\r
+                prev_cols = term->cols;\r
+                if (cfg.resize_action == RESIZE_TERM) {\r
+                    w = width / font_width;\r
+                    if (w < 1) w = 1;\r
+                    h = height / font_height;\r
+                    if (h < 1) h = 1;\r
+\r
+                    term_size(term, h, w, cfg.savelines);\r
+                }\r
+                reset_window(0);\r
+            } else if (wParam == SIZE_RESTORED && was_zoomed) {\r
+                was_zoomed = 0;\r
+                if (cfg.resize_action == RESIZE_TERM) {\r
+                    w = (width-cfg.window_border*2) / font_width;\r
+                    if (w < 1) w = 1;\r
+                    h = (height-cfg.window_border*2) / font_height;\r
+                    if (h < 1) h = 1;\r
+                    term_size(term, h, w, cfg.savelines);\r
+                    reset_window(2);\r
+                } else if (cfg.resize_action != RESIZE_FONT)\r
+                    reset_window(2);\r
+                else\r
+                    reset_window(0);\r
+            } else if (wParam == SIZE_MINIMIZED) {\r
+                /* do nothing */\r
+           } else if (cfg.resize_action == RESIZE_TERM ||\r
+                       (cfg.resize_action == RESIZE_EITHER &&\r
+                        !is_alt_pressed())) {\r
+                w = (width-cfg.window_border*2) / font_width;\r
+                if (w < 1) w = 1;\r
+                h = (height-cfg.window_border*2) / font_height;\r
+                if (h < 1) h = 1;\r
+\r
+                if (resizing) {\r
+                    /*\r
+                     * Don't call back->size in mid-resize. (To\r
+                     * prevent massive numbers of resize events\r
+                     * getting sent down the connection during an NT\r
+                     * opaque drag.)\r
+                     */\r
+                   need_backend_resize = TRUE;\r
+                   cfg.height = h;\r
+                   cfg.width = w;\r
+                } else {\r
+                    term_size(term, h, w, cfg.savelines);\r
+                }\r
+            } else {\r
+                reset_window(0);\r
+           }\r
+       }\r
+       sys_cursor_update();\r
+       return 0;\r
+      case WM_VSCROLL:\r
+       switch (LOWORD(wParam)) {\r
+         case SB_BOTTOM:\r
+           term_scroll(term, -1, 0);\r
+           break;\r
+         case SB_TOP:\r
+           term_scroll(term, +1, 0);\r
+           break;\r
+         case SB_LINEDOWN:\r
+           term_scroll(term, 0, +1);\r
+           break;\r
+         case SB_LINEUP:\r
+           term_scroll(term, 0, -1);\r
+           break;\r
+         case SB_PAGEDOWN:\r
+           term_scroll(term, 0, +term->rows / 2);\r
+           break;\r
+         case SB_PAGEUP:\r
+           term_scroll(term, 0, -term->rows / 2);\r
+           break;\r
+         case SB_THUMBPOSITION:\r
+         case SB_THUMBTRACK:\r
+           term_scroll(term, 1, HIWORD(wParam));\r
+           break;\r
+       }\r
+       break;\r
+      case WM_PALETTECHANGED:\r
+       if ((HWND) wParam != hwnd && pal != NULL) {\r
+           HDC hdc = get_ctx(NULL);\r
+           if (hdc) {\r
+               if (RealizePalette(hdc) > 0)\r
+                   UpdateColors(hdc);\r
+               free_ctx(hdc);\r
+           }\r
+       }\r
+       break;\r
+      case WM_QUERYNEWPALETTE:\r
+       if (pal != NULL) {\r
+           HDC hdc = get_ctx(NULL);\r
+           if (hdc) {\r
+               if (RealizePalette(hdc) > 0)\r
+                   UpdateColors(hdc);\r
+               free_ctx(hdc);\r
+               return TRUE;\r
+           }\r
+       }\r
+       return FALSE;\r
+      case WM_KEYDOWN:\r
+      case WM_SYSKEYDOWN:\r
+      case WM_KEYUP:\r
+      case WM_SYSKEYUP:\r
+       /*\r
+        * Add the scan code and keypress timing to the random\r
+        * number noise.\r
+        */\r
+       noise_ultralight(lParam);\r
+\r
+       /*\r
+        * We don't do TranslateMessage since it disassociates the\r
+        * resulting CHAR message from the KEYDOWN that sparked it,\r
+        * which we occasionally don't want. Instead, we process\r
+        * KEYDOWN, and call the Win32 translator functions so that\r
+        * we get the translations under _our_ control.\r
+        */\r
+       {\r
+           unsigned char buf[20];\r
+           int len;\r
+\r
+           if (wParam == VK_PROCESSKEY) { /* IME PROCESS key */\r
+               if (message == WM_KEYDOWN) {\r
+                   MSG m;\r
+                   m.hwnd = hwnd;\r
+                   m.message = WM_KEYDOWN;\r
+                   m.wParam = wParam;\r
+                   m.lParam = lParam & 0xdfff;\r
+                   TranslateMessage(&m);\r
+               } else break; /* pass to Windows for default processing */\r
+           } else {\r
+               len = TranslateKey(message, wParam, lParam, buf);\r
+               if (len == -1)\r
+                   return DefWindowProc(hwnd, message, wParam, lParam);\r
+\r
+               if (len != 0) {\r
+                   /*\r
+                    * Interrupt an ongoing paste. I'm not sure\r
+                    * this is sensible, but for the moment it's\r
+                    * preferable to having to faff about buffering\r
+                    * things.\r
+                    */\r
+                   term_nopaste(term);\r
+\r
+                   /*\r
+                    * We need not bother about stdin backlogs\r
+                    * here, because in GUI PuTTY we can't do\r
+                    * anything about it anyway; there's no means\r
+                    * of asking Windows to hold off on KEYDOWN\r
+                    * messages. We _have_ to buffer everything\r
+                    * we're sent.\r
+                    */\r
+                   term_seen_key_event(term);\r
+                   if (ldisc)\r
+                       ldisc_send(ldisc, buf, len, 1);\r
+                   show_mouseptr(0);\r
+               }\r
+           }\r
+       }\r
+       net_pending_errors();\r
+       return 0;\r
+      case WM_INPUTLANGCHANGE:\r
+       /* wParam == Font number */\r
+       /* lParam == Locale */\r
+       set_input_locale((HKL)lParam);\r
+       sys_cursor_update();\r
+       break;\r
+      case WM_IME_STARTCOMPOSITION:\r
+       {\r
+           HIMC hImc = ImmGetContext(hwnd);\r
+           ImmSetCompositionFont(hImc, &lfont);\r
+           ImmReleaseContext(hwnd, hImc);\r
+       }\r
+       break;\r
+      case WM_IME_COMPOSITION:\r
+       {\r
+           HIMC hIMC;\r
+           int n;\r
+           char *buff;\r
+\r
+           if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || \r
+               osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */\r
+\r
+           if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */\r
+               break; /* fall back to DefWindowProc */\r
+\r
+           hIMC = ImmGetContext(hwnd);\r
+           n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);\r
+\r
+           if (n > 0) {\r
+               int i;\r
+               buff = snewn(n, char);\r
+               ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);\r
+               /*\r
+                * Jaeyoun Chung reports that Korean character\r
+                * input doesn't work correctly if we do a single\r
+                * luni_send() covering the whole of buff. So\r
+                * instead we luni_send the characters one by one.\r
+                */\r
+               term_seen_key_event(term);\r
+               for (i = 0; i < n; i += 2) {\r
+                   if (ldisc)\r
+                       luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);\r
+               }\r
+               free(buff);\r
+           }\r
+           ImmReleaseContext(hwnd, hIMC);\r
+           return 1;\r
+       }\r
+\r
+      case WM_IME_CHAR:\r
+       if (wParam & 0xFF00) {\r
+           unsigned char buf[2];\r
+\r
+           buf[1] = wParam;\r
+           buf[0] = wParam >> 8;\r
+           term_seen_key_event(term);\r
+           if (ldisc)\r
+               lpage_send(ldisc, kbd_codepage, buf, 2, 1);\r
+       } else {\r
+           char c = (unsigned char) wParam;\r
+           term_seen_key_event(term);\r
+           if (ldisc)\r
+               lpage_send(ldisc, kbd_codepage, &c, 1, 1);\r
+       }\r
+       return (0);\r
+      case WM_CHAR:\r
+      case WM_SYSCHAR:\r
+       /*\r
+        * Nevertheless, we are prepared to deal with WM_CHAR\r
+        * messages, should they crop up. So if someone wants to\r
+        * post the things to us as part of a macro manoeuvre,\r
+        * we're ready to cope.\r
+        */\r
+       {\r
+           char c = (unsigned char)wParam;\r
+           term_seen_key_event(term);\r
+           if (ldisc)\r
+               lpage_send(ldisc, CP_ACP, &c, 1, 1);\r
+       }\r
+       return 0;\r
+      case WM_SYSCOLORCHANGE:\r
+       if (cfg.system_colour) {\r
+           /* Refresh palette from system colours. */\r
+           /* XXX actually this zaps the entire palette. */\r
+           systopalette();\r
+           init_palette();\r
+           /* Force a repaint of the terminal window. */\r
+           term_invalidate(term);\r
+       }\r
+       break;\r
+      case WM_AGENT_CALLBACK:\r
+       {\r
+           struct agent_callback *c = (struct agent_callback *)lParam;\r
+           c->callback(c->callback_ctx, c->data, c->len);\r
+           sfree(c);\r
+       }\r
+       return 0;\r
+      case WM_GOT_CLIPDATA:\r
+       if (process_clipdata((HGLOBAL)lParam, wParam))\r
+           term_do_paste(term);\r
+       return 0;\r
+      default:\r
+       if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {\r
+           int shift_pressed=0, control_pressed=0;\r
+\r
+           if (message == WM_MOUSEWHEEL) {\r
+               wheel_accumulator += (short)HIWORD(wParam);\r
+               shift_pressed=LOWORD(wParam) & MK_SHIFT;\r
+               control_pressed=LOWORD(wParam) & MK_CONTROL;\r
+           } else {\r
+               BYTE keys[256];\r
+               wheel_accumulator += (int)wParam;\r
+               if (GetKeyboardState(keys)!=0) {\r
+                   shift_pressed=keys[VK_SHIFT]&0x80;\r
+                   control_pressed=keys[VK_CONTROL]&0x80;\r
+               }\r
+           }\r
+\r
+           /* process events when the threshold is reached */\r
+           while (abs(wheel_accumulator) >= WHEEL_DELTA) {\r
+               int b;\r
+\r
+               /* reduce amount for next time */\r
+               if (wheel_accumulator > 0) {\r
+                   b = MBT_WHEEL_UP;\r
+                   wheel_accumulator -= WHEEL_DELTA;\r
+               } else if (wheel_accumulator < 0) {\r
+                   b = MBT_WHEEL_DOWN;\r
+                   wheel_accumulator += WHEEL_DELTA;\r
+               } else\r
+                   break;\r
+\r
+               if (send_raw_mouse &&\r
+                   !(cfg.mouse_override && shift_pressed)) {\r
+                   /* Mouse wheel position is in screen coordinates for\r
+                    * some reason */\r
+                   POINT p;\r
+                   p.x = X_POS(lParam); p.y = Y_POS(lParam);\r
+                   if (ScreenToClient(hwnd, &p)) {\r
+                       /* send a mouse-down followed by a mouse up */\r
+                       term_mouse(term, b, translate_button(b),\r
+                                  MA_CLICK,\r
+                                  TO_CHR_X(p.x),\r
+                                  TO_CHR_Y(p.y), shift_pressed,\r
+                                  control_pressed, is_alt_pressed());\r
+                       term_mouse(term, b, translate_button(b),\r
+                                  MA_RELEASE, TO_CHR_X(p.x),\r
+                                  TO_CHR_Y(p.y), shift_pressed,\r
+                                  control_pressed, is_alt_pressed());\r
+                   } /* else: not sure when this can fail */\r
+               } else {\r
+                   /* trigger a scroll */\r
+                   term_scroll(term, 0,\r
+                               b == MBT_WHEEL_UP ?\r
+                               -term->rows / 2 : term->rows / 2);\r
+               }\r
+           }\r
+           return 0;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Any messages we don't process completely above are passed through to\r
+     * DefWindowProc() for default processing.\r
+     */\r
+    return DefWindowProc(hwnd, message, wParam, lParam);\r
+}\r
+\r
+/*\r
+ * Move the system caret. (We maintain one, even though it's\r
+ * invisible, for the benefit of blind people: apparently some\r
+ * helper software tracks the system caret, so we should arrange to\r
+ * have one.)\r
+ */\r
+void sys_cursor(void *frontend, int x, int y)\r
+{\r
+    int cx, cy;\r
+\r
+    if (!term->has_focus) return;\r
+\r
+    /*\r
+     * Avoid gratuitously re-updating the cursor position and IMM\r
+     * window if there's no actual change required.\r
+     */\r
+    cx = x * font_width + offset_width;\r
+    cy = y * font_height + offset_height;\r
+    if (cx == caret_x && cy == caret_y)\r
+       return;\r
+    caret_x = cx;\r
+    caret_y = cy;\r
+\r
+    sys_cursor_update();\r
+}\r
+\r
+static void sys_cursor_update(void)\r
+{\r
+    COMPOSITIONFORM cf;\r
+    HIMC hIMC;\r
+\r
+    if (!term->has_focus) return;\r
+\r
+    if (caret_x < 0 || caret_y < 0)\r
+       return;\r
+\r
+    SetCaretPos(caret_x, caret_y);\r
+\r
+    /* IMM calls on Win98 and beyond only */\r
+    if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */\r
+    \r
+    if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&\r
+           osVersion.dwMinorVersion == 0) return; /* 95 */\r
+\r
+    /* we should have the IMM functions */\r
+    hIMC = ImmGetContext(hwnd);\r
+    cf.dwStyle = CFS_POINT;\r
+    cf.ptCurrentPos.x = caret_x;\r
+    cf.ptCurrentPos.y = caret_y;\r
+    ImmSetCompositionWindow(hIMC, &cf);\r
+\r
+    ImmReleaseContext(hwnd, hIMC);\r
+}\r
+\r
+/*\r
+ * Draw a line of text in the window, at given character\r
+ * coordinates, in given attributes.\r
+ *\r
+ * We are allowed to fiddle with the contents of `text'.\r
+ */\r
+void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,\r
+                     unsigned long attr, int lattr)\r
+{\r
+    COLORREF fg, bg, t;\r
+    int nfg, nbg, nfont;\r
+    HDC hdc = ctx;\r
+    RECT line_box;\r
+    int force_manual_underline = 0;\r
+    int fnt_width, char_width;\r
+    int text_adjust = 0;\r
+    int xoffset = 0;\r
+    int maxlen, remaining, opaque;\r
+    static int *lpDx = NULL;\r
+    static int lpDx_len = 0;\r
+    int *lpDx_maybe;\r
+\r
+    lattr &= LATTR_MODE;\r
+\r
+    char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM));\r
+\r
+    if (attr & ATTR_WIDE)\r
+       char_width *= 2;\r
+\r
+    /* Only want the left half of double width lines */\r
+    if (lattr != LATTR_NORM && x*2 >= term->cols)\r
+       return;\r
+\r
+    x *= fnt_width;\r
+    y *= font_height;\r
+    x += offset_width;\r
+    y += offset_height;\r
+\r
+    if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {\r
+       attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS);\r
+       if (bold_mode == BOLD_COLOURS)\r
+           attr &= ~ATTR_BOLD;\r
+\r
+       /* cursor fg and bg */\r
+       attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT);\r
+    }\r
+\r
+    nfont = 0;\r
+    if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {\r
+       /* Assume a poorman font is borken in other ways too. */\r
+       lattr = LATTR_WIDE;\r
+    } else\r
+       switch (lattr) {\r
+         case LATTR_NORM:\r
+           break;\r
+         case LATTR_WIDE:\r
+           nfont |= FONT_WIDE;\r
+           break;\r
+         default:\r
+           nfont |= FONT_WIDE + FONT_HIGH;\r
+           break;\r
+       }\r
+    if (attr & ATTR_NARROW)\r
+       nfont |= FONT_NARROW;\r
+\r
+    /* Special hack for the VT100 linedraw glyphs. */\r
+    if (text[0] >= 0x23BA && text[0] <= 0x23BD) {\r
+       switch ((unsigned char) (text[0])) {\r
+         case 0xBA:\r
+           text_adjust = -2 * font_height / 5;\r
+           break;\r
+         case 0xBB:\r
+           text_adjust = -1 * font_height / 5;\r
+           break;\r
+         case 0xBC:\r
+           text_adjust = font_height / 5;\r
+           break;\r
+         case 0xBD:\r
+           text_adjust = 2 * font_height / 5;\r
+           break;\r
+       }\r
+       if (lattr == LATTR_TOP || lattr == LATTR_BOT)\r
+           text_adjust *= 2;\r
+       text[0] = ucsdata.unitab_xterm['q'];\r
+       if (attr & ATTR_UNDER) {\r
+           attr &= ~ATTR_UNDER;\r
+           force_manual_underline = 1;\r
+       }\r
+    }\r
+\r
+    /* Anything left as an original character set is unprintable. */\r
+    if (DIRECT_CHAR(text[0])) {\r
+       int i;\r
+       for (i = 0; i < len; i++)\r
+           text[i] = 0xFFFD;\r
+    }\r
+\r
+    /* OEM CP */\r
+    if ((text[0] & CSET_MASK) == CSET_OEMCP)\r
+       nfont |= FONT_OEM;\r
+\r
+    nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);\r
+    nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);\r
+    if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))\r
+       nfont |= FONT_BOLD;\r
+    if (und_mode == UND_FONT && (attr & ATTR_UNDER))\r
+       nfont |= FONT_UNDERLINE;\r
+    another_font(nfont);\r
+    if (!fonts[nfont]) {\r
+       if (nfont & FONT_UNDERLINE)\r
+           force_manual_underline = 1;\r
+       /* Don't do the same for manual bold, it could be bad news. */\r
+\r
+       nfont &= ~(FONT_BOLD | FONT_UNDERLINE);\r
+    }\r
+    another_font(nfont);\r
+    if (!fonts[nfont])\r
+       nfont = FONT_NORMAL;\r
+    if (attr & ATTR_REVERSE) {\r
+       t = nfg;\r
+       nfg = nbg;\r
+       nbg = t;\r
+    }\r
+    if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) {\r
+       if (nfg < 16) nfg |= 8;\r
+       else if (nfg >= 256) nfg |= 1;\r
+    }\r
+    if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) {\r
+       if (nbg < 16) nbg |= 8;\r
+       else if (nbg >= 256) nbg |= 1;\r
+    }\r
+    fg = colours[nfg];\r
+    bg = colours[nbg];\r
+    SelectObject(hdc, fonts[nfont]);\r
+    SetTextColor(hdc, fg);\r
+    SetBkColor(hdc, bg);\r
+    if (attr & TATTR_COMBINING)\r
+       SetBkMode(hdc, TRANSPARENT);\r
+    else\r
+       SetBkMode(hdc, OPAQUE);\r
+    line_box.left = x;\r
+    line_box.top = y;\r
+    line_box.right = x + char_width * len;\r
+    line_box.bottom = y + font_height;\r
+\r
+    /* Only want the left half of double width lines */\r
+    if (line_box.right > font_width*term->cols+offset_width)\r
+       line_box.right = font_width*term->cols+offset_width;\r
+\r
+    if (font_varpitch) {\r
+        /*\r
+         * If we're using a variable-pitch font, we unconditionally\r
+         * draw the glyphs one at a time and centre them in their\r
+         * character cells (which means in particular that we must\r
+         * disable the lpDx mechanism). This gives slightly odd but\r
+         * generally reasonable results.\r
+         */\r
+        xoffset = char_width / 2;\r
+        SetTextAlign(hdc, TA_TOP | TA_CENTER | TA_NOUPDATECP);\r
+        lpDx_maybe = NULL;\r
+        maxlen = 1;\r
+    } else {\r
+        /*\r
+         * In a fixed-pitch font, we draw the whole string in one go\r
+         * in the normal way.\r
+         */\r
+        xoffset = 0;\r
+        SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);\r
+        lpDx_maybe = lpDx;\r
+        maxlen = len;\r
+    }\r
+\r
+    opaque = TRUE;                     /* start by erasing the rectangle */\r
+    for (remaining = len; remaining > 0;\r
+         text += len, remaining -= len, x += char_width * len) {\r
+        len = (maxlen < remaining ? maxlen : remaining);\r
+\r
+        if (len > lpDx_len) {\r
+            if (len > lpDx_len) {\r
+                lpDx_len = len * 9 / 8 + 16;\r
+                lpDx = sresize(lpDx, lpDx_len, int);\r
+            }\r
+        }\r
+        {\r
+            int i;\r
+            for (i = 0; i < len; i++)\r
+                lpDx[i] = char_width;\r
+        }\r
+\r
+        /* We're using a private area for direct to font. (512 chars.) */\r
+        if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) {\r
+            /* Ho Hum, dbcs fonts are a PITA! */\r
+            /* To display on W9x I have to convert to UCS */\r
+            static wchar_t *uni_buf = 0;\r
+            static int uni_len = 0;\r
+            int nlen, mptr;\r
+            if (len > uni_len) {\r
+                sfree(uni_buf);\r
+                uni_len = len;\r
+                uni_buf = snewn(uni_len, wchar_t);\r
+            }\r
+\r
+            for(nlen = mptr = 0; mptr<len; mptr++) {\r
+                uni_buf[nlen] = 0xFFFD;\r
+                if (IsDBCSLeadByteEx(ucsdata.font_codepage,\r
+                                     (BYTE) text[mptr])) {\r
+                    char dbcstext[2];\r
+                    dbcstext[0] = text[mptr] & 0xFF;\r
+                    dbcstext[1] = text[mptr+1] & 0xFF;\r
+                    lpDx[nlen] += char_width;\r
+                    MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,\r
+                                        dbcstext, 2, uni_buf+nlen, 1);\r
+                    mptr++;\r
+                }\r
+                else\r
+                {\r
+                    char dbcstext[1];\r
+                    dbcstext[0] = text[mptr] & 0xFF;\r
+                    MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,\r
+                                        dbcstext, 1, uni_buf+nlen, 1);\r
+                }\r
+                nlen++;\r
+            }\r
+            if (nlen <= 0)\r
+                return;                       /* Eeek! */\r
+\r
+            ExtTextOutW(hdc, x + xoffset,\r
+                        y - font_height * (lattr == LATTR_BOT) + text_adjust,\r
+                        ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),\r
+                        &line_box, uni_buf, nlen,\r
+                        lpDx_maybe);\r
+            if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {\r
+                SetBkMode(hdc, TRANSPARENT);\r
+                ExtTextOutW(hdc, x + xoffset - 1,\r
+                            y - font_height * (lattr ==\r
+                                               LATTR_BOT) + text_adjust,\r
+                            ETO_CLIPPED, &line_box, uni_buf, nlen, lpDx_maybe);\r
+            }\r
+\r
+            lpDx[0] = -1;\r
+        } else if (DIRECT_FONT(text[0])) {\r
+            static char *directbuf = NULL;\r
+            static int directlen = 0;\r
+            int i;\r
+            if (len > directlen) {\r
+                directlen = len;\r
+                directbuf = sresize(directbuf, directlen, char);\r
+            }\r
+\r
+            for (i = 0; i < len; i++)\r
+                directbuf[i] = text[i] & 0xFF;\r
+\r
+            ExtTextOut(hdc, x + xoffset,\r
+                       y - font_height * (lattr == LATTR_BOT) + text_adjust,\r
+                       ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),\r
+                       &line_box, directbuf, len, lpDx_maybe);\r
+            if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {\r
+                SetBkMode(hdc, TRANSPARENT);\r
+\r
+                /* GRR: This draws the character outside its box and\r
+                 * can leave 'droppings' even with the clip box! I\r
+                 * suppose I could loop it one character at a time ...\r
+                 * yuk.\r
+                 * \r
+                 * Or ... I could do a test print with "W", and use +1\r
+                 * or -1 for this shift depending on if the leftmost\r
+                 * column is blank...\r
+                 */\r
+                ExtTextOut(hdc, x + xoffset - 1,\r
+                           y - font_height * (lattr ==\r
+                                              LATTR_BOT) + text_adjust,\r
+                           ETO_CLIPPED, &line_box, directbuf, len, lpDx_maybe);\r
+            }\r
+        } else {\r
+            /* And 'normal' unicode characters */\r
+            static WCHAR *wbuf = NULL;\r
+            static int wlen = 0;\r
+            int i;\r
+\r
+            if (wlen < len) {\r
+                sfree(wbuf);\r
+                wlen = len;\r
+                wbuf = snewn(wlen, WCHAR);\r
+            }\r
+\r
+            for (i = 0; i < len; i++)\r
+                wbuf[i] = text[i];\r
+\r
+            /* print Glyphs as they are, without Windows' Shaping*/\r
+            general_textout(hdc, x + xoffset,\r
+                            y - font_height * (lattr==LATTR_BOT) + text_adjust,\r
+                            &line_box, wbuf, len, lpDx,\r
+                            opaque && !(attr & TATTR_COMBINING));\r
+\r
+            /* And the shadow bold hack. */\r
+            if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {\r
+                SetBkMode(hdc, TRANSPARENT);\r
+                ExtTextOutW(hdc, x + xoffset - 1,\r
+                            y - font_height * (lattr ==\r
+                                               LATTR_BOT) + text_adjust,\r
+                            ETO_CLIPPED, &line_box, wbuf, len, lpDx_maybe);\r
+            }\r
+        }\r
+\r
+        /*\r
+         * If we're looping round again, stop erasing the background\r
+         * rectangle.\r
+         */\r
+        SetBkMode(hdc, TRANSPARENT);\r
+        opaque = FALSE;\r
+    }\r
+    if (lattr != LATTR_TOP && (force_manual_underline ||\r
+                              (und_mode == UND_LINE\r
+                               && (attr & ATTR_UNDER)))) {\r
+       HPEN oldpen;\r
+       int dec = descent;\r
+       if (lattr == LATTR_BOT)\r
+           dec = dec * 2 - font_height;\r
+\r
+       oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));\r
+       MoveToEx(hdc, x, y + dec, NULL);\r
+       LineTo(hdc, x + len * char_width, y + dec);\r
+       oldpen = SelectObject(hdc, oldpen);\r
+       DeleteObject(oldpen);\r
+    }\r
+}\r
+\r
+/*\r
+ * Wrapper that handles combining characters.\r
+ */\r
+void do_text(Context ctx, int x, int y, wchar_t *text, int len,\r
+            unsigned long attr, int lattr)\r
+{\r
+    if (attr & TATTR_COMBINING) {\r
+       unsigned long a = 0;\r
+       attr &= ~TATTR_COMBINING;\r
+       while (len--) {\r
+           do_text_internal(ctx, x, y, text, 1, attr | a, lattr);\r
+           text++;\r
+           a = TATTR_COMBINING;\r
+       }\r
+    } else\r
+       do_text_internal(ctx, x, y, text, len, attr, lattr);\r
+}\r
+\r
+void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,\r
+              unsigned long attr, int lattr)\r
+{\r
+\r
+    int fnt_width;\r
+    int char_width;\r
+    HDC hdc = ctx;\r
+    int ctype = cfg.cursor_type;\r
+\r
+    lattr &= LATTR_MODE;\r
+\r
+    if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {\r
+       if (*text != UCSWIDE) {\r
+           do_text(ctx, x, y, text, len, attr, lattr);\r
+           return;\r
+       }\r
+       ctype = 2;\r
+       attr |= TATTR_RIGHTCURS;\r
+    }\r
+\r
+    fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));\r
+    if (attr & ATTR_WIDE)\r
+       char_width *= 2;\r
+    x *= fnt_width;\r
+    y *= font_height;\r
+    x += offset_width;\r
+    y += offset_height;\r
+\r
+    if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {\r
+       POINT pts[5];\r
+       HPEN oldpen;\r
+       pts[0].x = pts[1].x = pts[4].x = x;\r
+       pts[2].x = pts[3].x = x + char_width - 1;\r
+       pts[0].y = pts[3].y = pts[4].y = y;\r
+       pts[1].y = pts[2].y = y + font_height - 1;\r
+       oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));\r
+       Polyline(hdc, pts, 5);\r
+       oldpen = SelectObject(hdc, oldpen);\r
+       DeleteObject(oldpen);\r
+    } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {\r
+       int startx, starty, dx, dy, length, i;\r
+       if (ctype == 1) {\r
+           startx = x;\r
+           starty = y + descent;\r
+           dx = 1;\r
+           dy = 0;\r
+           length = char_width;\r
+       } else {\r
+           int xadjust = 0;\r
+           if (attr & TATTR_RIGHTCURS)\r
+               xadjust = char_width - 1;\r
+           startx = x + xadjust;\r
+           starty = y;\r
+           dx = 0;\r
+           dy = 1;\r
+           length = font_height;\r
+       }\r
+       if (attr & TATTR_ACTCURS) {\r
+           HPEN oldpen;\r
+           oldpen =\r
+               SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));\r
+           MoveToEx(hdc, startx, starty, NULL);\r
+           LineTo(hdc, startx + dx * length, starty + dy * length);\r
+           oldpen = SelectObject(hdc, oldpen);\r
+           DeleteObject(oldpen);\r
+       } else {\r
+           for (i = 0; i < length; i++) {\r
+               if (i % 2 == 0) {\r
+                   SetPixel(hdc, startx, starty, colours[261]);\r
+               }\r
+               startx += dx;\r
+               starty += dy;\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+/* This function gets the actual width of a character in the normal font.\r
+ */\r
+int char_width(Context ctx, int uc) {\r
+    HDC hdc = ctx;\r
+    int ibuf = 0;\r
+\r
+    /* If the font max is the same as the font ave width then this\r
+     * function is a no-op.\r
+     */\r
+    if (!font_dualwidth) return 1;\r
+\r
+    switch (uc & CSET_MASK) {\r
+      case CSET_ASCII:\r
+       uc = ucsdata.unitab_line[uc & 0xFF];\r
+       break;\r
+      case CSET_LINEDRW:\r
+       uc = ucsdata.unitab_xterm[uc & 0xFF];\r
+       break;\r
+      case CSET_SCOACS:\r
+       uc = ucsdata.unitab_scoacs[uc & 0xFF];\r
+       break;\r
+    }\r
+    if (DIRECT_FONT(uc)) {\r
+       if (ucsdata.dbcs_screenfont) return 1;\r
+\r
+       /* Speedup, I know of no font where ascii is the wrong width */\r
+       if ((uc&~CSET_MASK) >= ' ' && (uc&~CSET_MASK)<= '~')\r
+           return 1;\r
+\r
+       if ( (uc & CSET_MASK) == CSET_ACP ) {\r
+           SelectObject(hdc, fonts[FONT_NORMAL]);\r
+       } else if ( (uc & CSET_MASK) == CSET_OEMCP ) {\r
+           another_font(FONT_OEM);\r
+           if (!fonts[FONT_OEM]) return 0;\r
+\r
+           SelectObject(hdc, fonts[FONT_OEM]);\r
+       } else\r
+           return 0;\r
+\r
+       if ( GetCharWidth32(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1 &&\r
+            GetCharWidth(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1)\r
+           return 0;\r
+    } else {\r
+       /* Speedup, I know of no font where ascii is the wrong width */\r
+       if (uc >= ' ' && uc <= '~') return 1;\r
+\r
+       SelectObject(hdc, fonts[FONT_NORMAL]);\r
+       if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )\r
+           /* Okay that one worked */ ;\r
+       else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )\r
+           /* This should work on 9x too, but it's "less accurate" */ ;\r
+       else\r
+           return 0;\r
+    }\r
+\r
+    ibuf += font_width / 2 -1;\r
+    ibuf /= font_width;\r
+\r
+    return ibuf;\r
+}\r
+\r
+/*\r
+ * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII\r
+ * codes. Returns number of bytes used, zero to drop the message,\r
+ * -1 to forward the message to Windows, or another negative number\r
+ * to indicate a NUL-terminated "special" string.\r
+ */\r
+static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,\r
+                       unsigned char *output)\r
+{\r
+    BYTE keystate[256];\r
+    int scan, left_alt = 0, key_down, shift_state;\r
+    int r, i, code;\r
+    unsigned char *p = output;\r
+    static int alt_sum = 0;\r
+\r
+    HKL kbd_layout = GetKeyboardLayout(0);\r
+\r
+    /* keys is for ToAsciiEx. There's some ick here, see below. */\r
+    static WORD keys[3];\r
+    static int compose_char = 0;\r
+    static WPARAM compose_key = 0;\r
+\r
+    r = GetKeyboardState(keystate);\r
+    if (!r)\r
+       memset(keystate, 0, sizeof(keystate));\r
+    else {\r
+#if 0\r
+#define SHOW_TOASCII_RESULT\r
+       {                              /* Tell us all about key events */\r
+           static BYTE oldstate[256];\r
+           static int first = 1;\r
+           static int scan;\r
+           int ch;\r
+           if (first)\r
+               memcpy(oldstate, keystate, sizeof(oldstate));\r
+           first = 0;\r
+\r
+           if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {\r
+               debug(("+"));\r
+           } else if ((HIWORD(lParam) & KF_UP)\r
+                      && scan == (HIWORD(lParam) & 0xFF)) {\r
+               debug((". U"));\r
+           } else {\r
+               debug((".\n"));\r
+               if (wParam >= VK_F1 && wParam <= VK_F20)\r
+                   debug(("K_F%d", wParam + 1 - VK_F1));\r
+               else\r
+                   switch (wParam) {\r
+                     case VK_SHIFT:\r
+                       debug(("SHIFT"));\r
+                       break;\r
+                     case VK_CONTROL:\r
+                       debug(("CTRL"));\r
+                       break;\r
+                     case VK_MENU:\r
+                       debug(("ALT"));\r
+                       break;\r
+                     default:\r
+                       debug(("VK_%02x", wParam));\r
+                   }\r
+               if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)\r
+                   debug(("*"));\r
+               debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));\r
+\r
+               ch = MapVirtualKeyEx(wParam, 2, kbd_layout);\r
+               if (ch >= ' ' && ch <= '~')\r
+                   debug((", '%c'", ch));\r
+               else if (ch)\r
+                   debug((", $%02x", ch));\r
+\r
+               if (keys[0])\r
+                   debug((", KB0=%02x", keys[0]));\r
+               if (keys[1])\r
+                   debug((", KB1=%02x", keys[1]));\r
+               if (keys[2])\r
+                   debug((", KB2=%02x", keys[2]));\r
+\r
+               if ((keystate[VK_SHIFT] & 0x80) != 0)\r
+                   debug((", S"));\r
+               if ((keystate[VK_CONTROL] & 0x80) != 0)\r
+                   debug((", C"));\r
+               if ((HIWORD(lParam) & KF_EXTENDED))\r
+                   debug((", E"));\r
+               if ((HIWORD(lParam) & KF_UP))\r
+                   debug((", U"));\r
+           }\r
+\r
+           if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);\r
+           else if ((HIWORD(lParam) & KF_UP))\r
+               oldstate[wParam & 0xFF] ^= 0x80;\r
+           else\r
+               oldstate[wParam & 0xFF] ^= 0x81;\r
+\r
+           for (ch = 0; ch < 256; ch++)\r
+               if (oldstate[ch] != keystate[ch])\r
+                   debug((", M%02x=%02x", ch, keystate[ch]));\r
+\r
+           memcpy(oldstate, keystate, sizeof(oldstate));\r
+       }\r
+#endif\r
+\r
+       if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {\r
+           keystate[VK_RMENU] = keystate[VK_MENU];\r
+       }\r
+\r
+\r
+       /* Nastyness with NUMLock - Shift-NUMLock is left alone though */\r
+       if ((cfg.funky_type == FUNKY_VT400 ||\r
+            (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys &&\r
+             !cfg.no_applic_k))\r
+           && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {\r
+\r
+           wParam = VK_EXECUTE;\r
+\r
+           /* UnToggle NUMLock */\r
+           if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)\r
+               keystate[VK_NUMLOCK] ^= 1;\r
+       }\r
+\r
+       /* And write back the 'adjusted' state */\r
+       SetKeyboardState(keystate);\r
+    }\r
+\r
+    /* Disable Auto repeat if required */\r
+    if (term->repeat_off &&\r
+       (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)\r
+       return 0;\r
+\r
+    if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)\r
+       left_alt = 1;\r
+\r
+    key_down = ((HIWORD(lParam) & KF_UP) == 0);\r
+\r
+    /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */\r
+    if (left_alt && (keystate[VK_CONTROL] & 0x80)) {\r
+       if (cfg.ctrlaltkeys)\r
+           keystate[VK_MENU] = 0;\r
+       else {\r
+           keystate[VK_RMENU] = 0x80;\r
+           left_alt = 0;\r
+       }\r
+    }\r
+\r
+    scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));\r
+    shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)\r
+       + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;\r
+\r
+    /* Note if AltGr was pressed and if it was used as a compose key */\r
+    if (!compose_state) {\r
+       compose_key = 0x100;\r
+       if (cfg.compose_key) {\r
+           if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))\r
+               compose_key = wParam;\r
+       }\r
+       if (wParam == VK_APPS)\r
+           compose_key = wParam;\r
+    }\r
+\r
+    if (wParam == compose_key) {\r
+       if (compose_state == 0\r
+           && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =\r
+               1;\r
+       else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))\r
+           compose_state = 2;\r
+       else\r
+           compose_state = 0;\r
+    } else if (compose_state == 1 && wParam != VK_CONTROL)\r
+       compose_state = 0;\r
+\r
+    if (compose_state > 1 && left_alt)\r
+       compose_state = 0;\r
+\r
+    /* Sanitize the number pad if not using a PC NumPad */\r
+    if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k\r
+                    && cfg.funky_type != FUNKY_XTERM)\r
+       || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) {\r
+       if ((HIWORD(lParam) & KF_EXTENDED) == 0) {\r
+           int nParam = 0;\r
+           switch (wParam) {\r
+             case VK_INSERT:\r
+               nParam = VK_NUMPAD0;\r
+               break;\r
+             case VK_END:\r
+               nParam = VK_NUMPAD1;\r
+               break;\r
+             case VK_DOWN:\r
+               nParam = VK_NUMPAD2;\r
+               break;\r
+             case VK_NEXT:\r
+               nParam = VK_NUMPAD3;\r
+               break;\r
+             case VK_LEFT:\r
+               nParam = VK_NUMPAD4;\r
+               break;\r
+             case VK_CLEAR:\r
+               nParam = VK_NUMPAD5;\r
+               break;\r
+             case VK_RIGHT:\r
+               nParam = VK_NUMPAD6;\r
+               break;\r
+             case VK_HOME:\r
+               nParam = VK_NUMPAD7;\r
+               break;\r
+             case VK_UP:\r
+               nParam = VK_NUMPAD8;\r
+               break;\r
+             case VK_PRIOR:\r
+               nParam = VK_NUMPAD9;\r
+               break;\r
+             case VK_DELETE:\r
+               nParam = VK_DECIMAL;\r
+               break;\r
+           }\r
+           if (nParam) {\r
+               if (keystate[VK_NUMLOCK] & 1)\r
+                   shift_state |= 1;\r
+               wParam = nParam;\r
+           }\r
+       }\r
+    }\r
+\r
+    /* If a key is pressed and AltGr is not active */\r
+    if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {\r
+       /* Okay, prepare for most alts then ... */\r
+       if (left_alt)\r
+           *p++ = '\033';\r
+\r
+       /* Lets see if it's a pattern we know all about ... */\r
+       if (wParam == VK_PRIOR && shift_state == 1) {\r
+           SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);\r
+           return 0;\r
+       }\r
+       if (wParam == VK_PRIOR && shift_state == 2) {\r
+           SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);\r
+           return 0;\r
+       }\r
+       if (wParam == VK_NEXT && shift_state == 1) {\r
+           SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);\r
+           return 0;\r
+       }\r
+       if (wParam == VK_NEXT && shift_state == 2) {\r
+           SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);\r
+           return 0;\r
+       }\r
+       if ((wParam == VK_PRIOR || wParam == VK_NEXT) && shift_state == 3) {\r
+           term_scroll_to_selection(term, (wParam == VK_PRIOR ? 0 : 1));\r
+           return 0;\r
+       }\r
+       if (wParam == VK_INSERT && shift_state == 1) {\r
+           request_paste(NULL);\r
+           return 0;\r
+       }\r
+       if (left_alt && wParam == VK_F4 && cfg.alt_f4) {\r
+           return -1;\r
+       }\r
+       if (left_alt && wParam == VK_SPACE && cfg.alt_space) {\r
+           SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);\r
+           return -1;\r
+       }\r
+       if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&\r
+           (cfg.resize_action != RESIZE_DISABLED)) {\r
+           if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)\r
+               flip_full_screen();\r
+           return -1;\r
+       }\r
+       /* Control-Numlock for app-keypad mode switch */\r
+       if (wParam == VK_PAUSE && shift_state == 2) {\r
+           term->app_keypad_keys ^= 1;\r
+           return 0;\r
+       }\r
+\r
+       /* Nethack keypad */\r
+       if (cfg.nethack_keypad && !left_alt) {\r
+           switch (wParam) {\r
+             case VK_NUMPAD1:\r
+               *p++ = "bB\002\002"[shift_state & 3];\r
+               return p - output;\r
+             case VK_NUMPAD2:\r
+               *p++ = "jJ\012\012"[shift_state & 3];\r
+               return p - output;\r
+             case VK_NUMPAD3:\r
+               *p++ = "nN\016\016"[shift_state & 3];\r
+               return p - output;\r
+             case VK_NUMPAD4:\r
+               *p++ = "hH\010\010"[shift_state & 3];\r
+               return p - output;\r
+             case VK_NUMPAD5:\r
+               *p++ = shift_state ? '.' : '.';\r
+               return p - output;\r
+             case VK_NUMPAD6:\r
+               *p++ = "lL\014\014"[shift_state & 3];\r
+               return p - output;\r
+             case VK_NUMPAD7:\r
+               *p++ = "yY\031\031"[shift_state & 3];\r
+               return p - output;\r
+             case VK_NUMPAD8:\r
+               *p++ = "kK\013\013"[shift_state & 3];\r
+               return p - output;\r
+             case VK_NUMPAD9:\r
+               *p++ = "uU\025\025"[shift_state & 3];\r
+               return p - output;\r
+           }\r
+       }\r
+\r
+       /* Application Keypad */\r
+       if (!left_alt) {\r
+           int xkey = 0;\r
+\r
+           if (cfg.funky_type == FUNKY_VT400 ||\r
+               (cfg.funky_type <= FUNKY_LINUX &&\r
+                term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {\r
+                 case VK_EXECUTE:\r
+                   xkey = 'P';\r
+                   break;\r
+                 case VK_DIVIDE:\r
+                   xkey = 'Q';\r
+                   break;\r
+                 case VK_MULTIPLY:\r
+                   xkey = 'R';\r
+                   break;\r
+                 case VK_SUBTRACT:\r
+                   xkey = 'S';\r
+                   break;\r
+               }\r
+           if (term->app_keypad_keys && !cfg.no_applic_k)\r
+               switch (wParam) {\r
+                 case VK_NUMPAD0:\r
+                   xkey = 'p';\r
+                   break;\r
+                 case VK_NUMPAD1:\r
+                   xkey = 'q';\r
+                   break;\r
+                 case VK_NUMPAD2:\r
+                   xkey = 'r';\r
+                   break;\r
+                 case VK_NUMPAD3:\r
+                   xkey = 's';\r
+                   break;\r
+                 case VK_NUMPAD4:\r
+                   xkey = 't';\r
+                   break;\r
+                 case VK_NUMPAD5:\r
+                   xkey = 'u';\r
+                   break;\r
+                 case VK_NUMPAD6:\r
+                   xkey = 'v';\r
+                   break;\r
+                 case VK_NUMPAD7:\r
+                   xkey = 'w';\r
+                   break;\r
+                 case VK_NUMPAD8:\r
+                   xkey = 'x';\r
+                   break;\r
+                 case VK_NUMPAD9:\r
+                   xkey = 'y';\r
+                   break;\r
+\r
+                 case VK_DECIMAL:\r
+                   xkey = 'n';\r
+                   break;\r
+                 case VK_ADD:\r
+                   if (cfg.funky_type == FUNKY_XTERM) {\r
+                       if (shift_state)\r
+                           xkey = 'l';\r
+                       else\r
+                           xkey = 'k';\r
+                   } else if (shift_state)\r
+                       xkey = 'm';\r
+                   else\r
+                       xkey = 'l';\r
+                   break;\r
+\r
+                 case VK_DIVIDE:\r
+                   if (cfg.funky_type == FUNKY_XTERM)\r
+                       xkey = 'o';\r
+                   break;\r
+                 case VK_MULTIPLY:\r
+                   if (cfg.funky_type == FUNKY_XTERM)\r
+                       xkey = 'j';\r
+                   break;\r
+                 case VK_SUBTRACT:\r
+                   if (cfg.funky_type == FUNKY_XTERM)\r
+                       xkey = 'm';\r
+                   break;\r
+\r
+                 case VK_RETURN:\r
+                   if (HIWORD(lParam) & KF_EXTENDED)\r
+                       xkey = 'M';\r
+                   break;\r
+               }\r
+           if (xkey) {\r
+               if (term->vt52_mode) {\r
+                   if (xkey >= 'P' && xkey <= 'S')\r
+                       p += sprintf((char *) p, "\x1B%c", xkey);\r
+                   else\r
+                       p += sprintf((char *) p, "\x1B?%c", xkey);\r
+               } else\r
+                   p += sprintf((char *) p, "\x1BO%c", xkey);\r
+               return p - output;\r
+           }\r
+       }\r
+\r
+       if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */\r
+           *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);\r
+           *p++ = 0;\r
+           return -2;\r
+       }\r
+       if (wParam == VK_BACK && shift_state == 1) {    /* Shift Backspace */\r
+           /* We do the opposite of what is configured */\r
+           *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);\r
+           *p++ = 0;\r
+           return -2;\r
+       }\r
+       if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */\r
+           *p++ = 0x1B;\r
+           *p++ = '[';\r
+           *p++ = 'Z';\r
+           return p - output;\r
+       }\r
+       if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */\r
+           *p++ = 0;\r
+           return p - output;\r
+       }\r
+       if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */\r
+           *p++ = 160;\r
+           return p - output;\r
+       }\r
+       if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */\r
+           if (back)\r
+               back->special(backhandle, TS_BRK);\r
+           return 0;\r
+       }\r
+       if (wParam == VK_PAUSE) {      /* Break/Pause */\r
+           *p++ = 26;\r
+           *p++ = 0;\r
+           return -2;\r
+       }\r
+       /* Control-2 to Control-8 are special */\r
+       if (shift_state == 2 && wParam >= '2' && wParam <= '8') {\r
+           *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];\r
+           return p - output;\r
+       }\r
+       if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {\r
+           *p++ = 0x1F;\r
+           return p - output;\r
+       }\r
+       if (shift_state == 2 && (wParam == 0xDF || wParam == 0xDC)) {\r
+           *p++ = 0x1C;\r
+           return p - output;\r
+       }\r
+       if (shift_state == 3 && wParam == 0xDE) {\r
+           *p++ = 0x1E;               /* Ctrl-~ == Ctrl-^ in xterm at least */\r
+           return p - output;\r
+       }\r
+       if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {\r
+           *p++ = '\r';\r
+           *p++ = '\n';\r
+           return p - output;\r
+       }\r
+\r
+       /*\r
+        * Next, all the keys that do tilde codes. (ESC '[' nn '~',\r
+        * for integer decimal nn.)\r
+        *\r
+        * We also deal with the weird ones here. Linux VCs replace F1\r
+        * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but\r
+        * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w\r
+        * respectively.\r
+        */\r
+       code = 0;\r
+       switch (wParam) {\r
+         case VK_F1:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);\r
+           break;\r
+         case VK_F2:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);\r
+           break;\r
+         case VK_F3:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);\r
+           break;\r
+         case VK_F4:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);\r
+           break;\r
+         case VK_F5:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);\r
+           break;\r
+         case VK_F6:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);\r
+           break;\r
+         case VK_F7:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);\r
+           break;\r
+         case VK_F8:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);\r
+           break;\r
+         case VK_F9:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);\r
+           break;\r
+         case VK_F10:\r
+           code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);\r
+           break;\r
+         case VK_F11:\r
+           code = 23;\r
+           break;\r
+         case VK_F12:\r
+           code = 24;\r
+           break;\r
+         case VK_F13:\r
+           code = 25;\r
+           break;\r
+         case VK_F14:\r
+           code = 26;\r
+           break;\r
+         case VK_F15:\r
+           code = 28;\r
+           break;\r
+         case VK_F16:\r
+           code = 29;\r
+           break;\r
+         case VK_F17:\r
+           code = 31;\r
+           break;\r
+         case VK_F18:\r
+           code = 32;\r
+           break;\r
+         case VK_F19:\r
+           code = 33;\r
+           break;\r
+         case VK_F20:\r
+           code = 34;\r
+           break;\r
+       }\r
+       if ((shift_state&2) == 0) switch (wParam) {\r
+         case VK_HOME:\r
+           code = 1;\r
+           break;\r
+         case VK_INSERT:\r
+           code = 2;\r
+           break;\r
+         case VK_DELETE:\r
+           code = 3;\r
+           break;\r
+         case VK_END:\r
+           code = 4;\r
+           break;\r
+         case VK_PRIOR:\r
+           code = 5;\r
+           break;\r
+         case VK_NEXT:\r
+           code = 6;\r
+           break;\r
+       }\r
+       /* Reorder edit keys to physical order */\r
+       if (cfg.funky_type == FUNKY_VT400 && code <= 6)\r
+           code = "\0\2\1\4\5\3\6"[code];\r
+\r
+       if (term->vt52_mode && code > 0 && code <= 6) {\r
+           p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);\r
+           return p - output;\r
+       }\r
+\r
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */\r
+           code >= 11 && code <= 34) {\r
+           char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";\r
+           int index = 0;\r
+           switch (wParam) {\r
+             case VK_F1: index = 0; break;\r
+             case VK_F2: index = 1; break;\r
+             case VK_F3: index = 2; break;\r
+             case VK_F4: index = 3; break;\r
+             case VK_F5: index = 4; break;\r
+             case VK_F6: index = 5; break;\r
+             case VK_F7: index = 6; break;\r
+             case VK_F8: index = 7; break;\r
+             case VK_F9: index = 8; break;\r
+             case VK_F10: index = 9; break;\r
+             case VK_F11: index = 10; break;\r
+             case VK_F12: index = 11; break;\r
+           }\r
+           if (keystate[VK_SHIFT] & 0x80) index += 12;\r
+           if (keystate[VK_CONTROL] & 0x80) index += 24;\r
+           p += sprintf((char *) p, "\x1B[%c", codes[index]);\r
+           return p - output;\r
+       }\r
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */\r
+           code >= 1 && code <= 6) {\r
+           char codes[] = "HL.FIG";\r
+           if (code == 3) {\r
+               *p++ = '\x7F';\r
+           } else {\r
+               p += sprintf((char *) p, "\x1B[%c", codes[code-1]);\r
+           }\r
+           return p - output;\r
+       }\r
+       if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {\r
+           int offt = 0;\r
+           if (code > 15)\r
+               offt++;\r
+           if (code > 21)\r
+               offt++;\r
+           if (term->vt52_mode)\r
+               p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);\r
+           else\r
+               p +=\r
+                   sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);\r
+           return p - output;\r
+       }\r
+       if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {\r
+           p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);\r
+           return p - output;\r
+       }\r
+       if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {\r
+           if (term->vt52_mode)\r
+               p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);\r
+           else\r
+               p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);\r
+           return p - output;\r
+       }\r
+       if (cfg.rxvt_homeend && (code == 1 || code == 4)) {\r
+           p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");\r
+           return p - output;\r
+       }\r
+       if (code) {\r
+           p += sprintf((char *) p, "\x1B[%d~", code);\r
+           return p - output;\r
+       }\r
+\r
+       /*\r
+        * Now the remaining keys (arrows and Keypad 5. Keypad 5 for\r
+        * some reason seems to send VK_CLEAR to Windows...).\r
+        */\r
+       {\r
+           char xkey = 0;\r
+           switch (wParam) {\r
+             case VK_UP:\r
+               xkey = 'A';\r
+               break;\r
+             case VK_DOWN:\r
+               xkey = 'B';\r
+               break;\r
+             case VK_RIGHT:\r
+               xkey = 'C';\r
+               break;\r
+             case VK_LEFT:\r
+               xkey = 'D';\r
+               break;\r
+             case VK_CLEAR:\r
+               xkey = 'G';\r
+               break;\r
+           }\r
+           if (xkey) {\r
+               p += format_arrow_key(p, term, xkey, shift_state);\r
+               return p - output;\r
+           }\r
+       }\r
+\r
+       /*\r
+        * Finally, deal with Return ourselves. (Win95 seems to\r
+        * foul it up when Alt is pressed, for some reason.)\r
+        */\r
+       if (wParam == VK_RETURN) {     /* Return */\r
+           *p++ = 0x0D;\r
+           *p++ = 0;\r
+           return -2;\r
+       }\r
+\r
+       if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)\r
+           alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;\r
+       else\r
+           alt_sum = 0;\r
+    }\r
+\r
+    /* Okay we've done everything interesting; let windows deal with \r
+     * the boring stuff */\r
+    {\r
+       BOOL capsOn=0;\r
+\r
+       /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */\r
+       if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {\r
+           capsOn= !left_alt;\r
+           keystate[VK_CAPITAL] = 0;\r
+       }\r
+\r
+       /* XXX how do we know what the max size of the keys array should\r
+        * be is? There's indication on MS' website of an Inquire/InquireEx\r
+        * functioning returning a KBINFO structure which tells us. */\r
+       if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {\r
+           /* XXX 'keys' parameter is declared in MSDN documentation as\r
+            * 'LPWORD lpChar'.\r
+            * The experience of a French user indicates that on\r
+            * Win98, WORD[] should be passed in, but on Win2K, it should\r
+            * be BYTE[]. German WinXP and my Win2K with "US International"\r
+            * driver corroborate this.\r
+            * Experimentally I've conditionalised the behaviour on the\r
+            * Win9x/NT split, but I suspect it's worse than that.\r
+            * See wishlist item `win-dead-keys' for more horrible detail\r
+            * and speculations. */\r
+           BYTE keybs[3];\r
+           int i;\r
+           r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout);\r
+           for (i=0; i<3; i++) keys[i] = keybs[i];\r
+       } else {\r
+           r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);\r
+       }\r
+#ifdef SHOW_TOASCII_RESULT\r
+       if (r == 1 && !key_down) {\r
+           if (alt_sum) {\r
+               if (in_utf(term) || ucsdata.dbcs_screenfont)\r
+                   debug((", (U+%04x)", alt_sum));\r
+               else\r
+                   debug((", LCH(%d)", alt_sum));\r
+           } else {\r
+               debug((", ACH(%d)", keys[0]));\r
+           }\r
+       } else if (r > 0) {\r
+           int r1;\r
+           debug((", ASC("));\r
+           for (r1 = 0; r1 < r; r1++) {\r
+               debug(("%s%d", r1 ? "," : "", keys[r1]));\r
+           }\r
+           debug((")"));\r
+       }\r
+#endif\r
+       if (r > 0) {\r
+           WCHAR keybuf;\r
+\r
+           /*\r
+            * Interrupt an ongoing paste. I'm not sure this is\r
+            * sensible, but for the moment it's preferable to\r
+            * having to faff about buffering things.\r
+            */\r
+           term_nopaste(term);\r
+\r
+           p = output;\r
+           for (i = 0; i < r; i++) {\r
+               unsigned char ch = (unsigned char) keys[i];\r
+\r
+               if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {\r
+                   compose_char = ch;\r
+                   compose_state++;\r
+                   continue;\r
+               }\r
+               if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {\r
+                   int nc;\r
+                   compose_state = 0;\r
+\r
+                   if ((nc = check_compose(compose_char, ch)) == -1) {\r
+                       MessageBeep(MB_ICONHAND);\r
+                       return 0;\r
+                   }\r
+                   keybuf = nc;\r
+                   term_seen_key_event(term);\r
+                   if (ldisc)\r
+                       luni_send(ldisc, &keybuf, 1, 1);\r
+                   continue;\r
+               }\r
+\r
+               compose_state = 0;\r
+\r
+               if (!key_down) {\r
+                   if (alt_sum) {\r
+                       if (in_utf(term) || ucsdata.dbcs_screenfont) {\r
+                           keybuf = alt_sum;\r
+                           term_seen_key_event(term);\r
+                           if (ldisc)\r
+                               luni_send(ldisc, &keybuf, 1, 1);\r
+                       } else {\r
+                           ch = (char) alt_sum;\r
+                           /*\r
+                            * We need not bother about stdin\r
+                            * backlogs here, because in GUI PuTTY\r
+                            * we can't do anything about it\r
+                            * anyway; there's no means of asking\r
+                            * Windows to hold off on KEYDOWN\r
+                            * messages. We _have_ to buffer\r
+                            * everything we're sent.\r
+                            */\r
+                           term_seen_key_event(term);\r
+                           if (ldisc)\r
+                               ldisc_send(ldisc, &ch, 1, 1);\r
+                       }\r
+                       alt_sum = 0;\r
+                   } else {\r
+                       term_seen_key_event(term);\r
+                       if (ldisc)\r
+                           lpage_send(ldisc, kbd_codepage, &ch, 1, 1);\r
+                   }\r
+               } else {\r
+                   if(capsOn && ch < 0x80) {\r
+                       WCHAR cbuf[2];\r
+                       cbuf[0] = 27;\r
+                       cbuf[1] = xlat_uskbd2cyrllic(ch);\r
+                       term_seen_key_event(term);\r
+                       if (ldisc)\r
+                           luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);\r
+                   } else {\r
+                       char cbuf[2];\r
+                       cbuf[0] = '\033';\r
+                       cbuf[1] = ch;\r
+                       term_seen_key_event(term);\r
+                       if (ldisc)\r
+                           lpage_send(ldisc, kbd_codepage,\r
+                                      cbuf+!left_alt, 1+!!left_alt, 1);\r
+                   }\r
+               }\r
+               show_mouseptr(0);\r
+           }\r
+\r
+           /* This is so the ALT-Numpad and dead keys work correctly. */\r
+           keys[0] = 0;\r
+\r
+           return p - output;\r
+       }\r
+       /* If we're definitly not building up an ALT-54321 then clear it */\r
+       if (!left_alt)\r
+           keys[0] = 0;\r
+       /* If we will be using alt_sum fix the 256s */\r
+       else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))\r
+           keys[0] = 10;\r
+    }\r
+\r
+    /*\r
+     * ALT alone may or may not want to bring up the System menu.\r
+     * If it's not meant to, we return 0 on presses or releases of\r
+     * ALT, to show that we've swallowed the keystroke. Otherwise\r
+     * we return -1, which means Windows will give the keystroke\r
+     * its default handling (i.e. bring up the System menu).\r
+     */\r
+    if (wParam == VK_MENU && !cfg.alt_only)\r
+       return 0;\r
+\r
+    return -1;\r
+}\r
+\r
+void set_title(void *frontend, char *title)\r
+{\r
+    sfree(window_name);\r
+    window_name = snewn(1 + strlen(title), char);\r
+    strcpy(window_name, title);\r
+    if (cfg.win_name_always || !IsIconic(hwnd))\r
+       SetWindowText(hwnd, title);\r
+}\r
+\r
+void set_icon(void *frontend, char *title)\r
+{\r
+    sfree(icon_name);\r
+    icon_name = snewn(1 + strlen(title), char);\r
+    strcpy(icon_name, title);\r
+    if (!cfg.win_name_always && IsIconic(hwnd))\r
+       SetWindowText(hwnd, title);\r
+}\r
+\r
+void set_sbar(void *frontend, int total, int start, int page)\r
+{\r
+    SCROLLINFO si;\r
+\r
+    if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)\r
+       return;\r
+\r
+    si.cbSize = sizeof(si);\r
+    si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;\r
+    si.nMin = 0;\r
+    si.nMax = total - 1;\r
+    si.nPage = page;\r
+    si.nPos = start;\r
+    if (hwnd)\r
+       SetScrollInfo(hwnd, SB_VERT, &si, TRUE);\r
+}\r
+\r
+Context get_ctx(void *frontend)\r
+{\r
+    HDC hdc;\r
+    if (hwnd) {\r
+       hdc = GetDC(hwnd);\r
+       if (hdc && pal)\r
+           SelectPalette(hdc, pal, FALSE);\r
+       return hdc;\r
+    } else\r
+       return NULL;\r
+}\r
+\r
+void free_ctx(Context ctx)\r
+{\r
+    SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);\r
+    ReleaseDC(hwnd, ctx);\r
+}\r
+\r
+static void real_palette_set(int n, int r, int g, int b)\r
+{\r
+    if (pal) {\r
+       logpal->palPalEntry[n].peRed = r;\r
+       logpal->palPalEntry[n].peGreen = g;\r
+       logpal->palPalEntry[n].peBlue = b;\r
+       logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;\r
+       colours[n] = PALETTERGB(r, g, b);\r
+       SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);\r
+    } else\r
+       colours[n] = RGB(r, g, b);\r
+}\r
+\r
+void palette_set(void *frontend, int n, int r, int g, int b)\r
+{\r
+    if (n >= 16)\r
+       n += 256 - 16;\r
+    if (n > NALLCOLOURS)\r
+       return;\r
+    real_palette_set(n, r, g, b);\r
+    if (pal) {\r
+       HDC hdc = get_ctx(frontend);\r
+       UnrealizeObject(pal);\r
+       RealizePalette(hdc);\r
+       free_ctx(hdc);\r
+    } else {\r
+       if (n == (ATTR_DEFBG>>ATTR_BGSHIFT))\r
+           /* If Default Background changes, we need to ensure any\r
+            * space between the text area and the window border is\r
+            * redrawn. */\r
+           InvalidateRect(hwnd, NULL, TRUE);\r
+    }\r
+}\r
+\r
+void palette_reset(void *frontend)\r
+{\r
+    int i;\r
+\r
+    /* And this */\r
+    for (i = 0; i < NALLCOLOURS; i++) {\r
+       if (pal) {\r
+           logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;\r
+           logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;\r
+           logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;\r
+           logpal->palPalEntry[i].peFlags = 0;\r
+           colours[i] = PALETTERGB(defpal[i].rgbtRed,\r
+                                   defpal[i].rgbtGreen,\r
+                                   defpal[i].rgbtBlue);\r
+       } else\r
+           colours[i] = RGB(defpal[i].rgbtRed,\r
+                            defpal[i].rgbtGreen, defpal[i].rgbtBlue);\r
+    }\r
+\r
+    if (pal) {\r
+       HDC hdc;\r
+       SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);\r
+       hdc = get_ctx(frontend);\r
+       RealizePalette(hdc);\r
+       free_ctx(hdc);\r
+    } else {\r
+       /* Default Background may have changed. Ensure any space between\r
+        * text area and window border is redrawn. */\r
+       InvalidateRect(hwnd, NULL, TRUE);\r
+    }\r
+}\r
+\r
+void write_aclip(void *frontend, char *data, int len, int must_deselect)\r
+{\r
+    HGLOBAL clipdata;\r
+    void *lock;\r
+\r
+    clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);\r
+    if (!clipdata)\r
+       return;\r
+    lock = GlobalLock(clipdata);\r
+    if (!lock)\r
+       return;\r
+    memcpy(lock, data, len);\r
+    ((unsigned char *) lock)[len] = 0;\r
+    GlobalUnlock(clipdata);\r
+\r
+    if (!must_deselect)\r
+       SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);\r
+\r
+    if (OpenClipboard(hwnd)) {\r
+       EmptyClipboard();\r
+       SetClipboardData(CF_TEXT, clipdata);\r
+       CloseClipboard();\r
+    } else\r
+       GlobalFree(clipdata);\r
+\r
+    if (!must_deselect)\r
+       SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);\r
+}\r
+\r
+/*\r
+ * Note: unlike write_aclip() this will not append a nul.\r
+ */\r
+void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)\r
+{\r
+    HGLOBAL clipdata, clipdata2, clipdata3;\r
+    int len2;\r
+    void *lock, *lock2, *lock3;\r
+\r
+    len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);\r
+\r
+    clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,\r
+                          len * sizeof(wchar_t));\r
+    clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);\r
+\r
+    if (!clipdata || !clipdata2) {\r
+       if (clipdata)\r
+           GlobalFree(clipdata);\r
+       if (clipdata2)\r
+           GlobalFree(clipdata2);\r
+       return;\r
+    }\r
+    if (!(lock = GlobalLock(clipdata)))\r
+       return;\r
+    if (!(lock2 = GlobalLock(clipdata2)))\r
+       return;\r
+\r
+    memcpy(lock, data, len * sizeof(wchar_t));\r
+    WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);\r
+\r
+    if (cfg.rtf_paste) {\r
+       wchar_t unitab[256];\r
+       char *rtf = NULL;\r
+       unsigned char *tdata = (unsigned char *)lock2;\r
+       wchar_t *udata = (wchar_t *)lock;\r
+       int rtflen = 0, uindex = 0, tindex = 0;\r
+       int rtfsize = 0;\r
+       int multilen, blen, alen, totallen, i;\r
+       char before[16], after[4];\r
+       int fgcolour,  lastfgcolour  = 0;\r
+       int bgcolour,  lastbgcolour  = 0;\r
+       int attrBold,  lastAttrBold  = 0;\r
+       int attrUnder, lastAttrUnder = 0;\r
+       int palette[NALLCOLOURS];\r
+       int numcolours;\r
+\r
+       get_unitab(CP_ACP, unitab, 0);\r
+\r
+       rtfsize = 100 + strlen(cfg.font.name);\r
+       rtf = snewn(rtfsize, char);\r
+       rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d",\r
+                        cfg.font.name, cfg.font.height*2);\r
+\r
+       /*\r
+        * Add colour palette\r
+        * {\colortbl ;\red255\green0\blue0;\red0\green0\blue128;}\r
+        */\r
+\r
+       /*\r
+        * First - Determine all colours in use\r
+        *    o  Foregound and background colours share the same palette\r
+        */\r
+       if (attr) {\r
+           memset(palette, 0, sizeof(palette));\r
+           for (i = 0; i < (len-1); i++) {\r
+               fgcolour = ((attr[i] & ATTR_FGMASK) >> ATTR_FGSHIFT);\r
+               bgcolour = ((attr[i] & ATTR_BGMASK) >> ATTR_BGSHIFT);\r
+\r
+               if (attr[i] & ATTR_REVERSE) {\r
+                   int tmpcolour = fgcolour;   /* Swap foreground and background */\r
+                   fgcolour = bgcolour;\r
+                   bgcolour = tmpcolour;\r
+               }\r
+\r
+               if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) {\r
+                   if (fgcolour  <   8)        /* ANSI colours */\r
+                       fgcolour +=   8;\r
+                   else if (fgcolour >= 256)   /* Default colours */\r
+                       fgcolour ++;\r
+               }\r
+\r
+               if (attr[i] & ATTR_BLINK) {\r
+                   if (bgcolour  <   8)        /* ANSI colours */\r
+                       bgcolour +=   8;\r
+                   else if (bgcolour >= 256)   /* Default colours */\r
+                       bgcolour ++;\r
+               }\r
+\r
+               palette[fgcolour]++;\r
+               palette[bgcolour]++;\r
+           }\r
+\r
+           /*\r
+            * Next - Create a reduced palette\r
+            */\r
+           numcolours = 0;\r
+           for (i = 0; i < NALLCOLOURS; i++) {\r
+               if (palette[i] != 0)\r
+                   palette[i]  = ++numcolours;\r
+           }\r
+\r
+           /*\r
+            * Finally - Write the colour table\r
+            */\r
+           rtf = sresize(rtf, rtfsize + (numcolours * 25), char);\r
+           strcat(rtf, "{\\colortbl ;");\r
+           rtflen = strlen(rtf);\r
+\r
+           for (i = 0; i < NALLCOLOURS; i++) {\r
+               if (palette[i] != 0) {\r
+                   rtflen += sprintf(&rtf[rtflen], "\\red%d\\green%d\\blue%d;", defpal[i].rgbtRed, defpal[i].rgbtGreen, defpal[i].rgbtBlue);\r
+               }\r
+           }\r
+           strcpy(&rtf[rtflen], "}");\r
+           rtflen ++;\r
+       }\r
+\r
+       /*\r
+        * We want to construct a piece of RTF that specifies the\r
+        * same Unicode text. To do this we will read back in\r
+        * parallel from the Unicode data in `udata' and the\r
+        * non-Unicode data in `tdata'. For each character in\r
+        * `tdata' which becomes the right thing in `udata' when\r
+        * looked up in `unitab', we just copy straight over from\r
+        * tdata. For each one that doesn't, we must WCToMB it\r
+        * individually and produce a \u escape sequence.\r
+        * \r
+        * It would probably be more robust to just bite the bullet\r
+        * and WCToMB each individual Unicode character one by one,\r
+        * then MBToWC each one back to see if it was an accurate\r
+        * translation; but that strikes me as a horrifying number\r
+        * of Windows API calls so I want to see if this faster way\r
+        * will work. If it screws up badly we can always revert to\r
+        * the simple and slow way.\r
+        */\r
+       while (tindex < len2 && uindex < len &&\r
+              tdata[tindex] && udata[uindex]) {\r
+           if (tindex + 1 < len2 &&\r
+               tdata[tindex] == '\r' &&\r
+               tdata[tindex+1] == '\n') {\r
+               tindex++;\r
+               uindex++;\r
+            }\r
+\r
+            /*\r
+             * Set text attributes\r
+             */\r
+            if (attr) {\r
+                if (rtfsize < rtflen + 64) {\r
+                   rtfsize = rtflen + 512;\r
+                   rtf = sresize(rtf, rtfsize, char);\r
+                }\r
+\r
+                /*\r
+                 * Determine foreground and background colours\r
+                 */\r
+                fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT);\r
+                bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT);\r
+\r
+               if (attr[tindex] & ATTR_REVERSE) {\r
+                   int tmpcolour = fgcolour;       /* Swap foreground and background */\r
+                   fgcolour = bgcolour;\r
+                   bgcolour = tmpcolour;\r
+               }\r
+\r
+               if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) {\r
+                   if (fgcolour  <   8)            /* ANSI colours */\r
+                       fgcolour +=   8;\r
+                   else if (fgcolour >= 256)       /* Default colours */\r
+                       fgcolour ++;\r
+                }\r
+\r
+               if (attr[tindex] & ATTR_BLINK) {\r
+                   if (bgcolour  <   8)            /* ANSI colours */\r
+                       bgcolour +=   8;\r
+                   else if (bgcolour >= 256)       /* Default colours */\r
+                       bgcolour ++;\r
+                }\r
+\r
+                /*\r
+                 * Collect other attributes\r
+                 */\r
+               if (bold_mode != BOLD_COLOURS)\r
+                   attrBold  = attr[tindex] & ATTR_BOLD;\r
+               else\r
+                   attrBold  = 0;\r
+                \r
+               attrUnder = attr[tindex] & ATTR_UNDER;\r
+\r
+                /*\r
+                 * Reverse video\r
+                *   o  If video isn't reversed, ignore colour attributes for default foregound\r
+                *      or background.\r
+                *   o  Special case where bolded text is displayed using the default foregound\r
+                *      and background colours - force to bolded RTF.\r
+                 */\r
+               if (!(attr[tindex] & ATTR_REVERSE)) {\r
+                   if (bgcolour >= 256)            /* Default color */\r
+                       bgcolour  = -1;             /* No coloring */\r
+\r
+                   if (fgcolour >= 256) {          /* Default colour */\r
+                       if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1)\r
+                           attrBold = ATTR_BOLD;   /* Emphasize text with bold attribute */\r
+\r
+                       fgcolour  = -1;             /* No coloring */\r
+                   }\r
+               }\r
+\r
+                /*\r
+                 * Write RTF text attributes\r
+                 */\r
+               if (lastfgcolour != fgcolour) {\r
+                    lastfgcolour  = fgcolour;\r
+                   rtflen       += sprintf(&rtf[rtflen], "\\cf%d ", (fgcolour >= 0) ? palette[fgcolour] : 0);\r
+                }\r
+\r
+                if (lastbgcolour != bgcolour) {\r
+                    lastbgcolour  = bgcolour;\r
+                    rtflen       += sprintf(&rtf[rtflen], "\\highlight%d ", (bgcolour >= 0) ? palette[bgcolour] : 0);\r
+                }\r
+\r
+               if (lastAttrBold != attrBold) {\r
+                   lastAttrBold  = attrBold;\r
+                   rtflen       += sprintf(&rtf[rtflen], "%s", attrBold ? "\\b " : "\\b0 ");\r
+               }\r
+\r
+                if (lastAttrUnder != attrUnder) {\r
+                    lastAttrUnder  = attrUnder;\r
+                    rtflen        += sprintf(&rtf[rtflen], "%s", attrUnder ? "\\ul " : "\\ulnone ");\r
+                }\r
+           }\r
+\r
+           if (unitab[tdata[tindex]] == udata[uindex]) {\r
+               multilen = 1;\r
+               before[0] = '\0';\r
+               after[0] = '\0';\r
+               blen = alen = 0;\r
+           } else {\r
+               multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,\r
+                                              NULL, 0, NULL, NULL);\r
+               if (multilen != 1) {\r
+                   blen = sprintf(before, "{\\uc%d\\u%d", multilen,\r
+                                  udata[uindex]);\r
+                   alen = 1; strcpy(after, "}");\r
+               } else {\r
+                   blen = sprintf(before, "\\u%d", udata[uindex]);\r
+                   alen = 0; after[0] = '\0';\r
+               }\r
+           }\r
+           assert(tindex + multilen <= len2);\r
+           totallen = blen + alen;\r
+           for (i = 0; i < multilen; i++) {\r
+               if (tdata[tindex+i] == '\\' ||\r
+                   tdata[tindex+i] == '{' ||\r
+                   tdata[tindex+i] == '}')\r
+                   totallen += 2;\r
+               else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)\r
+                   totallen += 6;     /* \par\r\n */\r
+               else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)\r
+                   totallen += 4;\r
+               else\r
+                   totallen++;\r
+           }\r
+\r
+           if (rtfsize < rtflen + totallen + 3) {\r
+               rtfsize = rtflen + totallen + 512;\r
+               rtf = sresize(rtf, rtfsize, char);\r
+           }\r
+\r
+           strcpy(rtf + rtflen, before); rtflen += blen;\r
+           for (i = 0; i < multilen; i++) {\r
+               if (tdata[tindex+i] == '\\' ||\r
+                   tdata[tindex+i] == '{' ||\r
+                   tdata[tindex+i] == '}') {\r
+                   rtf[rtflen++] = '\\';\r
+                   rtf[rtflen++] = tdata[tindex+i];\r
+               } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {\r
+                   rtflen += sprintf(rtf+rtflen, "\\par\r\n");\r
+               } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {\r
+                   rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);\r
+               } else {\r
+                   rtf[rtflen++] = tdata[tindex+i];\r
+               }\r
+           }\r
+           strcpy(rtf + rtflen, after); rtflen += alen;\r
+\r
+           tindex += multilen;\r
+           uindex++;\r
+       }\r
+\r
+        rtf[rtflen++] = '}';          /* Terminate RTF stream */\r
+        rtf[rtflen++] = '\0';\r
+        rtf[rtflen++] = '\0';\r
+\r
+       clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);\r
+       if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {\r
+           memcpy(lock3, rtf, rtflen);\r
+           GlobalUnlock(clipdata3);\r
+       }\r
+       sfree(rtf);\r
+    } else\r
+       clipdata3 = NULL;\r
+\r
+    GlobalUnlock(clipdata);\r
+    GlobalUnlock(clipdata2);\r
+\r
+    if (!must_deselect)\r
+       SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);\r
+\r
+    if (OpenClipboard(hwnd)) {\r
+       EmptyClipboard();\r
+       SetClipboardData(CF_UNICODETEXT, clipdata);\r
+       SetClipboardData(CF_TEXT, clipdata2);\r
+       if (clipdata3)\r
+           SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);\r
+       CloseClipboard();\r
+    } else {\r
+       GlobalFree(clipdata);\r
+       GlobalFree(clipdata2);\r
+    }\r
+\r
+    if (!must_deselect)\r
+       SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);\r
+}\r
+\r
+static DWORD WINAPI clipboard_read_threadfunc(void *param)\r
+{\r
+    HWND hwnd = (HWND)param;\r
+    HGLOBAL clipdata;\r
+\r
+    if (OpenClipboard(NULL)) {\r
+       if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {\r
+           SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)1, (LPARAM)clipdata);\r
+       } else if ((clipdata = GetClipboardData(CF_TEXT))) {\r
+           SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)0, (LPARAM)clipdata);\r
+       }\r
+       CloseClipboard();\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+static int process_clipdata(HGLOBAL clipdata, int unicode)\r
+{\r
+    sfree(clipboard_contents);\r
+    clipboard_contents = NULL;\r
+    clipboard_length = 0;\r
+\r
+    if (unicode) {\r
+       wchar_t *p = GlobalLock(clipdata);\r
+       wchar_t *p2;\r
+\r
+       if (p) {\r
+           /* Unwilling to rely on Windows having wcslen() */\r
+           for (p2 = p; *p2; p2++);\r
+           clipboard_length = p2 - p;\r
+           clipboard_contents = snewn(clipboard_length + 1, wchar_t);\r
+           memcpy(clipboard_contents, p, clipboard_length * sizeof(wchar_t));\r
+           clipboard_contents[clipboard_length] = L'\0';\r
+           return TRUE;\r
+       }\r
+    } else {\r
+       char *s = GlobalLock(clipdata);\r
+       int i;\r
+\r
+       if (s) {\r
+           i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);\r
+           clipboard_contents = snewn(i, wchar_t);\r
+           MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1,\r
+                               clipboard_contents, i);\r
+           clipboard_length = i - 1;\r
+           clipboard_contents[clipboard_length] = L'\0';\r
+           return TRUE;\r
+       }\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+\r
+void request_paste(void *frontend)\r
+{\r
+    /*\r
+     * I always thought pasting was synchronous in Windows; the\r
+     * clipboard access functions certainly _look_ synchronous,\r
+     * unlike the X ones. But in fact it seems that in some\r
+     * situations the contents of the clipboard might not be\r
+     * immediately available, and the clipboard-reading functions\r
+     * may block. This leads to trouble if the application\r
+     * delivering the clipboard data has to get hold of it by -\r
+     * for example - talking over a network connection which is\r
+     * forwarded through this very PuTTY.\r
+     *\r
+     * Hence, we spawn a subthread to read the clipboard, and do\r
+     * our paste when it's finished. The thread will send a\r
+     * message back to our main window when it terminates, and\r
+     * that tells us it's OK to paste.\r
+     */\r
+    DWORD in_threadid; /* required for Win9x */\r
+    CreateThread(NULL, 0, clipboard_read_threadfunc,\r
+                hwnd, 0, &in_threadid);\r
+}\r
+\r
+void get_clip(void *frontend, wchar_t **p, int *len)\r
+{\r
+    if (p) {\r
+       *p = clipboard_contents;\r
+       *len = clipboard_length;\r
+    }\r
+}\r
+\r
+#if 0\r
+/*\r
+ * Move `lines' lines from position `from' to position `to' in the\r
+ * window.\r
+ */\r
+void optimised_move(void *frontend, int to, int from, int lines)\r
+{\r
+    RECT r;\r
+    int min, max;\r
+\r
+    min = (to < from ? to : from);\r
+    max = to + from - min;\r
+\r
+    r.left = offset_width;\r
+    r.right = offset_width + term->cols * font_width;\r
+    r.top = offset_height + min * font_height;\r
+    r.bottom = offset_height + (max + lines) * font_height;\r
+    ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);\r
+}\r
+#endif\r
+\r
+/*\r
+ * Print a message box and perform a fatal exit.\r
+ */\r
+void fatalbox(char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *stuff, morestuff[100];\r
+\r
+    va_start(ap, fmt);\r
+    stuff = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    sprintf(morestuff, "%.70s Fatal Error", appname);\r
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);\r
+    sfree(stuff);\r
+    cleanup_exit(1);\r
+}\r
+\r
+/*\r
+ * Print a modal (Really Bad) message box and perform a fatal exit.\r
+ */\r
+void modalfatalbox(char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *stuff, morestuff[100];\r
+\r
+    va_start(ap, fmt);\r
+    stuff = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    sprintf(morestuff, "%.70s Fatal Error", appname);\r
+    MessageBox(hwnd, stuff, morestuff,\r
+              MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);\r
+    sfree(stuff);\r
+    cleanup_exit(1);\r
+}\r
+\r
+DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));\r
+\r
+static void init_flashwindow(void)\r
+{\r
+    HMODULE user32_module = load_system32_dll("user32.dll");\r
+    GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);\r
+}\r
+\r
+static BOOL flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout)\r
+{\r
+    if (p_FlashWindowEx) {\r
+       FLASHWINFO fi;\r
+       fi.cbSize = sizeof(fi);\r
+       fi.hwnd = hwnd;\r
+       fi.dwFlags = dwFlags;\r
+       fi.uCount = uCount;\r
+       fi.dwTimeout = dwTimeout;\r
+       return (*p_FlashWindowEx)(&fi);\r
+    }\r
+    else\r
+       return FALSE; /* shrug */\r
+}\r
+\r
+static void flash_window(int mode);\r
+static long next_flash;\r
+static int flashing = 0;\r
+\r
+/*\r
+ * Timer for platforms where we must maintain window flashing manually\r
+ * (e.g., Win95).\r
+ */\r
+static void flash_window_timer(void *ctx, long now)\r
+{\r
+    if (flashing && now - next_flash >= 0) {\r
+       flash_window(1);\r
+    }\r
+}\r
+\r
+/*\r
+ * Manage window caption / taskbar flashing, if enabled.\r
+ * 0 = stop, 1 = maintain, 2 = start\r
+ */\r
+static void flash_window(int mode)\r
+{\r
+    if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {\r
+       /* stop */\r
+       if (flashing) {\r
+           flashing = 0;\r
+           if (p_FlashWindowEx)\r
+               flash_window_ex(FLASHW_STOP, 0, 0);\r
+           else\r
+               FlashWindow(hwnd, FALSE);\r
+       }\r
+\r
+    } else if (mode == 2) {\r
+       /* start */\r
+       if (!flashing) {\r
+           flashing = 1;\r
+           if (p_FlashWindowEx) {\r
+               /* For so-called "steady" mode, we use uCount=2, which\r
+                * seems to be the traditional number of flashes used\r
+                * by user notifications (e.g., by Explorer).\r
+                * uCount=0 appears to enable continuous flashing, per\r
+                * "flashing" mode, although I haven't seen this\r
+                * documented. */\r
+               flash_window_ex(FLASHW_ALL | FLASHW_TIMER,\r
+                               (cfg.beep_ind == B_IND_FLASH ? 0 : 2),\r
+                               0 /* system cursor blink rate */);\r
+               /* No need to schedule timer */\r
+           } else {\r
+               FlashWindow(hwnd, TRUE);\r
+               next_flash = schedule_timer(450, flash_window_timer, hwnd);\r
+           }\r
+       }\r
+\r
+    } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {\r
+       /* maintain */\r
+       if (flashing && !p_FlashWindowEx) {\r
+           FlashWindow(hwnd, TRUE);    /* toggle */\r
+           next_flash = schedule_timer(450, flash_window_timer, hwnd);\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Beep.\r
+ */\r
+void do_beep(void *frontend, int mode)\r
+{\r
+    if (mode == BELL_DEFAULT) {\r
+       /*\r
+        * For MessageBeep style bells, we want to be careful of\r
+        * timing, because they don't have the nice property of\r
+        * PlaySound bells that each one cancels the previous\r
+        * active one. So we limit the rate to one per 50ms or so.\r
+        */\r
+       static long lastbeep = 0;\r
+       long beepdiff;\r
+\r
+       beepdiff = GetTickCount() - lastbeep;\r
+       if (beepdiff >= 0 && beepdiff < 50)\r
+           return;\r
+       MessageBeep(MB_OK);\r
+       /*\r
+        * The above MessageBeep call takes time, so we record the\r
+        * time _after_ it finishes rather than before it starts.\r
+        */\r
+       lastbeep = GetTickCount();\r
+    } else if (mode == BELL_WAVEFILE) {\r
+       if (!PlaySound(cfg.bell_wavefile.path, NULL,\r
+                      SND_ASYNC | SND_FILENAME)) {\r
+           char buf[sizeof(cfg.bell_wavefile.path) + 80];\r
+           char otherbuf[100];\r
+           sprintf(buf, "Unable to play sound file\n%s\n"\r
+                   "Using default sound instead", cfg.bell_wavefile.path);\r
+           sprintf(otherbuf, "%.70s Sound Error", appname);\r
+           MessageBox(hwnd, buf, otherbuf,\r
+                      MB_OK | MB_ICONEXCLAMATION);\r
+           cfg.beep = BELL_DEFAULT;\r
+       }\r
+    } else if (mode == BELL_PCSPEAKER) {\r
+       static long lastbeep = 0;\r
+       long beepdiff;\r
+\r
+       beepdiff = GetTickCount() - lastbeep;\r
+       if (beepdiff >= 0 && beepdiff < 50)\r
+           return;\r
+\r
+       /*\r
+        * We must beep in different ways depending on whether this\r
+        * is a 95-series or NT-series OS.\r
+        */\r
+       if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)\r
+           Beep(800, 100);\r
+       else\r
+           MessageBeep(-1);\r
+       lastbeep = GetTickCount();\r
+    }\r
+    /* Otherwise, either visual bell or disabled; do nothing here */\r
+    if (!term->has_focus) {\r
+       flash_window(2);               /* start */\r
+    }\r
+}\r
+\r
+/*\r
+ * Minimise or restore the window in response to a server-side\r
+ * request.\r
+ */\r
+void set_iconic(void *frontend, int iconic)\r
+{\r
+    if (IsIconic(hwnd)) {\r
+       if (!iconic)\r
+           ShowWindow(hwnd, SW_RESTORE);\r
+    } else {\r
+       if (iconic)\r
+           ShowWindow(hwnd, SW_MINIMIZE);\r
+    }\r
+}\r
+\r
+/*\r
+ * Move the window in response to a server-side request.\r
+ */\r
+void move_window(void *frontend, int x, int y)\r
+{\r
+    if (cfg.resize_action == RESIZE_DISABLED || \r
+        cfg.resize_action == RESIZE_FONT ||\r
+       IsZoomed(hwnd))\r
+       return;\r
+\r
+    SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
+}\r
+\r
+/*\r
+ * Move the window to the top or bottom of the z-order in response\r
+ * to a server-side request.\r
+ */\r
+void set_zorder(void *frontend, int top)\r
+{\r
+    if (cfg.alwaysontop)\r
+       return;                        /* ignore */\r
+    SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,\r
+                SWP_NOMOVE | SWP_NOSIZE);\r
+}\r
+\r
+/*\r
+ * Refresh the window in response to a server-side request.\r
+ */\r
+void refresh_window(void *frontend)\r
+{\r
+    InvalidateRect(hwnd, NULL, TRUE);\r
+}\r
+\r
+/*\r
+ * Maximise or restore the window in response to a server-side\r
+ * request.\r
+ */\r
+void set_zoomed(void *frontend, int zoomed)\r
+{\r
+    if (IsZoomed(hwnd)) {\r
+        if (!zoomed)\r
+           ShowWindow(hwnd, SW_RESTORE);\r
+    } else {\r
+       if (zoomed)\r
+           ShowWindow(hwnd, SW_MAXIMIZE);\r
+    }\r
+}\r
+\r
+/*\r
+ * Report whether the window is iconic, for terminal reports.\r
+ */\r
+int is_iconic(void *frontend)\r
+{\r
+    return IsIconic(hwnd);\r
+}\r
+\r
+/*\r
+ * Report the window's position, for terminal reports.\r
+ */\r
+void get_window_pos(void *frontend, int *x, int *y)\r
+{\r
+    RECT r;\r
+    GetWindowRect(hwnd, &r);\r
+    *x = r.left;\r
+    *y = r.top;\r
+}\r
+\r
+/*\r
+ * Report the window's pixel size, for terminal reports.\r
+ */\r
+void get_window_pixels(void *frontend, int *x, int *y)\r
+{\r
+    RECT r;\r
+    GetWindowRect(hwnd, &r);\r
+    *x = r.right - r.left;\r
+    *y = r.bottom - r.top;\r
+}\r
+\r
+/*\r
+ * Return the window or icon title.\r
+ */\r
+char *get_window_title(void *frontend, int icon)\r
+{\r
+    return icon ? icon_name : window_name;\r
+}\r
+\r
+/*\r
+ * See if we're in full-screen mode.\r
+ */\r
+static int is_full_screen()\r
+{\r
+    if (!IsZoomed(hwnd))\r
+       return FALSE;\r
+    if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CAPTION)\r
+       return FALSE;\r
+    return TRUE;\r
+}\r
+\r
+/* Get the rect/size of a full screen window using the nearest available\r
+ * monitor in multimon systems; default to something sensible if only\r
+ * one monitor is present. */\r
+static int get_fullscreen_rect(RECT * ss)\r
+{\r
+#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)\r
+       HMONITOR mon;\r
+       MONITORINFO mi;\r
+       mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);\r
+       mi.cbSize = sizeof(mi);\r
+       GetMonitorInfo(mon, &mi);\r
+\r
+       /* structure copy */\r
+       *ss = mi.rcMonitor;\r
+       return TRUE;\r
+#else\r
+/* could also use code like this:\r
+       ss->left = ss->top = 0;\r
+       ss->right = GetSystemMetrics(SM_CXSCREEN);\r
+       ss->bottom = GetSystemMetrics(SM_CYSCREEN);\r
+*/ \r
+       return GetClientRect(GetDesktopWindow(), ss);\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+ * Go full-screen. This should only be called when we are already\r
+ * maximised.\r
+ */\r
+static void make_full_screen()\r
+{\r
+    DWORD style;\r
+       RECT ss;\r
+\r
+    assert(IsZoomed(hwnd));\r
+\r
+       if (is_full_screen())\r
+               return;\r
+       \r
+    /* Remove the window furniture. */\r
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);\r
+    style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);\r
+    if (cfg.scrollbar_in_fullscreen)\r
+       style |= WS_VSCROLL;\r
+    else\r
+       style &= ~WS_VSCROLL;\r
+    SetWindowLongPtr(hwnd, GWL_STYLE, style);\r
+\r
+    /* Resize ourselves to exactly cover the nearest monitor. */\r
+       get_fullscreen_rect(&ss);\r
+    SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,\r
+                       ss.right - ss.left,\r
+                       ss.bottom - ss.top,\r
+                       SWP_FRAMECHANGED);\r
+\r
+    /* We may have changed size as a result */\r
+\r
+    reset_window(0);\r
+\r
+    /* Tick the menu item in the System and context menus. */\r
+    {\r
+       int i;\r
+       for (i = 0; i < lenof(popup_menus); i++)\r
+           CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_CHECKED);\r
+    }\r
+}\r
+\r
+/*\r
+ * Clear the full-screen attributes.\r
+ */\r
+static void clear_full_screen()\r
+{\r
+    DWORD oldstyle, style;\r
+\r
+    /* Reinstate the window furniture. */\r
+    style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE);\r
+    style |= WS_CAPTION | WS_BORDER;\r
+    if (cfg.resize_action == RESIZE_DISABLED)\r
+        style &= ~WS_THICKFRAME;\r
+    else\r
+        style |= WS_THICKFRAME;\r
+    if (cfg.scrollbar)\r
+       style |= WS_VSCROLL;\r
+    else\r
+       style &= ~WS_VSCROLL;\r
+    if (style != oldstyle) {\r
+       SetWindowLongPtr(hwnd, GWL_STYLE, style);\r
+       SetWindowPos(hwnd, NULL, 0, 0, 0, 0,\r
+                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |\r
+                    SWP_FRAMECHANGED);\r
+    }\r
+\r
+    /* Untick the menu item in the System and context menus. */\r
+    {\r
+       int i;\r
+       for (i = 0; i < lenof(popup_menus); i++)\r
+           CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_UNCHECKED);\r
+    }\r
+}\r
+\r
+/*\r
+ * Toggle full-screen mode.\r
+ */\r
+static void flip_full_screen()\r
+{\r
+    if (is_full_screen()) {\r
+       ShowWindow(hwnd, SW_RESTORE);\r
+    } else if (IsZoomed(hwnd)) {\r
+       make_full_screen();\r
+    } else {\r
+       SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);\r
+       ShowWindow(hwnd, SW_MAXIMIZE);\r
+    }\r
+}\r
+\r
+void frontend_keypress(void *handle)\r
+{\r
+    /*\r
+     * Keypress termination in non-Close-On-Exit mode is not\r
+     * currently supported in PuTTY proper, because the window\r
+     * always has a perfectly good Close button anyway. So we do\r
+     * nothing here.\r
+     */\r
+    return;\r
+}\r
+\r
+int from_backend(void *frontend, int is_stderr, const char *data, int len)\r
+{\r
+    return term_data(term, is_stderr, data, len);\r
+}\r
+\r
+int from_backend_untrusted(void *frontend, const char *data, int len)\r
+{\r
+    return term_data_untrusted(term, data, len);\r
+}\r
+\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    int ret;\r
+    ret = cmdline_get_passwd_input(p, in, inlen);\r
+    if (ret == -1)\r
+       ret = term_get_userpass_input(term, p, in, inlen);\r
+    return ret;\r
+}\r
+\r
+void agent_schedule_callback(void (*callback)(void *, void *, int),\r
+                            void *callback_ctx, void *data, int len)\r
+{\r
+    struct agent_callback *c = snew(struct agent_callback);\r
+    c->callback = callback;\r
+    c->callback_ctx = callback_ctx;\r
+    c->data = data;\r
+    c->len = len;\r
+    PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);\r
+}\r
diff --git a/putty/WINDOWS/WINGSS.C b/putty/WINDOWS/WINGSS.C
new file mode 100644 (file)
index 0000000..a2076c5
--- /dev/null
@@ -0,0 +1,478 @@
+#ifndef NO_GSSAPI\r
+\r
+#include "putty.h"\r
+\r
+#include <security.h>\r
+\r
+#include "pgssapi.h"\r
+#include "sshgss.h"\r
+#include "sshgssc.h"\r
+\r
+#include "misc.h"\r
+\r
+/* Windows code to set up the GSSAPI library list. */\r
+\r
+const int ngsslibs = 3;\r
+const char *const gsslibnames[3] = {\r
+    "MIT Kerberos GSSAPI32.DLL",\r
+    "Microsoft SSPI SECUR32.DLL",\r
+    "User-specified GSSAPI DLL",\r
+};\r
+const struct keyvalwhere gsslibkeywords[] = {\r
+    { "gssapi32", 0, -1, -1 },\r
+    { "sspi", 1, -1, -1 },\r
+    { "custom", 2, -1, -1 },\r
+};\r
+\r
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,\r
+                     AcquireCredentialsHandleA,\r
+                     (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,\r
+                      PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));\r
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,\r
+                     InitializeSecurityContextA,\r
+                     (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,\r
+                      ULONG, PSecBufferDesc, ULONG, PCtxtHandle,\r
+                      PSecBufferDesc, PULONG, PTimeStamp));\r
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,\r
+                     FreeContextBuffer,\r
+                     (PVOID));\r
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,\r
+                     FreeCredentialsHandle,\r
+                     (PCredHandle));\r
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,\r
+                     DeleteSecurityContext,\r
+                     (PCtxtHandle));\r
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,\r
+                     QueryContextAttributesA,\r
+                     (PCtxtHandle, ULONG, PVOID));\r
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,\r
+                     MakeSignature,\r
+                     (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));\r
+\r
+typedef struct winSsh_gss_ctx {\r
+    unsigned long maj_stat;\r
+    unsigned long min_stat;\r
+    CredHandle cred_handle;\r
+    CtxtHandle context;\r
+    PCtxtHandle context_handle;\r
+    TimeStamp expiry;\r
+} winSsh_gss_ctx;\r
+\r
+\r
+const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};\r
+\r
+const char *gsslogmsg = NULL;\r
+\r
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);\r
+\r
+struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)\r
+{\r
+    HMODULE module;\r
+    HKEY regkey;\r
+    struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);\r
+\r
+    list->libraries = snewn(3, struct ssh_gss_library);\r
+    list->nlibraries = 0;\r
+\r
+    /* MIT Kerberos GSSAPI implementation */\r
+    /* TODO: For 64-bit builds, check for gssapi64.dll */\r
+    module = NULL;\r
+    if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", &regkey)\r
+       == ERROR_SUCCESS) {\r
+       DWORD type, size;\r
+       LONG ret;\r
+       char *buffer;\r
+\r
+       /* Find out the string length */\r
+        ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size);\r
+\r
+       if (ret == ERROR_SUCCESS && type == REG_SZ) {\r
+           buffer = snewn(size + 20, char);\r
+           ret = RegQueryValueEx(regkey, "InstallDir", NULL,\r
+                                 &type, buffer, &size);\r
+           if (ret == ERROR_SUCCESS && type == REG_SZ) {\r
+               strcat(buffer, "\\bin\\gssapi32.dll");\r
+               module = LoadLibrary(buffer);\r
+           }\r
+           sfree(buffer);\r
+       }\r
+       RegCloseKey(regkey);\r
+    }\r
+    if (module) {\r
+       struct ssh_gss_library *lib =\r
+           &list->libraries[list->nlibraries++];\r
+\r
+       lib->id = 0;\r
+       lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";\r
+       lib->handle = (void *)module;\r
+\r
+#define BIND_GSS_FN(name) \\r
+    lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)\r
+\r
+        BIND_GSS_FN(delete_sec_context);\r
+        BIND_GSS_FN(display_status);\r
+        BIND_GSS_FN(get_mic);\r
+        BIND_GSS_FN(import_name);\r
+        BIND_GSS_FN(init_sec_context);\r
+        BIND_GSS_FN(release_buffer);\r
+        BIND_GSS_FN(release_cred);\r
+        BIND_GSS_FN(release_name);\r
+\r
+#undef BIND_GSS_FN\r
+\r
+        ssh_gssapi_bind_fns(lib);\r
+    }\r
+\r
+    /* Microsoft SSPI Implementation */\r
+    module = load_system32_dll("secur32.dll");\r
+    if (module) {\r
+       struct ssh_gss_library *lib =\r
+           &list->libraries[list->nlibraries++];\r
+\r
+       lib->id = 1;\r
+       lib->gsslogmsg = "Using SSPI from SECUR32.DLL";\r
+       lib->handle = (void *)module;\r
+\r
+       GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);\r
+       GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);\r
+       GET_WINDOWS_FUNCTION(module, FreeContextBuffer);\r
+       GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);\r
+       GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);\r
+       GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);\r
+       GET_WINDOWS_FUNCTION(module, MakeSignature);\r
+\r
+       ssh_sspi_bind_fns(lib);\r
+    }\r
+\r
+    /*\r
+     * Custom GSSAPI DLL.\r
+     */\r
+    module = NULL;\r
+    if (cfg->ssh_gss_custom.path[0]) {\r
+       module = LoadLibrary(cfg->ssh_gss_custom.path);\r
+    }\r
+    if (module) {\r
+       struct ssh_gss_library *lib =\r
+           &list->libraries[list->nlibraries++];\r
+\r
+       lib->id = 2;\r
+       lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified"\r
+                                  " library '%s'", cfg->ssh_gss_custom.path);\r
+       lib->handle = (void *)module;\r
+\r
+#define BIND_GSS_FN(name) \\r
+    lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)\r
+\r
+        BIND_GSS_FN(delete_sec_context);\r
+        BIND_GSS_FN(display_status);\r
+        BIND_GSS_FN(get_mic);\r
+        BIND_GSS_FN(import_name);\r
+        BIND_GSS_FN(init_sec_context);\r
+        BIND_GSS_FN(release_buffer);\r
+        BIND_GSS_FN(release_cred);\r
+        BIND_GSS_FN(release_name);\r
+\r
+#undef BIND_GSS_FN\r
+\r
+        ssh_gssapi_bind_fns(lib);\r
+    }\r
+\r
+\r
+    return list;\r
+}\r
+\r
+void ssh_gss_cleanup(struct ssh_gss_liblist *list)\r
+{\r
+    int i;\r
+\r
+    /*\r
+     * LoadLibrary and FreeLibrary are defined to employ reference\r
+     * counting in the case where the same library is repeatedly\r
+     * loaded, so even in a multiple-sessions-per-process context\r
+     * (not that we currently expect ever to have such a thing on\r
+     * Windows) it's safe to naively FreeLibrary everything here\r
+     * without worrying about destroying it under the feet of\r
+     * another SSH instance still using it.\r
+     */\r
+    for (i = 0; i < list->nlibraries; i++) {\r
+       FreeLibrary((HMODULE)list->libraries[i].handle);\r
+       if (list->libraries[i].id == 2) {\r
+           /* The 'custom' id involves a dynamically allocated message.\r
+            * Note that we must cast away the 'const' to free it. */\r
+           sfree((char *)list->libraries[i].gsslogmsg);\r
+       }\r
+    }\r
+    sfree(list->libraries);\r
+    sfree(list);\r
+}\r
+\r
+static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,\r
+                                          Ssh_gss_buf *mech)\r
+{\r
+    *mech = gss_mech_krb5;\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+\r
+static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,\r
+                                        char *host, Ssh_gss_name *srv_name)\r
+{\r
+    char *pStr;\r
+\r
+    /* Check hostname */\r
+    if (host == NULL) return SSH_GSS_FAILURE;\r
+    \r
+    /* copy it into form host/FQDN */\r
+    pStr = dupcat("host/", host, NULL);\r
+\r
+    *srv_name = (Ssh_gss_name) pStr;\r
+\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,\r
+                                         Ssh_gss_ctx *ctx)\r
+{\r
+    winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);\r
+    memset(winctx, 0, sizeof(winSsh_gss_ctx));\r
+\r
+    /* prepare our "wrapper" structure */\r
+    winctx->maj_stat =  winctx->min_stat = SEC_E_OK;\r
+    winctx->context_handle = NULL;\r
+\r
+    /* Specifying no principal name here means use the credentials of\r
+       the current logged-in user */\r
+\r
+    winctx->maj_stat = p_AcquireCredentialsHandleA(NULL,\r
+                                                  "Kerberos",\r
+                                                  SECPKG_CRED_OUTBOUND,\r
+                                                  NULL,\r
+                                                  NULL,\r
+                                                  NULL,\r
+                                                  NULL,\r
+                                                  &winctx->cred_handle,\r
+                                                  &winctx->expiry);\r
+\r
+    if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;\r
+    \r
+    *ctx = (Ssh_gss_ctx) winctx;\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+\r
+static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,\r
+                                             Ssh_gss_ctx *ctx,\r
+                                             Ssh_gss_name srv_name,\r
+                                             int to_deleg,\r
+                                             Ssh_gss_buf *recv_tok,\r
+                                             Ssh_gss_buf *send_tok)\r
+{\r
+    winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;\r
+    SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value};\r
+    SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value};\r
+    SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok};\r
+    SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok};\r
+    unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT|\r
+       ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;\r
+    unsigned long ret_flags=0;\r
+    \r
+    /* check if we have to delegate ... */\r
+    if (to_deleg) flags |= ISC_REQ_DELEGATE;\r
+    winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle,\r
+                                                   winctx->context_handle,\r
+                                                   (char*) srv_name,\r
+                                                   flags,\r
+                                                   0,          /* reserved */\r
+                                                   SECURITY_NATIVE_DREP,\r
+                                                   &input_desc,\r
+                                                   0,          /* reserved */\r
+                                                   &winctx->context,\r
+                                                   &output_desc,\r
+                                                   &ret_flags,\r
+                                                   &winctx->expiry);\r
+  \r
+    /* prepare for the next round */\r
+    winctx->context_handle = &winctx->context;\r
+    send_tok->value = wsend_tok.pvBuffer;\r
+    send_tok->length = wsend_tok.cbBuffer;\r
+  \r
+    /* check & return our status */\r
+    if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE;\r
+    if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;\r
+    \r
+    return SSH_GSS_FAILURE;\r
+}\r
+\r
+static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,\r
+                                     Ssh_gss_buf *send_tok)\r
+{\r
+    /* check input */\r
+    if (send_tok == NULL) return SSH_GSS_FAILURE;\r
+\r
+    /* free Windows buffer */\r
+    p_FreeContextBuffer(send_tok->value);\r
+    SSH_GSS_CLEAR_BUF(send_tok);\r
+    \r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,\r
+                                         Ssh_gss_ctx *ctx)\r
+{\r
+    winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;\r
+\r
+    /* check input */\r
+    if (winctx == NULL) return SSH_GSS_FAILURE;\r
+\r
+    /* free Windows data */\r
+    p_FreeCredentialsHandle(&winctx->cred_handle);\r
+    p_DeleteSecurityContext(&winctx->context);\r
+\r
+    /* delete our "wrapper" structure */\r
+    sfree(winctx);\r
+    *ctx = (Ssh_gss_ctx) NULL;\r
+\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+\r
+static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,\r
+                                         Ssh_gss_name *srv_name)\r
+{\r
+    char *pStr= (char *) *srv_name;\r
+\r
+    if (pStr == NULL) return SSH_GSS_FAILURE;\r
+    sfree(pStr);\r
+    *srv_name = (Ssh_gss_name) NULL;\r
+\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,\r
+                                           Ssh_gss_ctx ctx, Ssh_gss_buf *buf)\r
+{\r
+    winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;\r
+    char *msg;\r
+\r
+    if (winctx == NULL) return SSH_GSS_FAILURE;\r
+\r
+    /* decode the error code */\r
+    switch (winctx->maj_stat) {\r
+      case SEC_E_OK: msg="SSPI status OK"; break;\r
+      case SEC_E_INVALID_HANDLE: msg="The handle passed to the function"\r
+           " is invalid.";\r
+       break;\r
+      case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break;\r
+      case SEC_E_LOGON_DENIED: msg="The logon failed."; break;\r
+      case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot"\r
+           " be contacted.";\r
+       break;\r
+      case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"\r
+           " security package.";\r
+       break;\r
+      case SEC_E_NO_AUTHENTICATING_AUTHORITY:\r
+       msg="No authority could be contacted for authentication."\r
+           "The domain name of the authenticating party could be wrong,"\r
+           " the domain could be unreachable, or there might have been"\r
+           " a trust relationship failure.";\r
+       break;\r
+      case SEC_E_INSUFFICIENT_MEMORY:\r
+       msg="One or more of the SecBufferDesc structures passed as"\r
+           " an OUT parameter has a buffer that is too small.";\r
+       break;\r
+      case SEC_E_INVALID_TOKEN:\r
+       msg="The error is due to a malformed input token, such as a"\r
+           " token corrupted in transit, a token"\r
+           " of incorrect size, or a token passed into the wrong"\r
+           " security package. Passing a token to"\r
+           " the wrong package can happen if client and server did not"\r
+           " negotiate the proper security package.";\r
+       break;\r
+      default:\r
+       msg = "Internal SSPI error";\r
+       break;\r
+    }\r
+\r
+    buf->value = dupstr(msg);\r
+    buf->length = strlen(buf->value);\r
+    \r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,\r
+                                    Ssh_gss_ctx ctx, Ssh_gss_buf *buf,\r
+                                    Ssh_gss_buf *hash)\r
+{\r
+    winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;\r
+    SecPkgContext_Sizes ContextSizes;\r
+    SecBufferDesc InputBufferDescriptor;\r
+    SecBuffer InputSecurityToken[2];\r
+\r
+    if (winctx == NULL) return SSH_GSS_FAILURE;\r
+  \r
+    winctx->maj_stat = 0;\r
+\r
+    memset(&ContextSizes, 0, sizeof(ContextSizes));\r
+\r
+    winctx->maj_stat = p_QueryContextAttributesA(&winctx->context,\r
+                                                SECPKG_ATTR_SIZES,\r
+                                                &ContextSizes);\r
+    \r
+    if (winctx->maj_stat != SEC_E_OK ||\r
+       ContextSizes.cbMaxSignature == 0)\r
+       return winctx->maj_stat;\r
+\r
+    InputBufferDescriptor.cBuffers = 2;\r
+    InputBufferDescriptor.pBuffers = InputSecurityToken;\r
+    InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;\r
+    InputSecurityToken[0].BufferType = SECBUFFER_DATA;\r
+    InputSecurityToken[0].cbBuffer = buf->length;\r
+    InputSecurityToken[0].pvBuffer = buf->value;\r
+    InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;\r
+    InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;\r
+    InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char);\r
+\r
+    winctx->maj_stat = p_MakeSignature(&winctx->context,\r
+                                      0,\r
+                                      &InputBufferDescriptor,\r
+                                      0);\r
+\r
+    if (winctx->maj_stat == SEC_E_OK) {\r
+       hash->length = InputSecurityToken[1].cbBuffer;\r
+       hash->value = InputSecurityToken[1].pvBuffer;\r
+    }\r
+\r
+    return winctx->maj_stat;\r
+}\r
+\r
+static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,\r
+                                     Ssh_gss_buf *hash)\r
+{\r
+    sfree(hash->value);\r
+    return SSH_GSS_OK;\r
+}\r
+\r
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)\r
+{\r
+    lib->indicate_mech = ssh_sspi_indicate_mech;\r
+    lib->import_name = ssh_sspi_import_name;\r
+    lib->release_name = ssh_sspi_release_name;\r
+    lib->init_sec_context = ssh_sspi_init_sec_context;\r
+    lib->free_tok = ssh_sspi_free_tok;\r
+    lib->acquire_cred = ssh_sspi_acquire_cred;\r
+    lib->release_cred = ssh_sspi_release_cred;\r
+    lib->get_mic = ssh_sspi_get_mic;\r
+    lib->free_mic = ssh_sspi_free_mic;\r
+    lib->display_status = ssh_sspi_display_status;\r
+}\r
+\r
+#else\r
+\r
+/* Dummy function so this source file defines something if NO_GSSAPI\r
+   is defined. */\r
+\r
+void ssh_gss_init(void)\r
+{\r
+}\r
+\r
+#endif\r
diff --git a/putty/WINDOWS/WINHANDL.C b/putty/WINDOWS/WINHANDL.C
new file mode 100644 (file)
index 0000000..dbcab2b
--- /dev/null
@@ -0,0 +1,596 @@
+/*\r
+ * winhandl.c: Module to give Windows front ends the general\r
+ * ability to deal with consoles, pipes, serial ports, or any other\r
+ * type of data stream accessed through a Windows API HANDLE rather\r
+ * than a WinSock SOCKET.\r
+ *\r
+ * We do this by spawning a subthread to continuously try to read\r
+ * from the handle. Every time a read successfully returns some\r
+ * data, the subthread sets an event object which is picked up by\r
+ * the main thread, and the main thread then sets an event in\r
+ * return to instruct the subthread to resume reading.\r
+ * \r
+ * Output works precisely the other way round, in a second\r
+ * subthread. The output subthread should not be attempting to\r
+ * write all the time, because it hasn't always got data _to_\r
+ * write; so the output thread waits for an event object notifying\r
+ * it to _attempt_ a write, and then it sets an event in return\r
+ * when one completes.\r
+ * \r
+ * (It's terribly annoying having to spawn a subthread for each\r
+ * direction of each handle. Technically it isn't necessary for\r
+ * serial ports, since we could use overlapped I/O within the main\r
+ * thread and wait directly on the event objects in the OVERLAPPED\r
+ * structures. However, we can't use this trick for some types of\r
+ * file handle at all - for some reason Windows restricts use of\r
+ * OVERLAPPED to files which were opened with the overlapped flag -\r
+ * and so we must use threads for those. This being the case, it's\r
+ * simplest just to use threads for everything rather than trying\r
+ * to keep track of multiple completely separate mechanisms.)\r
+ */\r
+\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Generic definitions.\r
+ */\r
+\r
+/*\r
+ * Maximum amount of backlog we will allow to build up on an input\r
+ * handle before we stop reading from it.\r
+ */\r
+#define MAX_BACKLOG 32768\r
+\r
+struct handle_generic {\r
+    /*\r
+     * Initial fields common to both handle_input and handle_output\r
+     * structures.\r
+     * \r
+     * The three HANDLEs are set up at initialisation time and are\r
+     * thereafter read-only to both main thread and subthread.\r
+     * `moribund' is only used by the main thread; `done' is\r
+     * written by the main thread before signalling to the\r
+     * subthread. `defunct' and `busy' are used only by the main\r
+     * thread.\r
+     */\r
+    HANDLE h;                         /* the handle itself */\r
+    HANDLE ev_to_main;                /* event used to signal main thread */\r
+    HANDLE ev_from_main;              /* event used to signal back to us */\r
+    int moribund;                     /* are we going to kill this soon? */\r
+    int done;                         /* request subthread to terminate */\r
+    int defunct;                      /* has the subthread already gone? */\r
+    int busy;                         /* operation currently in progress? */\r
+    void *privdata;                   /* for client to remember who they are */\r
+};\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Input threads.\r
+ */\r
+\r
+/*\r
+ * Data required by an input thread.\r
+ */\r
+struct handle_input {\r
+    /*\r
+     * Copy of the handle_generic structure.\r
+     */\r
+    HANDLE h;                         /* the handle itself */\r
+    HANDLE ev_to_main;                /* event used to signal main thread */\r
+    HANDLE ev_from_main;              /* event used to signal back to us */\r
+    int moribund;                     /* are we going to kill this soon? */\r
+    int done;                         /* request subthread to terminate */\r
+    int defunct;                      /* has the subthread already gone? */\r
+    int busy;                         /* operation currently in progress? */\r
+    void *privdata;                   /* for client to remember who they are */\r
+\r
+    /*\r
+     * Data set at initialisation and then read-only.\r
+     */\r
+    int flags;\r
+\r
+    /*\r
+     * Data set by the input thread before signalling ev_to_main,\r
+     * and read by the main thread after receiving that signal.\r
+     */\r
+    char buffer[4096];                /* the data read from the handle */\r
+    DWORD len;                        /* how much data that was */\r
+    int readerr;                      /* lets us know about read errors */\r
+\r
+    /*\r
+     * Callback function called by this module when data arrives on\r
+     * an input handle.\r
+     */\r
+    handle_inputfn_t gotdata;\r
+};\r
+\r
+/*\r
+ * The actual thread procedure for an input thread.\r
+ */\r
+static DWORD WINAPI handle_input_threadfunc(void *param)\r
+{\r
+    struct handle_input *ctx = (struct handle_input *) param;\r
+    OVERLAPPED ovl, *povl;\r
+    HANDLE oev;\r
+    int readret, readlen;\r
+\r
+    if (ctx->flags & HANDLE_FLAG_OVERLAPPED) {\r
+       povl = &ovl;\r
+       oev = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+    } else {\r
+       povl = NULL;\r
+    }\r
+\r
+    if (ctx->flags & HANDLE_FLAG_UNITBUFFER)\r
+       readlen = 1;\r
+    else\r
+       readlen = sizeof(ctx->buffer);\r
+\r
+    while (1) {\r
+       if (povl) {\r
+           memset(povl, 0, sizeof(OVERLAPPED));\r
+           povl->hEvent = oev;\r
+       }\r
+       readret = ReadFile(ctx->h, ctx->buffer,readlen, &ctx->len, povl);\r
+       if (!readret)\r
+           ctx->readerr = GetLastError();\r
+       else\r
+           ctx->readerr = 0;\r
+       if (povl && !readret && ctx->readerr == ERROR_IO_PENDING) {\r
+           WaitForSingleObject(povl->hEvent, INFINITE);\r
+           readret = GetOverlappedResult(ctx->h, povl, &ctx->len, FALSE);\r
+           if (!readret)\r
+               ctx->readerr = GetLastError();\r
+           else\r
+               ctx->readerr = 0;\r
+       }\r
+\r
+       if (!readret) {\r
+           /*\r
+            * Windows apparently sends ERROR_BROKEN_PIPE when a\r
+            * pipe we're reading from is closed normally from the\r
+            * writing end. This is ludicrous; if that situation\r
+            * isn't a natural EOF, _nothing_ is. So if we get that\r
+            * particular error, we pretend it's EOF.\r
+            */\r
+           if (ctx->readerr == ERROR_BROKEN_PIPE)\r
+               ctx->readerr = 0;\r
+           ctx->len = 0;\r
+       }\r
+\r
+       if (readret && ctx->len == 0 &&\r
+           (ctx->flags & HANDLE_FLAG_IGNOREEOF))\r
+           continue;\r
+\r
+       SetEvent(ctx->ev_to_main);\r
+\r
+       if (!ctx->len)\r
+           break;\r
+\r
+       WaitForSingleObject(ctx->ev_from_main, INFINITE);\r
+       if (ctx->done)\r
+           break;                     /* main thread told us to shut down */\r
+    }\r
+\r
+    if (povl)\r
+       CloseHandle(oev);\r
+\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * This is called after a succcessful read, or from the\r
+ * `unthrottle' function. It decides whether or not to begin a new\r
+ * read operation.\r
+ */\r
+static void handle_throttle(struct handle_input *ctx, int backlog)\r
+{\r
+    if (ctx->defunct)\r
+       return;\r
+\r
+    /*\r
+     * If there's a read operation already in progress, do nothing:\r
+     * when that completes, we'll come back here and be in a\r
+     * position to make a better decision.\r
+     */\r
+    if (ctx->busy)\r
+       return;\r
+\r
+    /*\r
+     * Otherwise, we must decide whether to start a new read based\r
+     * on the size of the backlog.\r
+     */\r
+    if (backlog < MAX_BACKLOG) {\r
+       SetEvent(ctx->ev_from_main);\r
+       ctx->busy = TRUE;\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Output threads.\r
+ */\r
+\r
+/*\r
+ * Data required by an output thread.\r
+ */\r
+struct handle_output {\r
+    /*\r
+     * Copy of the handle_generic structure.\r
+     */\r
+    HANDLE h;                         /* the handle itself */\r
+    HANDLE ev_to_main;                /* event used to signal main thread */\r
+    HANDLE ev_from_main;              /* event used to signal back to us */\r
+    int moribund;                     /* are we going to kill this soon? */\r
+    int done;                         /* request subthread to terminate */\r
+    int defunct;                      /* has the subthread already gone? */\r
+    int busy;                         /* operation currently in progress? */\r
+    void *privdata;                   /* for client to remember who they are */\r
+\r
+    /*\r
+     * Data set at initialisation and then read-only.\r
+     */\r
+    int flags;\r
+\r
+    /*\r
+     * Data set by the main thread before signalling ev_from_main,\r
+     * and read by the input thread after receiving that signal.\r
+     */\r
+    char *buffer;                     /* the data to write */\r
+    DWORD len;                        /* how much data there is */\r
+\r
+    /*\r
+     * Data set by the input thread before signalling ev_to_main,\r
+     * and read by the main thread after receiving that signal.\r
+     */\r
+    DWORD lenwritten;                 /* how much data we actually wrote */\r
+    int writeerr;                     /* return value from WriteFile */\r
+\r
+    /*\r
+     * Data only ever read or written by the main thread.\r
+     */\r
+    bufchain queued_data;             /* data still waiting to be written */\r
+\r
+    /*\r
+     * Callback function called when the backlog in the bufchain\r
+     * drops.\r
+     */\r
+    handle_outputfn_t sentdata;\r
+};\r
+\r
+static DWORD WINAPI handle_output_threadfunc(void *param)\r
+{\r
+    struct handle_output *ctx = (struct handle_output *) param;\r
+    OVERLAPPED ovl, *povl;\r
+    HANDLE oev;\r
+    int writeret;\r
+\r
+    if (ctx->flags & HANDLE_FLAG_OVERLAPPED) {\r
+       povl = &ovl;\r
+       oev = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+    } else {\r
+       povl = NULL;\r
+    }\r
+\r
+    while (1) {\r
+       WaitForSingleObject(ctx->ev_from_main, INFINITE);\r
+       if (ctx->done) {\r
+           SetEvent(ctx->ev_to_main);\r
+           break;\r
+       }\r
+       if (povl) {\r
+           memset(povl, 0, sizeof(OVERLAPPED));\r
+           povl->hEvent = oev;\r
+       }\r
+\r
+       writeret = WriteFile(ctx->h, ctx->buffer, ctx->len,\r
+                            &ctx->lenwritten, povl);\r
+       if (!writeret)\r
+           ctx->writeerr = GetLastError();\r
+       else\r
+           ctx->writeerr = 0;\r
+       if (povl && !writeret && GetLastError() == ERROR_IO_PENDING) {\r
+           writeret = GetOverlappedResult(ctx->h, povl,\r
+                                          &ctx->lenwritten, TRUE);\r
+           if (!writeret)\r
+               ctx->writeerr = GetLastError();\r
+           else\r
+               ctx->writeerr = 0;\r
+       }\r
+\r
+       SetEvent(ctx->ev_to_main);\r
+       if (!writeret)\r
+           break;\r
+    }\r
+\r
+    if (povl)\r
+       CloseHandle(oev);\r
+\r
+    return 0;\r
+}\r
+\r
+static void handle_try_output(struct handle_output *ctx)\r
+{\r
+    void *senddata;\r
+    int sendlen;\r
+\r
+    if (!ctx->busy && bufchain_size(&ctx->queued_data)) {\r
+       bufchain_prefix(&ctx->queued_data, &senddata, &sendlen);\r
+       ctx->buffer = senddata;\r
+       ctx->len = sendlen;\r
+       SetEvent(ctx->ev_from_main);\r
+       ctx->busy = TRUE;\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Unified code handling both input and output threads.\r
+ */\r
+\r
+struct handle {\r
+    int output;\r
+    union {\r
+       struct handle_generic g;\r
+       struct handle_input i;\r
+       struct handle_output o;\r
+    } u;\r
+};\r
+\r
+static tree234 *handles_by_evtomain;\r
+\r
+static int handle_cmp_evtomain(void *av, void *bv)\r
+{\r
+    struct handle *a = (struct handle *)av;\r
+    struct handle *b = (struct handle *)bv;\r
+\r
+    if ((unsigned)a->u.g.ev_to_main < (unsigned)b->u.g.ev_to_main)\r
+       return -1;\r
+    else if ((unsigned)a->u.g.ev_to_main > (unsigned)b->u.g.ev_to_main)\r
+       return +1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+static int handle_find_evtomain(void *av, void *bv)\r
+{\r
+    HANDLE *a = (HANDLE *)av;\r
+    struct handle *b = (struct handle *)bv;\r
+\r
+    if ((unsigned)*a < (unsigned)b->u.g.ev_to_main)\r
+       return -1;\r
+    else if ((unsigned)*a > (unsigned)b->u.g.ev_to_main)\r
+       return +1;\r
+    else\r
+       return 0;\r
+}\r
+\r
+struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,\r
+                               void *privdata, int flags)\r
+{\r
+    struct handle *h = snew(struct handle);\r
+    DWORD in_threadid; /* required for Win9x */\r
+\r
+    h->output = FALSE;\r
+    h->u.i.h = handle;\r
+    h->u.i.ev_to_main = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+    h->u.i.ev_from_main = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+    h->u.i.gotdata = gotdata;\r
+    h->u.i.defunct = FALSE;\r
+    h->u.i.moribund = FALSE;\r
+    h->u.i.done = FALSE;\r
+    h->u.i.privdata = privdata;\r
+    h->u.i.flags = flags;\r
+\r
+    if (!handles_by_evtomain)\r
+       handles_by_evtomain = newtree234(handle_cmp_evtomain);\r
+    add234(handles_by_evtomain, h);\r
+\r
+    CreateThread(NULL, 0, handle_input_threadfunc,\r
+                &h->u.i, 0, &in_threadid);\r
+    h->u.i.busy = TRUE;\r
+\r
+    return h;\r
+}\r
+\r
+struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,\r
+                                void *privdata, int flags)\r
+{\r
+    struct handle *h = snew(struct handle);\r
+    DWORD out_threadid; /* required for Win9x */\r
+\r
+    h->output = TRUE;\r
+    h->u.o.h = handle;\r
+    h->u.o.ev_to_main = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+    h->u.o.ev_from_main = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+    h->u.o.busy = FALSE;\r
+    h->u.o.defunct = FALSE;\r
+    h->u.o.moribund = FALSE;\r
+    h->u.o.done = FALSE;\r
+    h->u.o.privdata = privdata;\r
+    bufchain_init(&h->u.o.queued_data);\r
+    h->u.o.sentdata = sentdata;\r
+    h->u.o.flags = flags;\r
+\r
+    if (!handles_by_evtomain)\r
+       handles_by_evtomain = newtree234(handle_cmp_evtomain);\r
+    add234(handles_by_evtomain, h);\r
+\r
+    CreateThread(NULL, 0, handle_output_threadfunc,\r
+                &h->u.o, 0, &out_threadid);\r
+\r
+    return h;\r
+}\r
+\r
+int handle_write(struct handle *h, const void *data, int len)\r
+{\r
+    assert(h->output);\r
+    bufchain_add(&h->u.o.queued_data, data, len);\r
+    handle_try_output(&h->u.o);\r
+    return bufchain_size(&h->u.o.queued_data);\r
+}\r
+\r
+HANDLE *handle_get_events(int *nevents)\r
+{\r
+    HANDLE *ret;\r
+    struct handle *h;\r
+    int i, n, size;\r
+\r
+    /*\r
+     * Go through our tree counting the handle objects currently\r
+     * engaged in useful activity.\r
+     */\r
+    ret = NULL;\r
+    n = size = 0;\r
+    if (handles_by_evtomain) {\r
+       for (i = 0; (h = index234(handles_by_evtomain, i)) != NULL; i++) {\r
+           if (h->u.g.busy) {\r
+               if (n >= size) {\r
+                   size += 32;\r
+                   ret = sresize(ret, size, HANDLE);\r
+               }\r
+               ret[n++] = h->u.g.ev_to_main;\r
+           }\r
+       }\r
+    }\r
+\r
+    *nevents = n;\r
+    return ret;\r
+}\r
+\r
+static void handle_destroy(struct handle *h)\r
+{\r
+    if (h->output)\r
+       bufchain_clear(&h->u.o.queued_data);\r
+    CloseHandle(h->u.g.ev_from_main);\r
+    CloseHandle(h->u.g.ev_to_main);\r
+    del234(handles_by_evtomain, h);\r
+    sfree(h);\r
+}\r
+\r
+void handle_free(struct handle *h)\r
+{\r
+    /*\r
+     * If the handle is currently busy, we cannot immediately free\r
+     * it. Instead we must wait until it's finished its current\r
+     * operation, because otherwise the subthread will write to\r
+     * invalid memory after we free its context from under it.\r
+     */\r
+    assert(h && !h->u.g.moribund);\r
+    if (h->u.g.busy) {\r
+       /*\r
+        * Just set the moribund flag, which will be noticed next\r
+        * time an operation completes.\r
+        */\r
+       h->u.g.moribund = TRUE;\r
+    } else if (h->u.g.defunct) {\r
+       /*\r
+        * There isn't even a subthread; we can go straight to\r
+        * handle_destroy.\r
+        */\r
+       handle_destroy(h);\r
+    } else {\r
+       /*\r
+        * The subthread is alive but not busy, so we now signal it\r
+        * to die. Set the moribund flag to indicate that it will\r
+        * want destroying after that.\r
+        */\r
+       h->u.g.moribund = TRUE;\r
+       h->u.g.done = TRUE;\r
+       h->u.g.busy = TRUE;\r
+       SetEvent(h->u.g.ev_from_main);\r
+    }\r
+}\r
+\r
+void handle_got_event(HANDLE event)\r
+{\r
+    struct handle *h;\r
+\r
+    assert(handles_by_evtomain);\r
+    h = find234(handles_by_evtomain, &event, handle_find_evtomain);\r
+    if (!h) {\r
+       /*\r
+        * This isn't an error condition. If two or more event\r
+        * objects were signalled during the same select operation,\r
+        * and processing of the first caused the second handle to\r
+        * be closed, then it will sometimes happen that we receive\r
+        * an event notification here for a handle which is already\r
+        * deceased. In that situation we simply do nothing.\r
+        */\r
+       return;\r
+    }\r
+\r
+    if (h->u.g.moribund) {\r
+       /*\r
+        * A moribund handle is already treated as dead from the\r
+        * external user's point of view, so do nothing with the\r
+        * actual event. Just signal the thread to die if\r
+        * necessary, or destroy the handle if not.\r
+        */\r
+       if (h->u.g.done) {\r
+           handle_destroy(h);\r
+       } else {\r
+           h->u.g.done = TRUE;\r
+           h->u.g.busy = TRUE;\r
+           SetEvent(h->u.g.ev_from_main);\r
+       }\r
+       return;\r
+    }\r
+\r
+    if (!h->output) {\r
+       int backlog;\r
+\r
+       h->u.i.busy = FALSE;\r
+\r
+       /*\r
+        * A signal on an input handle means data has arrived.\r
+        */\r
+       if (h->u.i.len == 0) {\r
+           /*\r
+            * EOF, or (nearly equivalently) read error.\r
+            */\r
+           h->u.i.gotdata(h, NULL, -h->u.i.readerr);\r
+           h->u.i.defunct = TRUE;\r
+       } else {\r
+           backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len);\r
+           handle_throttle(&h->u.i, backlog);\r
+       }\r
+    } else {\r
+       h->u.o.busy = FALSE;\r
+\r
+       /*\r
+        * A signal on an output handle means we have completed a\r
+        * write. Call the callback to indicate that the output\r
+        * buffer size has decreased, or to indicate an error.\r
+        */\r
+       if (h->u.o.writeerr) {\r
+           /*\r
+            * Write error. Send a negative value to the callback,\r
+            * and mark the thread as defunct (because the output\r
+            * thread is terminating by now).\r
+            */\r
+           h->u.o.sentdata(h, -h->u.o.writeerr);\r
+           h->u.o.defunct = TRUE;\r
+       } else {\r
+           bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten);\r
+           h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data));\r
+           handle_try_output(&h->u.o);\r
+       }\r
+    }\r
+}\r
+\r
+void handle_unthrottle(struct handle *h, int backlog)\r
+{\r
+    assert(!h->output);\r
+    handle_throttle(&h->u.i, backlog);\r
+}\r
+\r
+int handle_backlog(struct handle *h)\r
+{\r
+    assert(h->output);\r
+    return bufchain_size(&h->u.o.queued_data);\r
+}\r
+\r
+void *handle_get_privdata(struct handle *h)\r
+{\r
+    return h->u.g.privdata;\r
+}\r
diff --git a/putty/WINDOWS/WINHELP.C b/putty/WINDOWS/WINHELP.C
new file mode 100644 (file)
index 0000000..c072e05
--- /dev/null
@@ -0,0 +1,138 @@
+/*\r
+ * winhelp.c: centralised functions to launch Windows help files,\r
+ * and to decide whether to use .HLP or .CHM help in any given\r
+ * situation.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+\r
+#ifndef NO_HTMLHELP\r
+#include <htmlhelp.h>\r
+#endif /* NO_HTMLHELP */\r
+\r
+static int requested_help;\r
+static char *help_path;\r
+static int help_has_contents;\r
+#ifndef NO_HTMLHELP\r
+DECL_WINDOWS_FUNCTION(static, HWND, HtmlHelpA, (HWND, LPCSTR, UINT, DWORD));\r
+static char *chm_path;\r
+#endif /* NO_HTMLHELP */\r
+\r
+void init_help(void)\r
+{\r
+    char b[2048], *p, *q, *r;\r
+    FILE *fp;\r
+\r
+    GetModuleFileName(NULL, b, sizeof(b) - 1);\r
+    r = b;\r
+    p = strrchr(b, '\\');\r
+    if (p && p >= r) r = p+1;\r
+    q = strrchr(b, ':');\r
+    if (q && q >= r) r = q+1;\r
+    strcpy(r, PUTTY_HELP_FILE);\r
+    if ( (fp = fopen(b, "r")) != NULL) {\r
+       help_path = dupstr(b);\r
+       fclose(fp);\r
+    } else\r
+       help_path = NULL;\r
+    strcpy(r, PUTTY_HELP_CONTENTS);\r
+    if ( (fp = fopen(b, "r")) != NULL) {\r
+       help_has_contents = TRUE;\r
+       fclose(fp);\r
+    } else\r
+       help_has_contents = FALSE;\r
+\r
+#ifndef NO_HTMLHELP\r
+    strcpy(r, PUTTY_CHM_FILE);\r
+    if ( (fp = fopen(b, "r")) != NULL) {\r
+       chm_path = dupstr(b);\r
+       fclose(fp);\r
+    } else\r
+       chm_path = NULL;\r
+    if (chm_path) {\r
+       HINSTANCE dllHH = load_system32_dll("hhctrl.ocx");\r
+       GET_WINDOWS_FUNCTION(dllHH, HtmlHelpA);\r
+       if (!p_HtmlHelpA) {\r
+           chm_path = NULL;\r
+           if (dllHH)\r
+               FreeLibrary(dllHH);\r
+       }\r
+    }\r
+#endif /* NO_HTMLHELP */\r
+}\r
+\r
+void shutdown_help(void)\r
+{\r
+    /* Nothing to do currently.\r
+     * (If we were running HTML Help single-threaded, this is where we'd\r
+     * call HH_UNINITIALIZE.) */\r
+}\r
+\r
+int has_help(void)\r
+{\r
+    /*\r
+     * FIXME: it would be nice here to disregard help_path on\r
+     * platforms that didn't have WINHLP32. But that's probably\r
+     * unrealistic, since even Vista will have it if the user\r
+     * specifically downloads it.\r
+     */\r
+    return (help_path\r
+#ifndef NO_HTMLHELP\r
+           || chm_path\r
+#endif /* NO_HTMLHELP */\r
+          );\r
+}\r
+\r
+void launch_help(HWND hwnd, const char *topic)\r
+{\r
+    if (topic) {\r
+       int colonpos = strcspn(topic, ":");\r
+\r
+#ifndef NO_HTMLHELP\r
+       if (chm_path) {\r
+           char *fname;\r
+           assert(topic[colonpos] != '\0');\r
+           fname = dupprintf("%s::/%s.html>main", chm_path,\r
+                             topic + colonpos + 1);\r
+           p_HtmlHelpA(hwnd, fname, HH_DISPLAY_TOPIC, 0);\r
+           sfree(fname);\r
+       } else\r
+#endif /* NO_HTMLHELP */\r
+       if (help_path) {\r
+           char *cmd = dupprintf("JI(`',`%.*s')", colonpos, topic);\r
+           WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);\r
+           sfree(cmd);\r
+       }\r
+    } else {\r
+#ifndef NO_HTMLHELP\r
+       if (chm_path) {\r
+           p_HtmlHelpA(hwnd, chm_path, HH_DISPLAY_TOPIC, 0);\r
+       } else\r
+#endif /* NO_HTMLHELP */\r
+       if (help_path) {\r
+           WinHelp(hwnd, help_path,\r
+                   help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);\r
+       }\r
+    }\r
+    requested_help = TRUE;\r
+}\r
+\r
+void quit_help(HWND hwnd)\r
+{\r
+    if (requested_help) {\r
+#ifndef NO_HTMLHELP\r
+       if (chm_path) {\r
+           p_HtmlHelpA(NULL, NULL, HH_CLOSE_ALL, 0);\r
+       } else\r
+#endif /* NO_HTMLHELP */\r
+       if (help_path) {\r
+           WinHelp(hwnd, help_path, HELP_QUIT, 0);\r
+       }\r
+       requested_help = FALSE;\r
+    }\r
+}\r
diff --git a/putty/WINDOWS/WINHELP.H b/putty/WINDOWS/WINHELP.H
new file mode 100644 (file)
index 0000000..d670c0c
--- /dev/null
@@ -0,0 +1,187 @@
+/*\r
+ * winhelp.h - define Windows Help context names.\r
+ * Each definition has the form "winhelp-topic:halibut-topic", where:\r
+ *  - "winhelp-topic" matches up with the \cfg{winhelp-topic} directives\r
+ *    in the Halibut source, and is used for WinHelp;\r
+ *  - "halibut-topic" matches up with the Halibut keywords in the source,\r
+ *    and is used for HTML Help.\r
+ */\r
+\r
+/* Maximum length for WINHELP_CTX_foo strings */\r
+#define WINHELP_CTX_MAXLEN 80\r
+\r
+/* These are used in the cross-platform configuration dialog code. */\r
+\r
+#define HELPCTX(x) P(WINHELP_CTX_ ## x)\r
+\r
+#define WINHELP_CTX_no_help NULL\r
+\r
+#define WINHELP_CTX_session_hostname "session.hostname:config-hostname"\r
+#define WINHELP_CTX_session_saved "session.saved:config-saving"\r
+#define WINHELP_CTX_session_coe "session.coe:config-closeonexit"\r
+#define WINHELP_CTX_logging_main "logging.main:config-logging"\r
+#define WINHELP_CTX_logging_filename "logging.filename:config-logfilename"\r
+#define WINHELP_CTX_logging_exists "logging.exists:config-logfileexists"\r
+#define WINHELP_CTX_logging_flush "logging.flush:config-logflush"\r
+#define WINHELP_CTX_logging_ssh_omit_password "logging.ssh.omitpassword:config-logssh"\r
+#define WINHELP_CTX_logging_ssh_omit_data "logging.ssh.omitdata:config-logssh"\r
+#define WINHELP_CTX_keyboard_backspace "keyboard.backspace:config-backspace"\r
+#define WINHELP_CTX_keyboard_homeend "keyboard.homeend:config-homeend"\r
+#define WINHELP_CTX_keyboard_funkeys "keyboard.funkeys:config-funkeys"\r
+#define WINHELP_CTX_keyboard_appkeypad "keyboard.appkeypad:config-appkeypad"\r
+#define WINHELP_CTX_keyboard_appcursor "keyboard.appcursor:config-appcursor"\r
+#define WINHELP_CTX_keyboard_nethack "keyboard.nethack:config-nethack"\r
+#define WINHELP_CTX_keyboard_compose "keyboard.compose:config-compose"\r
+#define WINHELP_CTX_keyboard_ctrlalt "keyboard.ctrlalt:config-ctrlalt"\r
+#define WINHELP_CTX_features_application "features.application:config-features-application"\r
+#define WINHELP_CTX_features_mouse "features.mouse:config-features-mouse"\r
+#define WINHELP_CTX_features_resize "features.resize:config-features-resize"\r
+#define WINHELP_CTX_features_altscreen "features.altscreen:config-features-altscreen"\r
+#define WINHELP_CTX_features_retitle "features.retitle:config-features-retitle"\r
+#define WINHELP_CTX_features_qtitle "features.qtitle:config-features-qtitle"\r
+#define WINHELP_CTX_features_dbackspace "features.dbackspace:config-features-dbackspace"\r
+#define WINHELP_CTX_features_charset "features.charset:config-features-charset"\r
+#define WINHELP_CTX_features_arabicshaping "features.arabicshaping:config-features-shaping"\r
+#define WINHELP_CTX_features_bidi "features.bidi:config-features-bidi"\r
+#define WINHELP_CTX_terminal_autowrap "terminal.autowrap:config-autowrap"\r
+#define WINHELP_CTX_terminal_decom "terminal.decom:config-decom"\r
+#define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr:config-crlf"\r
+#define WINHELP_CTX_terminal_crhaslf "terminal.crhaslf:config-lfcr"\r
+#define WINHELP_CTX_terminal_bce "terminal.bce:config-erase"\r
+#define WINHELP_CTX_terminal_blink "terminal.blink:config-blink"\r
+#define WINHELP_CTX_terminal_answerback "terminal.answerback:config-answerback"\r
+#define WINHELP_CTX_terminal_localecho "terminal.localecho:config-localecho"\r
+#define WINHELP_CTX_terminal_localedit "terminal.localedit:config-localedit"\r
+#define WINHELP_CTX_terminal_printing "terminal.printing:config-printing"\r
+#define WINHELP_CTX_bell_style "bell.style:config-bellstyle"\r
+#define WINHELP_CTX_bell_taskbar "bell.taskbar:config-belltaskbar"\r
+#define WINHELP_CTX_bell_overload "bell.overload:config-bellovl"\r
+#define WINHELP_CTX_window_size "window.size:config-winsize"\r
+#define WINHELP_CTX_window_resize "window.resize:config-winsizelock"\r
+#define WINHELP_CTX_window_scrollback "window.scrollback:config-scrollback"\r
+#define WINHELP_CTX_window_erased "window.erased:config-erasetoscrollback"\r
+#define WINHELP_CTX_behaviour_closewarn "behaviour.closewarn:config-warnonclose"\r
+#define WINHELP_CTX_behaviour_altf4 "behaviour.altf4:config-altf4"\r
+#define WINHELP_CTX_behaviour_altspace "behaviour.altspace:config-altspace"\r
+#define WINHELP_CTX_behaviour_altonly "behaviour.altonly:config-altonly"\r
+#define WINHELP_CTX_behaviour_alwaysontop "behaviour.alwaysontop:config-alwaysontop"\r
+#define WINHELP_CTX_behaviour_altenter "behaviour.altenter:config-fullscreen"\r
+#define WINHELP_CTX_appearance_cursor "appearance.cursor:config-cursor"\r
+#define WINHELP_CTX_appearance_font "appearance.font:config-font"\r
+#define WINHELP_CTX_appearance_title "appearance.title:config-title"\r
+#define WINHELP_CTX_appearance_hidemouse "appearance.hidemouse:config-mouseptr"\r
+#define WINHELP_CTX_appearance_border "appearance.border:config-winborder"\r
+#define WINHELP_CTX_connection_termtype "connection.termtype:config-termtype"\r
+#define WINHELP_CTX_connection_termspeed "connection.termspeed:config-termspeed"\r
+#define WINHELP_CTX_connection_username "connection.username:config-username"\r
+#define WINHELP_CTX_connection_username_from_env "connection.usernamefromenv:config-username-from-env"\r
+#define WINHELP_CTX_connection_keepalive "connection.keepalive:config-keepalive"\r
+#define WINHELP_CTX_connection_nodelay "connection.nodelay:config-nodelay"\r
+#define WINHELP_CTX_connection_ipversion "connection.ipversion:config-address-family"\r
+#define WINHELP_CTX_connection_tcpkeepalive "connection.tcpkeepalive:config-tcp-keepalives"\r
+#define WINHELP_CTX_connection_loghost "connection.loghost:config-loghost"\r
+#define WINHELP_CTX_proxy_type "proxy.type:config-proxy-type"\r
+#define WINHELP_CTX_proxy_main "proxy.main:config-proxy"\r
+#define WINHELP_CTX_proxy_exclude "proxy.exclude:config-proxy-exclude"\r
+#define WINHELP_CTX_proxy_dns "proxy.dns:config-proxy-dns"\r
+#define WINHELP_CTX_proxy_auth "proxy.auth:config-proxy-auth"\r
+#define WINHELP_CTX_proxy_command "proxy.command:config-proxy-command"\r
+#define WINHELP_CTX_telnet_environ "telnet.environ:config-environ"\r
+#define WINHELP_CTX_telnet_oldenviron "telnet.oldenviron:config-oldenviron"\r
+#define WINHELP_CTX_telnet_passive "telnet.passive:config-ptelnet"\r
+#define WINHELP_CTX_telnet_specialkeys "telnet.specialkeys:config-telnetkey"\r
+#define WINHELP_CTX_telnet_newline "telnet.newline:config-telnetnl"\r
+#define WINHELP_CTX_rlogin_localuser "rlogin.localuser:config-rlogin-localuser"\r
+#define WINHELP_CTX_ssh_nopty "ssh.nopty:config-ssh-pty"\r
+#define WINHELP_CTX_ssh_ttymodes "ssh.ttymodes:config-ttymodes"\r
+#define WINHELP_CTX_ssh_noshell "ssh.noshell:config-ssh-noshell"\r
+#define WINHELP_CTX_ssh_ciphers "ssh.ciphers:config-ssh-encryption"\r
+#define WINHELP_CTX_ssh_protocol "ssh.protocol:config-ssh-prot"\r
+#define WINHELP_CTX_ssh_command "ssh.command:config-command"\r
+#define WINHELP_CTX_ssh_compress "ssh.compress:config-ssh-comp"\r
+#define WINHELP_CTX_ssh_kexlist "ssh.kex.order:config-ssh-kex-order"\r
+#define WINHELP_CTX_ssh_kex_repeat "ssh.kex.repeat:config-ssh-kex-rekey"\r
+#define WINHELP_CTX_ssh_auth_bypass "ssh.auth.bypass:config-ssh-noauth"\r
+#define WINHELP_CTX_ssh_auth_banner "ssh.auth.banner:config-ssh-banner"\r
+#define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey:config-ssh-privkey"\r
+#define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd:config-ssh-agentfwd"\r
+#define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser:config-ssh-changeuser"\r
+#define WINHELP_CTX_ssh_auth_pageant "ssh.auth.pageant:config-ssh-tryagent"\r
+#define WINHELP_CTX_ssh_auth_tis "ssh.auth.tis:config-ssh-tis"\r
+#define WINHELP_CTX_ssh_auth_ki "ssh.auth.ki:config-ssh-ki"\r
+#define WINHELP_CTX_ssh_gssapi "ssh.auth.gssapi:config-ssh-auth-gssapi"\r
+#define WINHELP_CTX_ssh_gssapi_delegation "ssh.auth.gssapi.delegation:config-ssh-auth-gssapi-delegation"\r
+#define WINHELP_CTX_ssh_gssapi_libraries "ssh.auth.gssapi.libraries:config-ssh-auth-gssapi-libraries"\r
+#define WINHELP_CTX_selection_buttons "selection.buttons:config-mouse"\r
+#define WINHELP_CTX_selection_shiftdrag "selection.shiftdrag:config-mouseshift"\r
+#define WINHELP_CTX_selection_rect "selection.rect:config-rectselect"\r
+#define WINHELP_CTX_selection_charclasses "selection.charclasses:config-charclasses"\r
+#define WINHELP_CTX_selection_linedraw "selection.linedraw:config-linedrawpaste"\r
+#define WINHELP_CTX_selection_rtf "selection.rtf:config-rtfpaste"\r
+#define WINHELP_CTX_colours_ansi "colours.ansi:config-ansicolour"\r
+#define WINHELP_CTX_colours_xterm256 "colours.xterm256:config-xtermcolour"\r
+#define WINHELP_CTX_colours_bold "colours.bold:config-boldcolour"\r
+#define WINHELP_CTX_colours_system "colours.system:config-syscolour"\r
+#define WINHELP_CTX_colours_logpal "colours.logpal:config-logpalette"\r
+#define WINHELP_CTX_colours_config "colours.config:config-colourcfg"\r
+#define WINHELP_CTX_translation_codepage "translation.codepage:config-charset"\r
+#define WINHELP_CTX_translation_cjk_ambig_wide "translation.cjkambigwide:config-cjk-ambig-wide"\r
+#define WINHELP_CTX_translation_cyrillic "translation.cyrillic:config-cyr"\r
+#define WINHELP_CTX_translation_linedraw "translation.linedraw:config-linedraw"\r
+#define WINHELP_CTX_ssh_tunnels_x11 "ssh.tunnels.x11:config-ssh-x11"\r
+#define WINHELP_CTX_ssh_tunnels_x11auth "ssh.tunnels.x11auth:config-ssh-x11auth"\r
+#define WINHELP_CTX_ssh_tunnels_xauthority "ssh.tunnels.xauthority:config-ssh-xauthority"\r
+#define WINHELP_CTX_ssh_tunnels_portfwd "ssh.tunnels.portfwd:config-ssh-portfwd"\r
+#define WINHELP_CTX_ssh_tunnels_portfwd_localhost "ssh.tunnels.portfwd.localhost:config-ssh-portfwd-localhost"\r
+#define WINHELP_CTX_ssh_tunnels_portfwd_ipversion "ssh.tunnels.portfwd.ipversion:config-ssh-portfwd-address-family"\r
+#define WINHELP_CTX_ssh_bugs_ignore1 "ssh.bugs.ignore1:config-ssh-bug-ignore1"\r
+#define WINHELP_CTX_ssh_bugs_plainpw1 "ssh.bugs.plainpw1:config-ssh-bug-plainpw1"\r
+#define WINHELP_CTX_ssh_bugs_rsa1 "ssh.bugs.rsa1:config-ssh-bug-rsa1"\r
+#define WINHELP_CTX_ssh_bugs_ignore2 "ssh.bugs.ignore2:config-ssh-bug-ignore2"\r
+#define WINHELP_CTX_ssh_bugs_hmac2 "ssh.bugs.hmac2:config-ssh-bug-hmac2"\r
+#define WINHELP_CTX_ssh_bugs_derivekey2 "ssh.bugs.derivekey2:config-ssh-bug-derivekey2"\r
+#define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2:config-ssh-bug-sig"\r
+#define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2:config-ssh-bug-pksessid2"\r
+#define WINHELP_CTX_ssh_bugs_rekey2 "ssh.bugs.rekey2:config-ssh-bug-rekey"\r
+#define WINHELP_CTX_ssh_bugs_maxpkt2 "ssh.bugs.maxpkt2:config-ssh-bug-maxpkt2"\r
+#define WINHELP_CTX_serial_line "serial.line:config-serial-line"\r
+#define WINHELP_CTX_serial_speed "serial.speed:config-serial-speed"\r
+#define WINHELP_CTX_serial_databits "serial.databits:config-serial-databits"\r
+#define WINHELP_CTX_serial_stopbits "serial.stopbits:config-serial-stopbits"\r
+#define WINHELP_CTX_serial_parity "serial.parity:config-serial-parity"\r
+#define WINHELP_CTX_serial_flow "serial.flow:config-serial-flow"\r
+\r
+#define WINHELP_CTX_pageant_general "pageant.general:pageant"\r
+#define WINHELP_CTX_pageant_keylist "pageant.keylist:pageant-mainwin-keylist"\r
+#define WINHELP_CTX_pageant_addkey "pageant.addkey:pageant-mainwin-addkey"\r
+#define WINHELP_CTX_pageant_remkey "pageant.remkey:pageant-mainwin-remkey"\r
+#define WINHELP_CTX_pgpfingerprints "pgpfingerprints:pgpkeys"\r
+#define WINHELP_CTX_puttygen_general "puttygen.general:pubkey-puttygen"\r
+#define WINHELP_CTX_puttygen_keytype "puttygen.keytype:puttygen-keytype"\r
+#define WINHELP_CTX_puttygen_bits "puttygen.bits:puttygen-strength"\r
+#define WINHELP_CTX_puttygen_generate "puttygen.generate:puttygen-generate"\r
+#define WINHELP_CTX_puttygen_fingerprint "puttygen.fingerprint:puttygen-fingerprint"\r
+#define WINHELP_CTX_puttygen_comment "puttygen.comment:puttygen-comment"\r
+#define WINHELP_CTX_puttygen_passphrase "puttygen.passphrase:puttygen-passphrase"\r
+#define WINHELP_CTX_puttygen_savepriv "puttygen.savepriv:puttygen-savepriv"\r
+#define WINHELP_CTX_puttygen_savepub "puttygen.savepub:puttygen-savepub"\r
+#define WINHELP_CTX_puttygen_pastekey "puttygen.pastekey:puttygen-pastekey"\r
+#define WINHELP_CTX_puttygen_load "puttygen.load:puttygen-load"\r
+#define WINHELP_CTX_puttygen_conversions "puttygen.conversions:puttygen-conversions"\r
+\r
+/* These are used in Windows-specific bits of the frontend.\r
+ * We (ab)use "help context identifiers" (dwContextId) to identify them. */\r
+\r
+#define HELPCTXID(x) WINHELP_CTXID_ ## x\r
+\r
+#define WINHELP_CTXID_no_help 0\r
+#define WINHELP_CTX_errors_hostkey_absent "errors.hostkey.absent:errors-hostkey-absent"\r
+#define WINHELP_CTXID_errors_hostkey_absent 1\r
+#define WINHELP_CTX_errors_hostkey_changed "errors.hostkey.changed:errors-hostkey-wrong"\r
+#define WINHELP_CTXID_errors_hostkey_changed 2\r
+#define WINHELP_CTX_errors_cantloadkey "errors.cantloadkey:errors-cant-load-key"\r
+#define WINHELP_CTXID_errors_cantloadkey 3\r
+#define WINHELP_CTX_option_cleanup "options.cleanup:using-cleanup"\r
+#define WINHELP_CTXID_option_cleanup 4\r
+#define WINHELP_CTX_pgp_fingerprints "pgpfingerprints:pgpkeys"\r
+#define WINHELP_CTXID_pgp_fingerprints 5\r
diff --git a/putty/WINDOWS/WINJUMP.C b/putty/WINDOWS/WINJUMP.C
new file mode 100644 (file)
index 0000000..3455a8a
--- /dev/null
@@ -0,0 +1,706 @@
+/*\r
+ * winjump.c: support for Windows 7 jump lists.\r
+ *\r
+ * The Windows 7 jumplist is a customizable list defined by the\r
+ * application. It is persistent across application restarts: the OS\r
+ * maintains the list when the app is not running. The list is shown\r
+ * when the user right-clicks on the taskbar button of a running app\r
+ * or a pinned non-running application. We use the jumplist to\r
+ * maintain a list of recently started saved sessions, started either\r
+ * by doubleclicking on a saved session, or with the command line\r
+ * "-load" parameter.\r
+ *\r
+ * Since the jumplist is write-only: it can only be replaced and the\r
+ * current list cannot be read, we must maintain the contents of the\r
+ * list persistantly in the registry. The file winstore.h contains\r
+ * functions to directly manipulate these registry entries. This file\r
+ * contains higher level functions to manipulate the jumplist.\r
+ */\r
+\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+#include "storage.h"\r
+\r
+#define MAX_JUMPLIST_ITEMS 30 /* PuTTY will never show more items in\r
+                               * the jumplist than this, regardless of\r
+                               * user preferences. */\r
+\r
+/*\r
+ * COM structures and functions.\r
+ */\r
+#ifndef PROPERTYKEY_DEFINED\r
+#define PROPERTYKEY_DEFINED\r
+typedef struct _tagpropertykey {\r
+    GUID fmtid;\r
+    DWORD pid;\r
+} PROPERTYKEY;\r
+#endif\r
+#ifndef _REFPROPVARIANT_DEFINED\r
+#define _REFPROPVARIANT_DEFINED\r
+typedef PROPVARIANT *REFPROPVARIANT;\r
+#endif\r
+/* MinGW doesn't define this yet: */\r
+#ifndef _PROPVARIANTINIT_DEFINED_\r
+#define _PROPVARIANTINIT_DEFINED_\r
+#define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT))\r
+#endif\r
+\r
+#define IID_IShellLink IID_IShellLinkA\r
+\r
+typedef struct ICustomDestinationListVtbl {\r
+    HRESULT ( __stdcall *QueryInterface ) (\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [in] */  const GUID * const riid,\r
+        /* [out] */ void **ppvObject);\r
+\r
+    ULONG ( __stdcall *AddRef )(\r
+        /* [in] ICustomDestinationList*/ void *This);\r
+\r
+    ULONG ( __stdcall *Release )(\r
+        /* [in] ICustomDestinationList*/ void *This);\r
+\r
+    HRESULT ( __stdcall *SetAppID )(\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [string][in] */ LPCWSTR pszAppID);\r
+\r
+    HRESULT ( __stdcall *BeginList )(\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [out] */ UINT *pcMinSlots,\r
+        /* [in] */  const GUID * const riid,\r
+        /* [out] */ void **ppv);\r
+\r
+    HRESULT ( __stdcall *AppendCategory )(\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [string][in] */ LPCWSTR pszCategory,\r
+        /* [in] IObjectArray*/ void *poa);\r
+\r
+    HRESULT ( __stdcall *AppendKnownCategory )(\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [in] KNOWNDESTCATEGORY*/ int category);\r
+\r
+    HRESULT ( __stdcall *AddUserTasks )(\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [in] IObjectArray*/ void *poa);\r
+\r
+    HRESULT ( __stdcall *CommitList )(\r
+        /* [in] ICustomDestinationList*/ void *This);\r
+\r
+    HRESULT ( __stdcall *GetRemovedDestinations )(\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [in] */ const IID * const riid,\r
+        /* [out] */ void **ppv);\r
+\r
+    HRESULT ( __stdcall *DeleteList )(\r
+        /* [in] ICustomDestinationList*/ void *This,\r
+        /* [string][unique][in] */ LPCWSTR pszAppID);\r
+\r
+    HRESULT ( __stdcall *AbortList )(\r
+        /* [in] ICustomDestinationList*/ void *This);\r
+\r
+} ICustomDestinationListVtbl;\r
+\r
+typedef struct ICustomDestinationList\r
+{\r
+    ICustomDestinationListVtbl *lpVtbl;\r
+} ICustomDestinationList;\r
+\r
+typedef struct IObjectArrayVtbl\r
+{\r
+    HRESULT ( __stdcall *QueryInterface )(\r
+        /* [in] IObjectArray*/ void *This,\r
+        /* [in] */ const GUID * const riid,\r
+        /* [out] */ void **ppvObject);\r
+\r
+    ULONG ( __stdcall *AddRef )(\r
+        /* [in] IObjectArray*/ void *This);\r
+\r
+    ULONG ( __stdcall *Release )(\r
+        /* [in] IObjectArray*/ void *This);\r
+\r
+    HRESULT ( __stdcall *GetCount )(\r
+        /* [in] IObjectArray*/ void *This,\r
+        /* [out] */ UINT *pcObjects);\r
+\r
+    HRESULT ( __stdcall *GetAt )(\r
+        /* [in] IObjectArray*/ void *This,\r
+        /* [in] */ UINT uiIndex,\r
+        /* [in] */ const GUID * const riid,\r
+        /* [out] */ void **ppv);\r
+\r
+} IObjectArrayVtbl;\r
+\r
+typedef struct IObjectArray\r
+{\r
+    IObjectArrayVtbl *lpVtbl;\r
+} IObjectArray;\r
+\r
+typedef struct IShellLinkVtbl\r
+{\r
+    HRESULT ( __stdcall *QueryInterface )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ const GUID * const riid,\r
+        /* [out] */ void **ppvObject);\r
+\r
+    ULONG ( __stdcall *AddRef )(\r
+        /* [in] IShellLink*/ void *This);\r
+\r
+    ULONG ( __stdcall *Release )(\r
+        /* [in] IShellLink*/ void *This);\r
+\r
+    HRESULT ( __stdcall *GetPath )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][out] */ LPSTR pszFile,\r
+        /* [in] */ int cch,\r
+        /* [unique][out][in] */ WIN32_FIND_DATAA *pfd,\r
+        /* [in] */ DWORD fFlags);\r
+\r
+    HRESULT ( __stdcall *GetIDList )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [out] LPITEMIDLIST*/ void **ppidl);\r
+\r
+    HRESULT ( __stdcall *SetIDList )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] LPITEMIDLIST*/ void *pidl);\r
+\r
+    HRESULT ( __stdcall *GetDescription )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][out] */ LPSTR pszName,\r
+        /* [in] */ int cch);\r
+\r
+    HRESULT ( __stdcall *SetDescription )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][in] */ LPCSTR pszName);\r
+\r
+    HRESULT ( __stdcall *GetWorkingDirectory )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][out] */ LPSTR pszDir,\r
+        /* [in] */ int cch);\r
+\r
+    HRESULT ( __stdcall *SetWorkingDirectory )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][in] */ LPCSTR pszDir);\r
+\r
+    HRESULT ( __stdcall *GetArguments )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][out] */ LPSTR pszArgs,\r
+        /* [in] */ int cch);\r
+\r
+    HRESULT ( __stdcall *SetArguments )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][in] */ LPCSTR pszArgs);\r
+\r
+    HRESULT ( __stdcall *GetHotkey )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [out] */ WORD *pwHotkey);\r
+\r
+    HRESULT ( __stdcall *SetHotkey )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ WORD wHotkey);\r
+\r
+    HRESULT ( __stdcall *GetShowCmd )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [out] */ int *piShowCmd);\r
+\r
+    HRESULT ( __stdcall *SetShowCmd )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ int iShowCmd);\r
+\r
+    HRESULT ( __stdcall *GetIconLocation )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][out] */ LPSTR pszIconPath,\r
+        /* [in] */ int cch,\r
+        /* [out] */ int *piIcon);\r
+\r
+    HRESULT ( __stdcall *SetIconLocation )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][in] */ LPCSTR pszIconPath,\r
+        /* [in] */ int iIcon);\r
+\r
+    HRESULT ( __stdcall *SetRelativePath )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][in] */ LPCSTR pszPathRel,\r
+        /* [in] */ DWORD dwReserved);\r
+\r
+    HRESULT ( __stdcall *Resolve )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [unique][in] */ HWND hwnd,\r
+        /* [in] */ DWORD fFlags);\r
+\r
+    HRESULT ( __stdcall *SetPath )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [string][in] */ LPCSTR pszFile);\r
+\r
+} IShellLinkVtbl;\r
+\r
+typedef struct IShellLink\r
+{\r
+    IShellLinkVtbl *lpVtbl;\r
+} IShellLink;\r
+\r
+typedef struct IObjectCollectionVtbl\r
+{\r
+    HRESULT ( __stdcall *QueryInterface )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ const GUID * const riid,\r
+        /* [out] */ void **ppvObject);\r
+\r
+    ULONG ( __stdcall *AddRef )(\r
+        /* [in] IShellLink*/ void *This);\r
+\r
+    ULONG ( __stdcall *Release )(\r
+        /* [in] IShellLink*/ void *This);\r
+\r
+    HRESULT ( __stdcall *GetCount )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [out] */ UINT *pcObjects);\r
+\r
+    HRESULT ( __stdcall *GetAt )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ UINT uiIndex,\r
+        /* [in] */ const GUID * const riid,\r
+        /* [iid_is][out] */ void **ppv);\r
+\r
+    HRESULT ( __stdcall *AddObject )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ void *punk);\r
+\r
+    HRESULT ( __stdcall *AddFromArray )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ IObjectArray *poaSource);\r
+\r
+    HRESULT ( __stdcall *RemoveObjectAt )(\r
+        /* [in] IShellLink*/ void *This,\r
+        /* [in] */ UINT uiIndex);\r
+\r
+    HRESULT ( __stdcall *Clear )(\r
+        /* [in] IShellLink*/ void *This);\r
+\r
+} IObjectCollectionVtbl;\r
+\r
+typedef struct IObjectCollection\r
+{\r
+    IObjectCollectionVtbl *lpVtbl;\r
+} IObjectCollection;\r
+\r
+typedef struct IPropertyStoreVtbl\r
+{\r
+    HRESULT ( __stdcall *QueryInterface )(\r
+        /* [in] IPropertyStore*/ void *This,\r
+        /* [in] */ const GUID * const riid,\r
+        /* [iid_is][out] */ void **ppvObject);\r
+\r
+    ULONG ( __stdcall *AddRef )(\r
+        /* [in] IPropertyStore*/ void *This);\r
+\r
+    ULONG ( __stdcall *Release )(\r
+        /* [in] IPropertyStore*/ void *This);\r
+\r
+    HRESULT ( __stdcall *GetCount )(\r
+        /* [in] IPropertyStore*/ void *This,\r
+        /* [out] */ DWORD *cProps);\r
+\r
+    HRESULT ( __stdcall *GetAt )(\r
+        /* [in] IPropertyStore*/ void *This,\r
+        /* [in] */ DWORD iProp,\r
+        /* [out] */ PROPERTYKEY *pkey);\r
+\r
+    HRESULT ( __stdcall *GetValue )(\r
+        /* [in] IPropertyStore*/ void *This,\r
+        /* [in] */ const PROPERTYKEY * const key,\r
+        /* [out] */ PROPVARIANT *pv);\r
+\r
+    HRESULT ( __stdcall *SetValue )(\r
+        /* [in] IPropertyStore*/ void *This,\r
+        /* [in] */ const PROPERTYKEY * const key,\r
+        /* [in] */ REFPROPVARIANT propvar);\r
+\r
+    HRESULT ( __stdcall *Commit )(\r
+        /* [in] IPropertyStore*/ void *This);\r
+} IPropertyStoreVtbl;\r
+\r
+typedef struct IPropertyStore\r
+{\r
+    IPropertyStoreVtbl *lpVtbl;\r
+} IPropertyStore;\r
+\r
+static const CLSID CLSID_DestinationList = {\r
+    0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}\r
+};\r
+static const CLSID CLSID_ShellLink = {\r
+    0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}\r
+};\r
+static const CLSID CLSID_EnumerableObjectCollection = {\r
+    0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}\r
+};\r
+static const IID IID_IObjectCollection = {\r
+    0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}\r
+};\r
+static const IID IID_IShellLink = {\r
+    0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}\r
+};\r
+static const IID IID_ICustomDestinationList = {\r
+    0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}\r
+};\r
+static const IID IID_IObjectArray = {\r
+    0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}\r
+};\r
+static const IID IID_IPropertyStore = {\r
+    0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99}\r
+};\r
+static const PROPERTYKEY PKEY_Title = {\r
+    {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}},\r
+    0x00000002\r
+};\r
+\r
+/* Type-checking macro to provide arguments for CoCreateInstance() etc.\r
+ * The pointer arithmetic is a compile-time pointer type check that 'obj'\r
+ * really is a 'type **', but is intended to have no effect at runtime. */\r
+#define COMPTR(type, obj) &IID_##type, \\r
+    (void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \\r
+                           - (sizeof((obj)-(type **)(obj))))\r
+\r
+static char putty_path[2048];\r
+\r
+/*\r
+ * Function to make an IShellLink describing a particular PuTTY\r
+ * command. If 'appname' is null, the command run will be the one\r
+ * returned by GetModuleFileName, i.e. our own executable; if it's\r
+ * non-null then it will be assumed to be a filename in the same\r
+ * directory as our own executable, and the return value will be NULL\r
+ * if that file doesn't exist.\r
+ *\r
+ * If 'sessionname' is null then no command line will be passed to the\r
+ * program. If it's non-null, the command line will be that text\r
+ * prefixed with an @ (to load a PuTTY saved session).\r
+ *\r
+ * Hence, you can launch a saved session using make_shell_link(NULL,\r
+ * sessionname), and launch another app using e.g.\r
+ * make_shell_link("puttygen.exe", NULL).\r
+ */\r
+static IShellLink *make_shell_link(const char *appname,\r
+                                   const char *sessionname)\r
+{\r
+    IShellLink *ret;\r
+    char *app_path, *param_string, *desc_string;\r
+    void *psettings_tmp;\r
+    IPropertyStore *pPS;\r
+    PROPVARIANT pv;\r
+\r
+    /* Retrieve path to executable. */\r
+    if (!putty_path[0])\r
+        GetModuleFileName(NULL, putty_path, sizeof(putty_path) - 1);\r
+    if (appname) {\r
+        char *p, *q = putty_path;\r
+        FILE *fp;\r
+\r
+        if ((p = strrchr(q, '\\')) != NULL) q = p+1;\r
+        if ((p = strrchr(q, ':')) != NULL) q = p+1;\r
+        app_path = dupprintf("%.*s%s", (int)(q - putty_path), putty_path,\r
+                             appname);\r
+        if ((fp = fopen(app_path, "r")) == NULL) {\r
+            sfree(app_path);\r
+            return NULL;\r
+        }\r
+        fclose(fp);\r
+    } else {\r
+        app_path = dupstr(putty_path);\r
+    }\r
+\r
+    /* Check if this is a valid session, otherwise don't add. */\r
+    if (sessionname) {\r
+        psettings_tmp = open_settings_r(sessionname);\r
+        if (!psettings_tmp)\r
+            return NULL;\r
+        close_settings_r(psettings_tmp);\r
+    }\r
+\r
+    /* Create the new item. */\r
+    if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL,\r
+                                    CLSCTX_INPROC_SERVER,\r
+                                    COMPTR(IShellLink, &ret))))\r
+        return NULL;\r
+\r
+    /* Set path, parameters, icon and description. */\r
+    ret->lpVtbl->SetPath(ret, app_path);\r
+\r
+    if (sessionname) {\r
+        param_string = dupcat("@", sessionname, NULL);\r
+    } else {\r
+        param_string = dupstr("");\r
+    }\r
+    ret->lpVtbl->SetArguments(ret, param_string);\r
+    sfree(param_string);\r
+\r
+    if (sessionname) {\r
+        desc_string = dupcat("Connect to PuTTY session '",\r
+                             sessionname, "'", NULL);\r
+    } else {\r
+        assert(appname);\r
+        desc_string = dupprintf("Run %.*s", strcspn(appname, "."), appname);\r
+    }\r
+    ret->lpVtbl->SetDescription(ret, desc_string);\r
+    sfree(desc_string);\r
+\r
+    ret->lpVtbl->SetIconLocation(ret, app_path, 0);\r
+\r
+    /* To set the link title, we require the property store of the link. */\r
+    if (SUCCEEDED(ret->lpVtbl->QueryInterface(ret,\r
+                                              COMPTR(IPropertyStore, &pPS)))) {\r
+        PropVariantInit(&pv);\r
+        pv.vt = VT_LPSTR;\r
+        if (sessionname) {\r
+            pv.pszVal = dupstr(sessionname);\r
+        } else {\r
+            assert(appname);\r
+            pv.pszVal = dupprintf("Run %.*s", strcspn(appname, "."), appname);\r
+        }\r
+        pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv);\r
+        sfree(pv.pszVal);\r
+        pPS->lpVtbl->Commit(pPS);\r
+        pPS->lpVtbl->Release(pPS);\r
+    }\r
+\r
+    sfree(app_path);\r
+\r
+    return ret;\r
+}\r
+\r
+/* Updates jumplist from registry. */\r
+static void update_jumplist_from_registry(void)\r
+{\r
+    const char *piterator;\r
+    UINT num_items;\r
+    int jumplist_counter;\r
+    UINT nremoved;\r
+\r
+    /* Variables used by the cleanup code must be initialised to NULL,\r
+     * so that we don't try to free or release them if they were never\r
+     * set up. */\r
+    ICustomDestinationList *pCDL = NULL;\r
+    char *pjumplist_reg_entries = NULL;\r
+    IObjectCollection *collection = NULL;\r
+    IObjectArray *array = NULL;\r
+    IShellLink *link = NULL;\r
+    IObjectArray *pRemoved = NULL;\r
+    int need_abort = FALSE;\r
+\r
+    /*\r
+     * Create an ICustomDestinationList: the top-level object which\r
+     * deals with jump list management.\r
+     */\r
+    if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL,\r
+                                    CLSCTX_INPROC_SERVER,\r
+                                    COMPTR(ICustomDestinationList, &pCDL))))\r
+        goto cleanup;\r
+\r
+    /*\r
+     * Call its BeginList method to start compiling a list. This gives\r
+     * us back 'num_items' (a hint derived from systemwide\r
+     * configuration about how many things to put on the list) and\r
+     * 'pRemoved' (user configuration about things to leave off the\r
+     * list).\r
+     */\r
+    if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items,\r
+                                           COMPTR(IObjectArray, &pRemoved))))\r
+        goto cleanup;\r
+    need_abort = TRUE;\r
+    if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved)))\r
+        nremoved = 0;\r
+\r
+    /*\r
+     * Create an object collection to form the 'Recent Sessions'\r
+     * category on the jump list.\r
+     */\r
+    if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,\r
+                                    NULL, CLSCTX_INPROC_SERVER,\r
+                                    COMPTR(IObjectCollection, &collection))))\r
+        goto cleanup;\r
+\r
+    /*\r
+     * Go through the jump list entries from the registry and add each\r
+     * one to the collection.\r
+     */\r
+    pjumplist_reg_entries = get_jumplist_registry_entries();\r
+    piterator = pjumplist_reg_entries;\r
+    jumplist_counter = 0;\r
+    while (*piterator != '\0' &&\r
+           (jumplist_counter < min(MAX_JUMPLIST_ITEMS, (int) num_items))) {\r
+        link = make_shell_link(NULL, piterator);\r
+        if (link) {\r
+            UINT i;\r
+            int found;\r
+\r
+            /*\r
+             * Check that the link isn't in the user-removed list.\r
+             */\r
+            for (i = 0, found = FALSE; i < nremoved && !found; i++) {\r
+                IShellLink *rlink;\r
+                if (SUCCEEDED(pRemoved->lpVtbl->GetAt\r
+                              (pRemoved, i, COMPTR(IShellLink, &rlink)))) {\r
+                    char desc1[2048], desc2[2048];\r
+                    if (SUCCEEDED(link->lpVtbl->GetDescription\r
+                                  (link, desc1, sizeof(desc1)-1)) &&\r
+                        SUCCEEDED(rlink->lpVtbl->GetDescription\r
+                                  (rlink, desc2, sizeof(desc2)-1)) &&\r
+                        !strcmp(desc1, desc2)) {\r
+                        found = TRUE;\r
+                    }\r
+                    rlink->lpVtbl->Release(rlink);\r
+                }\r
+            }\r
+\r
+            if (!found) {\r
+                collection->lpVtbl->AddObject(collection, link);\r
+                jumplist_counter++;\r
+            }\r
+\r
+            link->lpVtbl->Release(link);\r
+            link = NULL;\r
+        }\r
+        piterator += strlen(piterator) + 1;\r
+    }\r
+    sfree(pjumplist_reg_entries);\r
+    pjumplist_reg_entries = NULL;\r
+\r
+    /*\r
+     * Get the array form of the collection we've just constructed,\r
+     * and put it in the jump list.\r
+     */\r
+    if (!SUCCEEDED(collection->lpVtbl->QueryInterface\r
+                   (collection, COMPTR(IObjectArray, &array))))\r
+        goto cleanup;\r
+\r
+    pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array);\r
+\r
+    /*\r
+     * Create an object collection to form the 'Tasks' category on the\r
+     * jump list.\r
+     */\r
+    if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,\r
+                                    NULL, CLSCTX_INPROC_SERVER,\r
+                                    COMPTR(IObjectCollection, &collection))))\r
+        goto cleanup;\r
+\r
+    /*\r
+     * Add task entries for PuTTYgen and Pageant.\r
+     */\r
+    piterator = "Pageant.exe\0PuTTYgen.exe\0\0";\r
+    while (*piterator != '\0') {\r
+        link = make_shell_link(piterator, NULL);\r
+        if (link) {\r
+            collection->lpVtbl->AddObject(collection, link);\r
+            link->lpVtbl->Release(link);\r
+            link = NULL;\r
+        }\r
+        piterator += strlen(piterator) + 1;\r
+    }\r
+\r
+    /*\r
+     * Get the array form of the collection we've just constructed,\r
+     * and put it in the jump list.\r
+     */\r
+    if (!SUCCEEDED(collection->lpVtbl->QueryInterface\r
+                   (collection, COMPTR(IObjectArray, &array))))\r
+        goto cleanup;\r
+\r
+    pCDL->lpVtbl->AddUserTasks(pCDL, array);\r
+\r
+    /*\r
+     * Now we can clean up the array and collection variables, so as\r
+     * to be able to reuse them.\r
+     */\r
+    array->lpVtbl->Release(array);\r
+    array = NULL;\r
+    collection->lpVtbl->Release(collection);\r
+    collection = NULL;\r
+\r
+    /*\r
+     * Create another object collection to form the user tasks\r
+     * category.\r
+     */\r
+    if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection,\r
+                                    NULL, CLSCTX_INPROC_SERVER,\r
+                                    COMPTR(IObjectCollection, &collection))))\r
+        goto cleanup;\r
+\r
+    /*\r
+     * Get the array form of the collection we've just constructed,\r
+     * and put it in the jump list.\r
+     */\r
+    if (!SUCCEEDED(collection->lpVtbl->QueryInterface\r
+                   (collection, COMPTR(IObjectArray, &array))))\r
+        goto cleanup;\r
+\r
+    pCDL->lpVtbl->AddUserTasks(pCDL, array);\r
+\r
+    /*\r
+     * Now we can clean up the array and collection variables, so as\r
+     * to be able to reuse them.\r
+     */\r
+    array->lpVtbl->Release(array);\r
+    array = NULL;\r
+    collection->lpVtbl->Release(collection);\r
+    collection = NULL;\r
+\r
+    /*\r
+     * Commit the jump list.\r
+     */\r
+    pCDL->lpVtbl->CommitList(pCDL);\r
+    need_abort = FALSE;\r
+\r
+    /*\r
+     * Clean up.\r
+     */\r
+  cleanup:\r
+    if (pRemoved) pRemoved->lpVtbl->Release(pRemoved);\r
+    if (pCDL && need_abort) pCDL->lpVtbl->AbortList(pCDL);\r
+    if (pCDL) pCDL->lpVtbl->Release(pCDL);\r
+    if (collection) collection->lpVtbl->Release(collection);\r
+    if (array) array->lpVtbl->Release(array);\r
+    if (link) link->lpVtbl->Release(link);\r
+    sfree(pjumplist_reg_entries);\r
+}\r
+\r
+/* Clears the entire jumplist. */\r
+void clear_jumplist(void)\r
+{\r
+    ICustomDestinationList *pCDL;\r
+\r
+    if (CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER,\r
+                         COMPTR(ICustomDestinationList, &pCDL)) == S_OK) {\r
+        pCDL->lpVtbl->DeleteList(pCDL, NULL);\r
+        pCDL->lpVtbl->Release(pCDL);\r
+    }\r
+\r
+}\r
+\r
+/* Adds a saved session to the Windows 7 jumplist. */\r
+void add_session_to_jumplist(const char * const sessionname)\r
+{\r
+    if ((osVersion.dwMajorVersion < 6) ||\r
+        (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))\r
+        return;                        /* do nothing on pre-Win7 systems */\r
+\r
+    if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) {\r
+        update_jumplist_from_registry();\r
+    } else {\r
+        /* Make sure we don't leave the jumplist dangling. */\r
+        clear_jumplist();\r
+    }\r
+}\r
+\r
+/* Removes a saved session from the Windows jumplist. */\r
+void remove_session_from_jumplist(const char * const sessionname)\r
+{\r
+    if ((osVersion.dwMajorVersion < 6) ||\r
+        (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion < 1))\r
+        return;                        /* do nothing on pre-Win7 systems */\r
+\r
+    if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) {\r
+        update_jumplist_from_registry();\r
+    } else {\r
+        /* Make sure we don't leave the jumplist dangling. */\r
+        clear_jumplist();\r
+    }\r
+}\r
diff --git a/putty/WINDOWS/WINMISC.C b/putty/WINDOWS/WINMISC.C
new file mode 100644 (file)
index 0000000..e70e77e
--- /dev/null
@@ -0,0 +1,381 @@
+/*\r
+ * winmisc.c: miscellaneous Windows-specific things\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include "putty.h"\r
+#include <security.h>\r
+\r
+OSVERSIONINFO osVersion;\r
+\r
+char *platform_get_x_display(void) {\r
+    /* We may as well check for DISPLAY in case it's useful. */\r
+    return dupstr(getenv("DISPLAY"));\r
+}\r
+\r
+Filename filename_from_str(const char *str)\r
+{\r
+    Filename ret;\r
+    strncpy(ret.path, str, sizeof(ret.path));\r
+    ret.path[sizeof(ret.path)-1] = '\0';\r
+    return ret;\r
+}\r
+\r
+const char *filename_to_str(const Filename *fn)\r
+{\r
+    return fn->path;\r
+}\r
+\r
+int filename_equal(Filename f1, Filename f2)\r
+{\r
+    return !strcmp(f1.path, f2.path);\r
+}\r
+\r
+int filename_is_null(Filename fn)\r
+{\r
+    return !*fn.path;\r
+}\r
+\r
+char *get_username(void)\r
+{\r
+    DWORD namelen;\r
+    char *user;\r
+    int got_username = FALSE;\r
+    DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,\r
+                         (EXTENDED_NAME_FORMAT, LPSTR, PULONG));\r
+\r
+    {\r
+       static int tried_usernameex = FALSE;\r
+       if (!tried_usernameex) {\r
+           /* Not available on Win9x, so load dynamically */\r
+           HMODULE secur32 = load_system32_dll("secur32.dll");\r
+           GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);\r
+           tried_usernameex = TRUE;\r
+       }\r
+    }\r
+\r
+    if (p_GetUserNameExA) {\r
+       /*\r
+        * If available, use the principal -- this avoids the problem\r
+        * that the local username is case-insensitive but Kerberos\r
+        * usernames are case-sensitive.\r
+        */\r
+\r
+       /* Get the length */\r
+       namelen = 0;\r
+       (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);\r
+\r
+       user = snewn(namelen, char);\r
+       got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);\r
+       if (got_username) {\r
+           char *p = strchr(user, '@');\r
+           if (p) *p = 0;\r
+       } else {\r
+           sfree(user);\r
+       }\r
+    }\r
+\r
+    if (!got_username) {\r
+       /* Fall back to local user name */\r
+       namelen = 0;\r
+       if (GetUserName(NULL, &namelen) == FALSE) {\r
+           /*\r
+            * Apparently this doesn't work at least on Windows XP SP2.\r
+            * Thus assume a maximum of 256. It will fail again if it\r
+            * doesn't fit.\r
+            */\r
+           namelen = 256;\r
+       }\r
+\r
+       user = snewn(namelen, char);\r
+       got_username = GetUserName(user, &namelen);\r
+       if (!got_username) {\r
+           sfree(user);\r
+       }\r
+    }\r
+\r
+    return got_username ? user : NULL;\r
+}\r
+\r
+BOOL init_winver(void)\r
+{\r
+    ZeroMemory(&osVersion, sizeof(osVersion));\r
+    osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);\r
+    return GetVersionEx ( (OSVERSIONINFO *) &osVersion);\r
+}\r
+\r
+HMODULE load_system32_dll(const char *libname)\r
+{\r
+    /*\r
+     * Wrapper function to load a DLL out of c:\windows\system32\r
+     * without going through the full DLL search path. (Hence no\r
+     * attack is possible by placing a substitute DLL earlier on that\r
+     * path.)\r
+     */\r
+    static char *sysdir = NULL;\r
+    char *fullpath;\r
+    HMODULE ret;\r
+\r
+    if (!sysdir) {\r
+       int size = 0, len;\r
+       do {\r
+           size = 3*size/2 + 512;\r
+           sysdir = sresize(sysdir, size, char);\r
+           len = GetSystemDirectory(sysdir, size);\r
+       } while (len >= size);\r
+    }\r
+\r
+    fullpath = dupcat(sysdir, "\\", libname, NULL);\r
+    ret = LoadLibrary(fullpath);\r
+    sfree(fullpath);\r
+    return ret;\r
+}\r
+\r
+#ifdef DEBUG\r
+static FILE *debug_fp = NULL;\r
+static HANDLE debug_hdl = INVALID_HANDLE_VALUE;\r
+static int debug_got_console = 0;\r
+\r
+void dputs(char *buf)\r
+{\r
+    DWORD dw;\r
+\r
+    if (!debug_got_console) {\r
+       if (AllocConsole()) {\r
+           debug_got_console = 1;\r
+           debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);\r
+       }\r
+    }\r
+    if (!debug_fp) {\r
+       debug_fp = fopen("debug.log", "w");\r
+    }\r
+\r
+    if (debug_hdl != INVALID_HANDLE_VALUE) {\r
+       WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);\r
+    }\r
+    fputs(buf, debug_fp);\r
+    fflush(debug_fp);\r
+}\r
+#endif\r
+\r
+#ifdef MINEFIELD\r
+/*\r
+ * Minefield - a Windows equivalent for Electric Fence\r
+ */\r
+\r
+#define PAGESIZE 4096\r
+\r
+/*\r
+ * Design:\r
+ * \r
+ * We start by reserving as much virtual address space as Windows\r
+ * will sensibly (or not sensibly) let us have. We flag it all as\r
+ * invalid memory.\r
+ * \r
+ * Any allocation attempt is satisfied by committing one or more\r
+ * pages, with an uncommitted page on either side. The returned\r
+ * memory region is jammed up against the _end_ of the pages.\r
+ * \r
+ * Freeing anything causes instantaneous decommitment of the pages\r
+ * involved, so stale pointers are caught as soon as possible.\r
+ */\r
+\r
+static int minefield_initialised = 0;\r
+static void *minefield_region = NULL;\r
+static long minefield_size = 0;\r
+static long minefield_npages = 0;\r
+static long minefield_curpos = 0;\r
+static unsigned short *minefield_admin = NULL;\r
+static void *minefield_pages = NULL;\r
+\r
+static void minefield_admin_hide(int hide)\r
+{\r
+    int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;\r
+    VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);\r
+}\r
+\r
+static void minefield_init(void)\r
+{\r
+    int size;\r
+    int admin_size;\r
+    int i;\r
+\r
+    for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {\r
+       minefield_region = VirtualAlloc(NULL, size,\r
+                                       MEM_RESERVE, PAGE_NOACCESS);\r
+       if (minefield_region)\r
+           break;\r
+    }\r
+    minefield_size = size;\r
+\r
+    /*\r
+     * Firstly, allocate a section of that to be the admin block.\r
+     * We'll need a two-byte field for each page.\r
+     */\r
+    minefield_admin = minefield_region;\r
+    minefield_npages = minefield_size / PAGESIZE;\r
+    admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);\r
+    minefield_npages = (minefield_size - admin_size) / PAGESIZE;\r
+    minefield_pages = (char *) minefield_region + admin_size;\r
+\r
+    /*\r
+     * Commit the admin region.\r
+     */\r
+    VirtualAlloc(minefield_admin, minefield_npages * 2,\r
+                MEM_COMMIT, PAGE_READWRITE);\r
+\r
+    /*\r
+     * Mark all pages as unused (0xFFFF).\r
+     */\r
+    for (i = 0; i < minefield_npages; i++)\r
+       minefield_admin[i] = 0xFFFF;\r
+\r
+    /*\r
+     * Hide the admin region.\r
+     */\r
+    minefield_admin_hide(1);\r
+\r
+    minefield_initialised = 1;\r
+}\r
+\r
+static void minefield_bomb(void)\r
+{\r
+    div(1, *(int *) minefield_pages);\r
+}\r
+\r
+static void *minefield_alloc(int size)\r
+{\r
+    int npages;\r
+    int pos, lim, region_end, region_start;\r
+    int start;\r
+    int i;\r
+\r
+    npages = (size + PAGESIZE - 1) / PAGESIZE;\r
+\r
+    minefield_admin_hide(0);\r
+\r
+    /*\r
+     * Search from current position until we find a contiguous\r
+     * bunch of npages+2 unused pages.\r
+     */\r
+    pos = minefield_curpos;\r
+    lim = minefield_npages;\r
+    while (1) {\r
+       /* Skip over used pages. */\r
+       while (pos < lim && minefield_admin[pos] != 0xFFFF)\r
+           pos++;\r
+       /* Count unused pages. */\r
+       start = pos;\r
+       while (pos < lim && pos - start < npages + 2 &&\r
+              minefield_admin[pos] == 0xFFFF)\r
+           pos++;\r
+       if (pos - start == npages + 2)\r
+           break;\r
+       /* If we've reached the limit, reset the limit or stop. */\r
+       if (pos >= lim) {\r
+           if (lim == minefield_npages) {\r
+               /* go round and start again at zero */\r
+               lim = minefield_curpos;\r
+               pos = 0;\r
+           } else {\r
+               minefield_admin_hide(1);\r
+               return NULL;\r
+           }\r
+       }\r
+    }\r
+\r
+    minefield_curpos = pos - 1;\r
+\r
+    /*\r
+     * We have npages+2 unused pages starting at start. We leave\r
+     * the first and last of these alone and use the rest.\r
+     */\r
+    region_end = (start + npages + 1) * PAGESIZE;\r
+    region_start = region_end - size;\r
+    /* FIXME: could align here if we wanted */\r
+\r
+    /*\r
+     * Update the admin region.\r
+     */\r
+    for (i = start + 2; i < start + npages + 1; i++)\r
+       minefield_admin[i] = 0xFFFE;   /* used but no region starts here */\r
+    minefield_admin[start + 1] = region_start % PAGESIZE;\r
+\r
+    minefield_admin_hide(1);\r
+\r
+    VirtualAlloc((char *) minefield_pages + region_start, size,\r
+                MEM_COMMIT, PAGE_READWRITE);\r
+    return (char *) minefield_pages + region_start;\r
+}\r
+\r
+static void minefield_free(void *ptr)\r
+{\r
+    int region_start, i, j;\r
+\r
+    minefield_admin_hide(0);\r
+\r
+    region_start = (char *) ptr - (char *) minefield_pages;\r
+    i = region_start / PAGESIZE;\r
+    if (i < 0 || i >= minefield_npages ||\r
+       minefield_admin[i] != region_start % PAGESIZE)\r
+       minefield_bomb();\r
+    for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {\r
+       minefield_admin[j] = 0xFFFF;\r
+    }\r
+\r
+    VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);\r
+\r
+    minefield_admin_hide(1);\r
+}\r
+\r
+static int minefield_get_size(void *ptr)\r
+{\r
+    int region_start, i, j;\r
+\r
+    minefield_admin_hide(0);\r
+\r
+    region_start = (char *) ptr - (char *) minefield_pages;\r
+    i = region_start / PAGESIZE;\r
+    if (i < 0 || i >= minefield_npages ||\r
+       minefield_admin[i] != region_start % PAGESIZE)\r
+       minefield_bomb();\r
+    for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);\r
+\r
+    minefield_admin_hide(1);\r
+\r
+    return j * PAGESIZE - region_start;\r
+}\r
+\r
+void *minefield_c_malloc(size_t size)\r
+{\r
+    if (!minefield_initialised)\r
+       minefield_init();\r
+    return minefield_alloc(size);\r
+}\r
+\r
+void minefield_c_free(void *p)\r
+{\r
+    if (!minefield_initialised)\r
+       minefield_init();\r
+    minefield_free(p);\r
+}\r
+\r
+/*\r
+ * realloc _always_ moves the chunk, for rapid detection of code\r
+ * that assumes it won't.\r
+ */\r
+void *minefield_c_realloc(void *p, size_t size)\r
+{\r
+    size_t oldsize;\r
+    void *q;\r
+    if (!minefield_initialised)\r
+       minefield_init();\r
+    q = minefield_alloc(size);\r
+    oldsize = minefield_get_size(p);\r
+    memcpy(q, p, (oldsize < size ? oldsize : size));\r
+    minefield_free(p);\r
+    return q;\r
+}\r
+\r
+#endif                         /* MINEFIELD */\r
diff --git a/putty/WINDOWS/WINNET.C b/putty/WINDOWS/WINNET.C
new file mode 100644 (file)
index 0000000..c5445c5
--- /dev/null
@@ -0,0 +1,1706 @@
+/*\r
+ * Windows networking abstraction.\r
+ *\r
+ * For the IPv6 code in here I am indebted to Jeroen Massar and\r
+ * unfix.org.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "tree234.h"\r
+\r
+#include <ws2tcpip.h>\r
+\r
+#ifndef NO_IPV6\r
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;\r
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;\r
+#endif\r
+\r
+#define ipv4_is_loopback(addr) \\r
+       ((p_ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L)\r
+\r
+/*\r
+ * We used to typedef struct Socket_tag *Socket.\r
+ *\r
+ * Since we have made the networking abstraction slightly more\r
+ * abstract, Socket no longer means a tcp socket (it could mean\r
+ * an ssl socket).  So now we must use Actual_Socket when we know\r
+ * we are talking about a tcp socket.\r
+ */\r
+typedef struct Socket_tag *Actual_Socket;\r
+\r
+/*\r
+ * Mutable state that goes with a SockAddr: stores information\r
+ * about where in the list of candidate IP(v*) addresses we've\r
+ * currently got to.\r
+ */\r
+typedef struct SockAddrStep_tag SockAddrStep;\r
+struct SockAddrStep_tag {\r
+#ifndef NO_IPV6\r
+    struct addrinfo *ai;              /* steps along addr->ais */\r
+#endif\r
+    int curraddr;\r
+};\r
+\r
+struct Socket_tag {\r
+    const struct socket_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+    char *error;\r
+    SOCKET s;\r
+    Plug plug;\r
+    void *private_ptr;\r
+    bufchain output_data;\r
+    int connected;\r
+    int writable;\r
+    int frozen; /* this causes readability notifications to be ignored */\r
+    int frozen_readable; /* this means we missed at least one readability\r
+                         * notification while we were frozen */\r
+    int localhost_only;                       /* for listening sockets */\r
+    char oobdata[1];\r
+    int sending_oob;\r
+    int oobinline, nodelay, keepalive, privport;\r
+    SockAddr addr;\r
+    SockAddrStep step;\r
+    int port;\r
+    int pending_error;                /* in case send() returns error */\r
+    /*\r
+     * We sometimes need pairs of Socket structures to be linked:\r
+     * if we are listening on the same IPv6 and v4 port, for\r
+     * example. So here we define `parent' and `child' pointers to\r
+     * track this link.\r
+     */\r
+    Actual_Socket parent, child;\r
+};\r
+\r
+struct SockAddr_tag {\r
+    int refcount;\r
+    char *error;\r
+    int resolved;\r
+#ifndef NO_IPV6\r
+    struct addrinfo *ais;             /* Addresses IPv6 style. */\r
+#endif\r
+    unsigned long *addresses;         /* Addresses IPv4 style. */\r
+    int naddresses;\r
+    char hostname[512];                       /* Store an unresolved host name. */\r
+};\r
+\r
+/*\r
+ * Which address family this address belongs to. AF_INET for IPv4;\r
+ * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has\r
+ * not been done and a simple host name is held in this SockAddr\r
+ * structure.\r
+ */\r
+#ifndef NO_IPV6\r
+#define SOCKADDR_FAMILY(addr, step) \\r
+    (!(addr)->resolved ? AF_UNSPEC : \\r
+     (step).ai ? (step).ai->ai_family : AF_INET)\r
+#else\r
+#define SOCKADDR_FAMILY(addr, step) \\r
+    (!(addr)->resolved ? AF_UNSPEC : AF_INET)\r
+#endif\r
+\r
+/*\r
+ * Start a SockAddrStep structure to step through multiple\r
+ * addresses.\r
+ */\r
+#ifndef NO_IPV6\r
+#define START_STEP(addr, step) \\r
+    ((step).ai = (addr)->ais, (step).curraddr = 0)\r
+#else\r
+#define START_STEP(addr, step) \\r
+    ((step).curraddr = 0)\r
+#endif\r
+\r
+static tree234 *sktree;\r
+\r
+static int cmpfortree(void *av, void *bv)\r
+{\r
+    Actual_Socket a = (Actual_Socket) av, b = (Actual_Socket) bv;\r
+    unsigned long as = (unsigned long) a->s, bs = (unsigned long) b->s;\r
+    if (as < bs)\r
+       return -1;\r
+    if (as > bs)\r
+       return +1;\r
+    if (a < b)\r
+       return -1;\r
+    if (a > b)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+static int cmpforsearch(void *av, void *bv)\r
+{\r
+    Actual_Socket b = (Actual_Socket) bv;\r
+    unsigned long as = (unsigned long) av, bs = (unsigned long) b->s;\r
+    if (as < bs)\r
+       return -1;\r
+    if (as > bs)\r
+       return +1;\r
+    return 0;\r
+}\r
+\r
+DECL_WINDOWS_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA));\r
+DECL_WINDOWS_FUNCTION(static, int, WSACleanup, (void));\r
+DECL_WINDOWS_FUNCTION(static, int, closesocket, (SOCKET));\r
+DECL_WINDOWS_FUNCTION(static, u_long, ntohl, (u_long));\r
+DECL_WINDOWS_FUNCTION(static, u_long, htonl, (u_long));\r
+DECL_WINDOWS_FUNCTION(static, u_short, htons, (u_short));\r
+DECL_WINDOWS_FUNCTION(static, u_short, ntohs, (u_short));\r
+DECL_WINDOWS_FUNCTION(static, int, gethostname, (char *, int));\r
+DECL_WINDOWS_FUNCTION(static, struct hostent FAR *, gethostbyname,\r
+                     (const char FAR *));\r
+DECL_WINDOWS_FUNCTION(static, struct servent FAR *, getservbyname,\r
+                     (const char FAR *, const char FAR *));\r
+DECL_WINDOWS_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));\r
+DECL_WINDOWS_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));\r
+DECL_WINDOWS_FUNCTION(static, int, connect,\r
+                     (SOCKET, const struct sockaddr FAR *, int));\r
+DECL_WINDOWS_FUNCTION(static, int, bind,\r
+                     (SOCKET, const struct sockaddr FAR *, int));\r
+DECL_WINDOWS_FUNCTION(static, int, setsockopt,\r
+                     (SOCKET, int, int, const char FAR *, int));\r
+DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int));\r
+DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int));\r
+DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));\r
+DECL_WINDOWS_FUNCTION(static, int, ioctlsocket,\r
+                     (SOCKET, long, u_long FAR *));\r
+DECL_WINDOWS_FUNCTION(static, SOCKET, accept,\r
+                     (SOCKET, struct sockaddr FAR *, int FAR *));\r
+DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));\r
+DECL_WINDOWS_FUNCTION(static, int, WSAIoctl,\r
+                     (SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD,\r
+                      LPDWORD, LPWSAOVERLAPPED,\r
+                      LPWSAOVERLAPPED_COMPLETION_ROUTINE));\r
+#ifndef NO_IPV6\r
+DECL_WINDOWS_FUNCTION(static, int, getaddrinfo,\r
+                     (const char *nodename, const char *servname,\r
+                      const struct addrinfo *hints, struct addrinfo **res));\r
+DECL_WINDOWS_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));\r
+DECL_WINDOWS_FUNCTION(static, int, getnameinfo,\r
+                     (const struct sockaddr FAR * sa, socklen_t salen,\r
+                      char FAR * host, size_t hostlen, char FAR * serv,\r
+                      size_t servlen, int flags));\r
+DECL_WINDOWS_FUNCTION(static, char *, gai_strerror, (int ecode));\r
+DECL_WINDOWS_FUNCTION(static, int, WSAAddressToStringA,\r
+                     (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO,\r
+                      LPSTR, LPDWORD));\r
+#endif\r
+\r
+static HMODULE winsock_module = NULL;\r
+static WSADATA wsadata;\r
+#ifndef NO_IPV6\r
+static HMODULE winsock2_module = NULL;\r
+static HMODULE wship6_module = NULL;\r
+#endif\r
+\r
+int sk_startup(int hi, int lo)\r
+{\r
+    WORD winsock_ver;\r
+\r
+    winsock_ver = MAKEWORD(hi, lo);\r
+\r
+    if (p_WSAStartup(winsock_ver, &wsadata)) {\r
+       return FALSE;\r
+    }\r
+\r
+    if (LOBYTE(wsadata.wVersion) != LOBYTE(winsock_ver)) {\r
+       return FALSE;\r
+    }\r
+\r
+#ifdef NET_SETUP_DIAGNOSTICS\r
+    {\r
+       char buf[80];\r
+       sprintf(buf, "Using WinSock %d.%d", hi, lo);\r
+       logevent(NULL, buf);\r
+    }\r
+#endif\r
+    return TRUE;\r
+}\r
+\r
+void sk_init(void)\r
+{\r
+#ifndef NO_IPV6\r
+    winsock2_module =\r
+#endif\r
+        winsock_module = load_system32_dll("ws2_32.dll");\r
+    if (!winsock_module) {\r
+       winsock_module = load_system32_dll("wsock32.dll");\r
+    }\r
+    if (!winsock_module)\r
+       fatalbox("Unable to load any WinSock library");\r
+\r
+#ifndef NO_IPV6\r
+    /* Check if we have getaddrinfo in Winsock */\r
+    if (GetProcAddress(winsock_module, "getaddrinfo") != NULL) {\r
+#ifdef NET_SETUP_DIAGNOSTICS\r
+       logevent(NULL, "Native WinSock IPv6 support detected");\r
+#endif\r
+       GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo);\r
+       GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo);\r
+       GET_WINDOWS_FUNCTION(winsock_module, getnameinfo);\r
+       GET_WINDOWS_FUNCTION(winsock_module, gai_strerror);\r
+    } else {\r
+       /* Fall back to wship6.dll for Windows 2000 */\r
+       wship6_module = load_system32_dll("wship6.dll");\r
+       if (wship6_module) {\r
+#ifdef NET_SETUP_DIAGNOSTICS\r
+           logevent(NULL, "WSH IPv6 support detected");\r
+#endif\r
+           GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo);\r
+           GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo);\r
+           GET_WINDOWS_FUNCTION(wship6_module, getnameinfo);\r
+           GET_WINDOWS_FUNCTION(wship6_module, gai_strerror);\r
+       } else {\r
+#ifdef NET_SETUP_DIAGNOSTICS\r
+           logevent(NULL, "No IPv6 support detected");\r
+#endif\r
+       }\r
+    }\r
+    GET_WINDOWS_FUNCTION(winsock2_module, WSAAddressToStringA);\r
+#else\r
+#ifdef NET_SETUP_DIAGNOSTICS\r
+    logevent(NULL, "PuTTY was built without IPv6 support");\r
+#endif\r
+#endif\r
+\r
+    GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect);\r
+    GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect);\r
+    GET_WINDOWS_FUNCTION(winsock_module, select);\r
+    GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError);\r
+    GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents);\r
+    GET_WINDOWS_FUNCTION(winsock_module, WSAStartup);\r
+    GET_WINDOWS_FUNCTION(winsock_module, WSACleanup);\r
+    GET_WINDOWS_FUNCTION(winsock_module, closesocket);\r
+    GET_WINDOWS_FUNCTION(winsock_module, ntohl);\r
+    GET_WINDOWS_FUNCTION(winsock_module, htonl);\r
+    GET_WINDOWS_FUNCTION(winsock_module, htons);\r
+    GET_WINDOWS_FUNCTION(winsock_module, ntohs);\r
+    GET_WINDOWS_FUNCTION(winsock_module, gethostname);\r
+    GET_WINDOWS_FUNCTION(winsock_module, gethostbyname);\r
+    GET_WINDOWS_FUNCTION(winsock_module, getservbyname);\r
+    GET_WINDOWS_FUNCTION(winsock_module, inet_addr);\r
+    GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa);\r
+    GET_WINDOWS_FUNCTION(winsock_module, connect);\r
+    GET_WINDOWS_FUNCTION(winsock_module, bind);\r
+    GET_WINDOWS_FUNCTION(winsock_module, setsockopt);\r
+    GET_WINDOWS_FUNCTION(winsock_module, socket);\r
+    GET_WINDOWS_FUNCTION(winsock_module, listen);\r
+    GET_WINDOWS_FUNCTION(winsock_module, send);\r
+    GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket);\r
+    GET_WINDOWS_FUNCTION(winsock_module, accept);\r
+    GET_WINDOWS_FUNCTION(winsock_module, recv);\r
+    GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl);\r
+\r
+    /* Try to get the best WinSock version we can get */\r
+    if (!sk_startup(2,2) &&\r
+       !sk_startup(2,0) &&\r
+       !sk_startup(1,1)) {\r
+       fatalbox("Unable to initialise WinSock");\r
+    }\r
+\r
+    sktree = newtree234(cmpfortree);\r
+}\r
+\r
+void sk_cleanup(void)\r
+{\r
+    Actual_Socket s;\r
+    int i;\r
+\r
+    if (sktree) {\r
+       for (i = 0; (s = index234(sktree, i)) != NULL; i++) {\r
+           p_closesocket(s->s);\r
+       }\r
+       freetree234(sktree);\r
+       sktree = NULL;\r
+    }\r
+\r
+    if (p_WSACleanup)\r
+       p_WSACleanup();\r
+    if (winsock_module)\r
+       FreeLibrary(winsock_module);\r
+#ifndef NO_IPV6\r
+    if (wship6_module)\r
+       FreeLibrary(wship6_module);\r
+#endif\r
+}\r
+\r
+char *winsock_error_string(int error)\r
+{\r
+    switch (error) {\r
+      case WSAEACCES:\r
+       return "Network error: Permission denied";\r
+      case WSAEADDRINUSE:\r
+       return "Network error: Address already in use";\r
+      case WSAEADDRNOTAVAIL:\r
+       return "Network error: Cannot assign requested address";\r
+      case WSAEAFNOSUPPORT:\r
+       return\r
+           "Network error: Address family not supported by protocol family";\r
+      case WSAEALREADY:\r
+       return "Network error: Operation already in progress";\r
+      case WSAECONNABORTED:\r
+       return "Network error: Software caused connection abort";\r
+      case WSAECONNREFUSED:\r
+       return "Network error: Connection refused";\r
+      case WSAECONNRESET:\r
+       return "Network error: Connection reset by peer";\r
+      case WSAEDESTADDRREQ:\r
+       return "Network error: Destination address required";\r
+      case WSAEFAULT:\r
+       return "Network error: Bad address";\r
+      case WSAEHOSTDOWN:\r
+       return "Network error: Host is down";\r
+      case WSAEHOSTUNREACH:\r
+       return "Network error: No route to host";\r
+      case WSAEINPROGRESS:\r
+       return "Network error: Operation now in progress";\r
+      case WSAEINTR:\r
+       return "Network error: Interrupted function call";\r
+      case WSAEINVAL:\r
+       return "Network error: Invalid argument";\r
+      case WSAEISCONN:\r
+       return "Network error: Socket is already connected";\r
+      case WSAEMFILE:\r
+       return "Network error: Too many open files";\r
+      case WSAEMSGSIZE:\r
+       return "Network error: Message too long";\r
+      case WSAENETDOWN:\r
+       return "Network error: Network is down";\r
+      case WSAENETRESET:\r
+       return "Network error: Network dropped connection on reset";\r
+      case WSAENETUNREACH:\r
+       return "Network error: Network is unreachable";\r
+      case WSAENOBUFS:\r
+       return "Network error: No buffer space available";\r
+      case WSAENOPROTOOPT:\r
+       return "Network error: Bad protocol option";\r
+      case WSAENOTCONN:\r
+       return "Network error: Socket is not connected";\r
+      case WSAENOTSOCK:\r
+       return "Network error: Socket operation on non-socket";\r
+      case WSAEOPNOTSUPP:\r
+       return "Network error: Operation not supported";\r
+      case WSAEPFNOSUPPORT:\r
+       return "Network error: Protocol family not supported";\r
+      case WSAEPROCLIM:\r
+       return "Network error: Too many processes";\r
+      case WSAEPROTONOSUPPORT:\r
+       return "Network error: Protocol not supported";\r
+      case WSAEPROTOTYPE:\r
+       return "Network error: Protocol wrong type for socket";\r
+      case WSAESHUTDOWN:\r
+       return "Network error: Cannot send after socket shutdown";\r
+      case WSAESOCKTNOSUPPORT:\r
+       return "Network error: Socket type not supported";\r
+      case WSAETIMEDOUT:\r
+       return "Network error: Connection timed out";\r
+      case WSAEWOULDBLOCK:\r
+       return "Network error: Resource temporarily unavailable";\r
+      case WSAEDISCON:\r
+       return "Network error: Graceful shutdown in progress";\r
+      default:\r
+       return "Unknown network error";\r
+    }\r
+}\r
+\r
+SockAddr sk_namelookup(const char *host, char **canonicalname,\r
+                      int address_family)\r
+{\r
+    SockAddr ret = snew(struct SockAddr_tag);\r
+    unsigned long a;\r
+    char realhost[8192];\r
+    int hint_family;\r
+\r
+    /* Default to IPv4. */\r
+    hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :\r
+#ifndef NO_IPV6\r
+                  address_family == ADDRTYPE_IPV6 ? AF_INET6 :\r
+#endif\r
+                  AF_UNSPEC);\r
+\r
+    /* Clear the structure and default to IPv4. */\r
+    memset(ret, 0, sizeof(struct SockAddr_tag));\r
+#ifndef NO_IPV6\r
+    ret->ais = NULL;\r
+#endif\r
+    ret->addresses = NULL;\r
+    ret->resolved = FALSE;\r
+    ret->refcount = 1;\r
+    *realhost = '\0';\r
+\r
+    if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {\r
+       struct hostent *h = NULL;\r
+       int err;\r
+#ifndef NO_IPV6\r
+       /*\r
+        * Use getaddrinfo when it's available\r
+        */\r
+       if (p_getaddrinfo) {\r
+           struct addrinfo hints;\r
+#ifdef NET_SETUP_DIAGNOSTICS\r
+           logevent(NULL, "Using getaddrinfo() for resolving");\r
+#endif\r
+           memset(&hints, 0, sizeof(hints));\r
+           hints.ai_family = hint_family;\r
+           hints.ai_flags = AI_CANONNAME;\r
+           if ((err = p_getaddrinfo(host, NULL, &hints, &ret->ais)) == 0)\r
+               ret->resolved = TRUE;\r
+       } else\r
+#endif\r
+       {\r
+#ifdef NET_SETUP_DIAGNOSTICS\r
+           logevent(NULL, "Using gethostbyname() for resolving");\r
+#endif\r
+           /*\r
+            * Otherwise use the IPv4-only gethostbyname...\r
+            * (NOTE: we don't use gethostbyname as a fallback!)\r
+            */\r
+           if ( (h = p_gethostbyname(host)) )\r
+               ret->resolved = TRUE;\r
+           else\r
+               err = p_WSAGetLastError();\r
+       }\r
+\r
+       if (!ret->resolved) {\r
+           ret->error = (err == WSAENETDOWN ? "Network is down" :\r
+                         err == WSAHOST_NOT_FOUND ? "Host does not exist" :\r
+                         err == WSATRY_AGAIN ? "Host not found" :\r
+#ifndef NO_IPV6\r
+                         p_getaddrinfo&&p_gai_strerror ? p_gai_strerror(err) :\r
+#endif\r
+                         "gethostbyname: unknown error");\r
+       } else {\r
+           ret->error = NULL;\r
+\r
+#ifndef NO_IPV6\r
+           /* If we got an address info use that... */\r
+           if (ret->ais) {\r
+               /* Are we in IPv4 fallback mode? */\r
+               /* We put the IPv4 address into the a variable so we can further-on use the IPv4 code... */\r
+               if (ret->ais->ai_family == AF_INET)\r
+                   memcpy(&a,\r
+                          (char *) &((SOCKADDR_IN *) ret->ais->\r
+                                     ai_addr)->sin_addr, sizeof(a));\r
+\r
+               if (ret->ais->ai_canonname)\r
+                   strncpy(realhost, ret->ais->ai_canonname, lenof(realhost));\r
+               else\r
+                   strncpy(realhost, host, lenof(realhost));\r
+           }\r
+           /* We used the IPv4-only gethostbyname()... */\r
+           else\r
+#endif\r
+           {\r
+               int n;\r
+               for (n = 0; h->h_addr_list[n]; n++);\r
+               ret->addresses = snewn(n, unsigned long);\r
+               ret->naddresses = n;\r
+               for (n = 0; n < ret->naddresses; n++) {\r
+                   memcpy(&a, h->h_addr_list[n], sizeof(a));\r
+                   ret->addresses[n] = p_ntohl(a);\r
+               }\r
+               memcpy(&a, h->h_addr, sizeof(a));\r
+               /* This way we are always sure the h->h_name is valid :) */\r
+               strncpy(realhost, h->h_name, sizeof(realhost));\r
+           }\r
+       }\r
+    } else {\r
+       /*\r
+        * This must be a numeric IPv4 address because it caused a\r
+        * success return from inet_addr.\r
+        */\r
+       ret->addresses = snewn(1, unsigned long);\r
+       ret->naddresses = 1;\r
+       ret->addresses[0] = p_ntohl(a);\r
+       ret->resolved = TRUE;\r
+       strncpy(realhost, host, sizeof(realhost));\r
+    }\r
+    realhost[lenof(realhost)-1] = '\0';\r
+    *canonicalname = snewn(1+strlen(realhost), char);\r
+    strcpy(*canonicalname, realhost);\r
+    return ret;\r
+}\r
+\r
+SockAddr sk_nonamelookup(const char *host)\r
+{\r
+    SockAddr ret = snew(struct SockAddr_tag);\r
+    ret->error = NULL;\r
+    ret->resolved = FALSE;\r
+#ifndef NO_IPV6\r
+    ret->ais = NULL;\r
+#endif\r
+    ret->addresses = NULL;\r
+    ret->naddresses = 0;\r
+    ret->refcount = 1;\r
+    strncpy(ret->hostname, host, lenof(ret->hostname));\r
+    ret->hostname[lenof(ret->hostname)-1] = '\0';\r
+    return ret;\r
+}\r
+\r
+int sk_nextaddr(SockAddr addr, SockAddrStep *step)\r
+{\r
+#ifndef NO_IPV6\r
+    if (step->ai) {\r
+       if (step->ai->ai_next) {\r
+           step->ai = step->ai->ai_next;\r
+           return TRUE;\r
+       } else\r
+           return FALSE;\r
+    }\r
+#endif\r
+    if (step->curraddr+1 < addr->naddresses) {\r
+       step->curraddr++;\r
+       return TRUE;\r
+    } else {\r
+       return FALSE;\r
+    }\r
+}\r
+\r
+void sk_getaddr(SockAddr addr, char *buf, int buflen)\r
+{\r
+    SockAddrStep step;\r
+    START_STEP(addr, step);\r
+\r
+#ifndef NO_IPV6\r
+    if (step.ai) {\r
+       int err = 0;\r
+       if (p_WSAAddressToStringA) {\r
+           DWORD dwbuflen = buflen;\r
+           err = p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen,\r
+                                       NULL, buf, &dwbuflen);\r
+       } else\r
+           err = -1;\r
+       if (err) {\r
+           strncpy(buf, addr->hostname, buflen);\r
+           if (!buf[0])\r
+               strncpy(buf, "<unknown>", buflen);\r
+           buf[buflen-1] = '\0';\r
+       }\r
+    } else\r
+#endif\r
+    if (SOCKADDR_FAMILY(addr, step) == AF_INET) {\r
+       struct in_addr a;\r
+       assert(addr->addresses && step.curraddr < addr->naddresses);\r
+       a.s_addr = p_htonl(addr->addresses[step.curraddr]);\r
+       strncpy(buf, p_inet_ntoa(a), buflen);\r
+       buf[buflen-1] = '\0';\r
+    } else {\r
+       strncpy(buf, addr->hostname, buflen);\r
+       buf[buflen-1] = '\0';\r
+    }\r
+}\r
+\r
+int sk_hostname_is_local(char *name)\r
+{\r
+    return !strcmp(name, "localhost") ||\r
+          !strcmp(name, "::1") ||\r
+          !strncmp(name, "127.", 4);\r
+}\r
+\r
+static INTERFACE_INFO local_interfaces[16];\r
+static int n_local_interfaces;       /* 0=not yet, -1=failed, >0=number */\r
+\r
+static int ipv4_is_local_addr(struct in_addr addr)\r
+{\r
+    if (ipv4_is_loopback(addr))\r
+       return 1;                      /* loopback addresses are local */\r
+    if (!n_local_interfaces) {\r
+       SOCKET s = p_socket(AF_INET, SOCK_DGRAM, 0);\r
+       DWORD retbytes;\r
+\r
+       if (p_WSAIoctl &&\r
+           p_WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0,\r
+                      local_interfaces, sizeof(local_interfaces),\r
+                      &retbytes, NULL, NULL) == 0)\r
+           n_local_interfaces = retbytes / sizeof(INTERFACE_INFO);\r
+       else\r
+           logevent(NULL, "Unable to get list of local IP addresses");\r
+    }\r
+    if (n_local_interfaces > 0) {\r
+       int i;\r
+       for (i = 0; i < n_local_interfaces; i++) {\r
+           SOCKADDR_IN *address =\r
+               (SOCKADDR_IN *)&local_interfaces[i].iiAddress;\r
+           if (address->sin_addr.s_addr == addr.s_addr)\r
+               return 1;              /* this address is local */\r
+       }\r
+    }\r
+    return 0;                 /* this address is not local */\r
+}\r
+\r
+int sk_address_is_local(SockAddr addr)\r
+{\r
+    SockAddrStep step;\r
+    int family;\r
+    START_STEP(addr, step);\r
+    family = SOCKADDR_FAMILY(addr, step);\r
+\r
+#ifndef NO_IPV6\r
+    if (family == AF_INET6) {\r
+       return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)step.ai->ai_addr);\r
+    } else\r
+#endif\r
+    if (family == AF_INET) {\r
+#ifndef NO_IPV6\r
+       if (step.ai) {\r
+           return ipv4_is_local_addr(((struct sockaddr_in *)step.ai->ai_addr)\r
+                                     ->sin_addr);\r
+       } else\r
+#endif\r
+       {\r
+           struct in_addr a;\r
+           assert(addr->addresses && step.curraddr < addr->naddresses);\r
+           a.s_addr = p_htonl(addr->addresses[step.curraddr]);\r
+           return ipv4_is_local_addr(a);\r
+       }\r
+    } else {\r
+       assert(family == AF_UNSPEC);\r
+       return 0;                      /* we don't know; assume not */\r
+    }\r
+}\r
+\r
+int sk_addrtype(SockAddr addr)\r
+{\r
+    SockAddrStep step;\r
+    int family;\r
+    START_STEP(addr, step);\r
+    family = SOCKADDR_FAMILY(addr, step);\r
+\r
+    return (family == AF_INET ? ADDRTYPE_IPV4 :\r
+#ifndef NO_IPV6\r
+           family == AF_INET6 ? ADDRTYPE_IPV6 :\r
+#endif\r
+           ADDRTYPE_NAME);\r
+}\r
+\r
+void sk_addrcopy(SockAddr addr, char *buf)\r
+{\r
+    SockAddrStep step;\r
+    int family;\r
+    START_STEP(addr, step);\r
+    family = SOCKADDR_FAMILY(addr, step);\r
+\r
+    assert(family != AF_UNSPEC);\r
+#ifndef NO_IPV6\r
+    if (step.ai) {\r
+       if (family == AF_INET)\r
+           memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr,\r
+                  sizeof(struct in_addr));\r
+       else if (family == AF_INET6)\r
+           memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr,\r
+                  sizeof(struct in6_addr));\r
+       else\r
+           assert(FALSE);\r
+    } else\r
+#endif\r
+    if (family == AF_INET) {\r
+       struct in_addr a;\r
+       assert(addr->addresses && step.curraddr < addr->naddresses);\r
+       a.s_addr = p_htonl(addr->addresses[step.curraddr]);\r
+       memcpy(buf, (char*) &a.s_addr, 4);\r
+    }\r
+}\r
+\r
+void sk_addr_free(SockAddr addr)\r
+{\r
+    if (--addr->refcount > 0)\r
+       return;\r
+#ifndef NO_IPV6\r
+    if (addr->ais && p_freeaddrinfo)\r
+       p_freeaddrinfo(addr->ais);\r
+#endif\r
+    if (addr->addresses)\r
+       sfree(addr->addresses);\r
+    sfree(addr);\r
+}\r
+\r
+SockAddr sk_addr_dup(SockAddr addr)\r
+{\r
+    addr->refcount++;\r
+    return addr;\r
+}\r
+\r
+static Plug sk_tcp_plug(Socket sock, Plug p)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    Plug ret = s->plug;\r
+    if (p)\r
+       s->plug = p;\r
+    return ret;\r
+}\r
+\r
+static void sk_tcp_flush(Socket s)\r
+{\r
+    /*\r
+     * We send data to the socket as soon as we can anyway,\r
+     * so we don't need to do anything here.  :-)\r
+     */\r
+}\r
+\r
+static void sk_tcp_close(Socket s);\r
+static int sk_tcp_write(Socket s, const char *data, int len);\r
+static int sk_tcp_write_oob(Socket s, const char *data, int len);\r
+static void sk_tcp_set_private_ptr(Socket s, void *ptr);\r
+static void *sk_tcp_get_private_ptr(Socket s);\r
+static void sk_tcp_set_frozen(Socket s, int is_frozen);\r
+static const char *sk_tcp_socket_error(Socket s);\r
+\r
+extern char *do_select(SOCKET skt, int startup);\r
+\r
+Socket sk_register(void *sock, Plug plug)\r
+{\r
+    static const struct socket_function_table fn_table = {\r
+       sk_tcp_plug,\r
+       sk_tcp_close,\r
+       sk_tcp_write,\r
+       sk_tcp_write_oob,\r
+       sk_tcp_flush,\r
+       sk_tcp_set_private_ptr,\r
+       sk_tcp_get_private_ptr,\r
+       sk_tcp_set_frozen,\r
+       sk_tcp_socket_error\r
+    };\r
+\r
+    DWORD err;\r
+    char *errstr;\r
+    Actual_Socket ret;\r
+\r
+    /*\r
+     * Create Socket structure.\r
+     */\r
+    ret = snew(struct Socket_tag);\r
+    ret->fn = &fn_table;\r
+    ret->error = NULL;\r
+    ret->plug = plug;\r
+    bufchain_init(&ret->output_data);\r
+    ret->writable = 1;                /* to start with */\r
+    ret->sending_oob = 0;\r
+    ret->frozen = 1;\r
+    ret->frozen_readable = 0;\r
+    ret->localhost_only = 0;          /* unused, but best init anyway */\r
+    ret->pending_error = 0;\r
+    ret->parent = ret->child = NULL;\r
+    ret->addr = NULL;\r
+\r
+    ret->s = (SOCKET)sock;\r
+\r
+    if (ret->s == INVALID_SOCKET) {\r
+       err = p_WSAGetLastError();\r
+       ret->error = winsock_error_string(err);\r
+       return (Socket) ret;\r
+    }\r
+\r
+    ret->oobinline = 0;\r
+\r
+    /* Set up a select mechanism. This could be an AsyncSelect on a\r
+     * window, or an EventSelect on an event object. */\r
+    errstr = do_select(ret->s, 1);\r
+    if (errstr) {\r
+       ret->error = errstr;\r
+       return (Socket) ret;\r
+    }\r
+\r
+    add234(sktree, ret);\r
+\r
+    return (Socket) ret;\r
+}\r
+\r
+static DWORD try_connect(Actual_Socket sock)\r
+{\r
+    SOCKET s;\r
+#ifndef NO_IPV6\r
+    SOCKADDR_IN6 a6;\r
+#endif\r
+    SOCKADDR_IN a;\r
+    DWORD err;\r
+    char *errstr;\r
+    short localport;\r
+    int family;\r
+\r
+    if (sock->s != INVALID_SOCKET) {\r
+       do_select(sock->s, 0);\r
+        p_closesocket(sock->s);\r
+    }\r
+\r
+    plug_log(sock->plug, 0, sock->addr, sock->port, NULL, 0);\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    family = SOCKADDR_FAMILY(sock->addr, sock->step);\r
+\r
+    /*\r
+     * Remove the socket from the tree before we overwrite its\r
+     * internal socket id, because that forms part of the tree's\r
+     * sorting criterion. We'll add it back before exiting this\r
+     * function, whether we changed anything or not.\r
+     */\r
+    del234(sktree, sock);\r
+\r
+    s = p_socket(family, SOCK_STREAM, 0);\r
+    sock->s = s;\r
+\r
+    if (s == INVALID_SOCKET) {\r
+       err = p_WSAGetLastError();\r
+       sock->error = winsock_error_string(err);\r
+       goto ret;\r
+    }\r
+\r
+    if (sock->oobinline) {\r
+       BOOL b = TRUE;\r
+       p_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b));\r
+    }\r
+\r
+    if (sock->nodelay) {\r
+       BOOL b = TRUE;\r
+       p_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b));\r
+    }\r
+\r
+    if (sock->keepalive) {\r
+       BOOL b = TRUE;\r
+       p_setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b));\r
+    }\r
+\r
+    /*\r
+     * Bind to local address.\r
+     */\r
+    if (sock->privport)\r
+       localport = 1023;              /* count from 1023 downwards */\r
+    else\r
+       localport = 0;                 /* just use port 0 (ie winsock picks) */\r
+\r
+    /* Loop round trying to bind */\r
+    while (1) {\r
+       int sockcode;\r
+\r
+#ifndef NO_IPV6\r
+       if (family == AF_INET6) {\r
+           memset(&a6, 0, sizeof(a6));\r
+           a6.sin6_family = AF_INET6;\r
+          /*a6.sin6_addr = in6addr_any; */ /* == 0 done by memset() */\r
+           a6.sin6_port = p_htons(localport);\r
+       } else\r
+#endif\r
+       {\r
+           a.sin_family = AF_INET;\r
+           a.sin_addr.s_addr = p_htonl(INADDR_ANY);\r
+           a.sin_port = p_htons(localport);\r
+       }\r
+#ifndef NO_IPV6\r
+       sockcode = p_bind(s, (family == AF_INET6 ?\r
+                             (struct sockaddr *) &a6 :\r
+                             (struct sockaddr *) &a),\r
+                         (family == AF_INET6 ? sizeof(a6) : sizeof(a)));\r
+#else\r
+       sockcode = p_bind(s, (struct sockaddr *) &a, sizeof(a));\r
+#endif\r
+       if (sockcode != SOCKET_ERROR) {\r
+           err = 0;\r
+           break;                     /* done */\r
+       } else {\r
+           err = p_WSAGetLastError();\r
+           if (err != WSAEADDRINUSE)  /* failed, for a bad reason */\r
+               break;\r
+       }\r
+\r
+       if (localport == 0)\r
+           break;                     /* we're only looping once */\r
+       localport--;\r
+       if (localport == 0)\r
+           break;                     /* we might have got to the end */\r
+    }\r
+\r
+    if (err) {\r
+       sock->error = winsock_error_string(err);\r
+       goto ret;\r
+    }\r
+\r
+    /*\r
+     * Connect to remote address.\r
+     */\r
+#ifndef NO_IPV6\r
+    if (sock->step.ai) {\r
+       if (family == AF_INET6) {\r
+           a6.sin6_family = AF_INET6;\r
+           a6.sin6_port = p_htons((short) sock->port);\r
+           a6.sin6_addr =\r
+               ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_addr;\r
+           a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_flowinfo;\r
+           a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_scope_id;\r
+       } else {\r
+           a.sin_family = AF_INET;\r
+           a.sin_addr =\r
+               ((struct sockaddr_in *) sock->step.ai->ai_addr)->sin_addr;\r
+           a.sin_port = p_htons((short) sock->port);\r
+       }\r
+    } else\r
+#endif\r
+    {\r
+       assert(sock->addr->addresses && sock->step.curraddr < sock->addr->naddresses);\r
+       a.sin_family = AF_INET;\r
+       a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->step.curraddr]);\r
+       a.sin_port = p_htons((short) sock->port);\r
+    }\r
+\r
+    /* Set up a select mechanism. This could be an AsyncSelect on a\r
+     * window, or an EventSelect on an event object. */\r
+    errstr = do_select(s, 1);\r
+    if (errstr) {\r
+       sock->error = errstr;\r
+       err = 1;\r
+       goto ret;\r
+    }\r
+\r
+    if ((\r
+#ifndef NO_IPV6\r
+           p_connect(s,\r
+                     ((family == AF_INET6) ? (struct sockaddr *) &a6 :\r
+                      (struct sockaddr *) &a),\r
+                     (family == AF_INET6) ? sizeof(a6) : sizeof(a))\r
+#else\r
+           p_connect(s, (struct sockaddr *) &a, sizeof(a))\r
+#endif\r
+       ) == SOCKET_ERROR) {\r
+       err = p_WSAGetLastError();\r
+       /*\r
+        * We expect a potential EWOULDBLOCK here, because the\r
+        * chances are the front end has done a select for\r
+        * FD_CONNECT, so that connect() will complete\r
+        * asynchronously.\r
+        */\r
+       if ( err != WSAEWOULDBLOCK ) {\r
+           sock->error = winsock_error_string(err);\r
+           goto ret;\r
+       }\r
+    } else {\r
+       /*\r
+        * If we _don't_ get EWOULDBLOCK, the connect has completed\r
+        * and we should set the socket as writable.\r
+        */\r
+       sock->writable = 1;\r
+    }\r
+\r
+    err = 0;\r
+\r
+    ret:\r
+\r
+    /*\r
+     * No matter what happened, put the socket back in the tree.\r
+     */\r
+    add234(sktree, sock);\r
+\r
+    if (err)\r
+       plug_log(sock->plug, 1, sock->addr, sock->port, sock->error, err);\r
+    return err;\r
+}\r
+\r
+Socket sk_new(SockAddr addr, int port, int privport, int oobinline,\r
+             int nodelay, int keepalive, Plug plug)\r
+{\r
+    static const struct socket_function_table fn_table = {\r
+       sk_tcp_plug,\r
+       sk_tcp_close,\r
+       sk_tcp_write,\r
+       sk_tcp_write_oob,\r
+       sk_tcp_flush,\r
+       sk_tcp_set_private_ptr,\r
+       sk_tcp_get_private_ptr,\r
+       sk_tcp_set_frozen,\r
+       sk_tcp_socket_error\r
+    };\r
+\r
+    Actual_Socket ret;\r
+    DWORD err;\r
+\r
+    /*\r
+     * Create Socket structure.\r
+     */\r
+    ret = snew(struct Socket_tag);\r
+    ret->fn = &fn_table;\r
+    ret->error = NULL;\r
+    ret->plug = plug;\r
+    bufchain_init(&ret->output_data);\r
+    ret->connected = 0;                       /* to start with */\r
+    ret->writable = 0;                /* to start with */\r
+    ret->sending_oob = 0;\r
+    ret->frozen = 0;\r
+    ret->frozen_readable = 0;\r
+    ret->localhost_only = 0;          /* unused, but best init anyway */\r
+    ret->pending_error = 0;\r
+    ret->parent = ret->child = NULL;\r
+    ret->oobinline = oobinline;\r
+    ret->nodelay = nodelay;\r
+    ret->keepalive = keepalive;\r
+    ret->privport = privport;\r
+    ret->port = port;\r
+    ret->addr = addr;\r
+    START_STEP(ret->addr, ret->step);\r
+    ret->s = INVALID_SOCKET;\r
+\r
+    err = 0;\r
+    do {\r
+        err = try_connect(ret);\r
+    } while (err && sk_nextaddr(ret->addr, &ret->step));\r
+\r
+    return (Socket) ret;\r
+}\r
+\r
+Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,\r
+                     int orig_address_family)\r
+{\r
+    static const struct socket_function_table fn_table = {\r
+       sk_tcp_plug,\r
+       sk_tcp_close,\r
+       sk_tcp_write,\r
+       sk_tcp_write_oob,\r
+       sk_tcp_flush,\r
+       sk_tcp_set_private_ptr,\r
+       sk_tcp_get_private_ptr,\r
+       sk_tcp_set_frozen,\r
+       sk_tcp_socket_error\r
+    };\r
+\r
+    SOCKET s;\r
+#ifndef NO_IPV6\r
+    SOCKADDR_IN6 a6;\r
+#endif\r
+    SOCKADDR_IN a;\r
+\r
+    DWORD err;\r
+    char *errstr;\r
+    Actual_Socket ret;\r
+    int retcode;\r
+    int on = 1;\r
+\r
+    int address_family;\r
+\r
+    /*\r
+     * Create Socket structure.\r
+     */\r
+    ret = snew(struct Socket_tag);\r
+    ret->fn = &fn_table;\r
+    ret->error = NULL;\r
+    ret->plug = plug;\r
+    bufchain_init(&ret->output_data);\r
+    ret->writable = 0;                /* to start with */\r
+    ret->sending_oob = 0;\r
+    ret->frozen = 0;\r
+    ret->frozen_readable = 0;\r
+    ret->localhost_only = local_host_only;\r
+    ret->pending_error = 0;\r
+    ret->parent = ret->child = NULL;\r
+    ret->addr = NULL;\r
+\r
+    /*\r
+     * Translate address_family from platform-independent constants\r
+     * into local reality.\r
+     */\r
+    address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET :\r
+#ifndef NO_IPV6\r
+                     orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 :\r
+#endif\r
+                     AF_UNSPEC);\r
+\r
+    /*\r
+     * Our default, if passed the `don't care' value\r
+     * ADDRTYPE_UNSPEC, is to listen on IPv4. If IPv6 is supported,\r
+     * we will also set up a second socket listening on IPv6, but\r
+     * the v4 one is primary since that ought to work even on\r
+     * non-v6-supporting systems.\r
+     */\r
+    if (address_family == AF_UNSPEC) address_family = AF_INET;\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    s = p_socket(address_family, SOCK_STREAM, 0);\r
+    ret->s = s;\r
+\r
+    if (s == INVALID_SOCKET) {\r
+       err = p_WSAGetLastError();\r
+       ret->error = winsock_error_string(err);\r
+       return (Socket) ret;\r
+    }\r
+\r
+    ret->oobinline = 0;\r
+\r
+    p_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));\r
+\r
+#ifndef NO_IPV6\r
+       if (address_family == AF_INET6) {\r
+           memset(&a6, 0, sizeof(a6));\r
+           a6.sin6_family = AF_INET6;\r
+           /* FIXME: srcaddr is ignored for IPv6, because I (SGT) don't\r
+            * know how to do it. :-)\r
+            * (jeroen:) saddr is specified as an address.. eg 2001:db8::1\r
+            * Thus we need either a parser that understands [2001:db8::1]:80\r
+            * style addresses and/or enhance this to understand hostnames too. */\r
+           if (local_host_only)\r
+               a6.sin6_addr = in6addr_loopback;\r
+           else\r
+               a6.sin6_addr = in6addr_any;\r
+           a6.sin6_port = p_htons(port);\r
+       } else\r
+#endif\r
+       {\r
+           int got_addr = 0;\r
+           a.sin_family = AF_INET;\r
+\r
+           /*\r
+            * Bind to source address. First try an explicitly\r
+            * specified one...\r
+            */\r
+           if (srcaddr) {\r
+               a.sin_addr.s_addr = p_inet_addr(srcaddr);\r
+               if (a.sin_addr.s_addr != INADDR_NONE) {\r
+                   /* Override localhost_only with specified listen addr. */\r
+                   ret->localhost_only = ipv4_is_loopback(a.sin_addr);\r
+                   got_addr = 1;\r
+               }\r
+           }\r
+\r
+           /*\r
+            * ... and failing that, go with one of the standard ones.\r
+            */\r
+           if (!got_addr) {\r
+               if (local_host_only)\r
+                   a.sin_addr.s_addr = p_htonl(INADDR_LOOPBACK);\r
+               else\r
+                   a.sin_addr.s_addr = p_htonl(INADDR_ANY);\r
+           }\r
+\r
+           a.sin_port = p_htons((short)port);\r
+       }\r
+#ifndef NO_IPV6\r
+       retcode = p_bind(s, (address_family == AF_INET6 ?\r
+                          (struct sockaddr *) &a6 :\r
+                          (struct sockaddr *) &a),\r
+                      (address_family ==\r
+                       AF_INET6 ? sizeof(a6) : sizeof(a)));\r
+#else\r
+       retcode = p_bind(s, (struct sockaddr *) &a, sizeof(a));\r
+#endif\r
+       if (retcode != SOCKET_ERROR) {\r
+           err = 0;\r
+       } else {\r
+           err = p_WSAGetLastError();\r
+       }\r
+\r
+    if (err) {\r
+       p_closesocket(s);\r
+       ret->error = winsock_error_string(err);\r
+       return (Socket) ret;\r
+    }\r
+\r
+\r
+    if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) {\r
+        p_closesocket(s);\r
+       ret->error = winsock_error_string(err);\r
+       return (Socket) ret;\r
+    }\r
+\r
+    /* Set up a select mechanism. This could be an AsyncSelect on a\r
+     * window, or an EventSelect on an event object. */\r
+    errstr = do_select(s, 1);\r
+    if (errstr) {\r
+       p_closesocket(s);\r
+       ret->error = errstr;\r
+       return (Socket) ret;\r
+    }\r
+\r
+    add234(sktree, ret);\r
+\r
+#ifndef NO_IPV6\r
+    /*\r
+     * If we were given ADDRTYPE_UNSPEC, we must also create an\r
+     * IPv6 listening socket and link it to this one.\r
+     */\r
+    if (address_family == AF_INET && orig_address_family == ADDRTYPE_UNSPEC) {\r
+       Actual_Socket other;\r
+\r
+       other = (Actual_Socket) sk_newlistener(srcaddr, port, plug,\r
+                                              local_host_only, ADDRTYPE_IPV6);\r
+\r
+       if (other) {\r
+           if (!other->error) {\r
+               other->parent = ret;\r
+               ret->child = other;\r
+           } else {\r
+               sfree(other);\r
+           }\r
+       }\r
+    }\r
+#endif\r
+\r
+    return (Socket) ret;\r
+}\r
+\r
+static void sk_tcp_close(Socket sock)\r
+{\r
+    extern char *do_select(SOCKET skt, int startup);\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+\r
+    if (s->child)\r
+       sk_tcp_close((Socket)s->child);\r
+\r
+    del234(sktree, s);\r
+    do_select(s->s, 0);\r
+    p_closesocket(s->s);\r
+    if (s->addr)\r
+       sk_addr_free(s->addr);\r
+    sfree(s);\r
+}\r
+\r
+/*\r
+ * The function which tries to send on a socket once it's deemed\r
+ * writable.\r
+ */\r
+void try_send(Actual_Socket s)\r
+{\r
+    while (s->sending_oob || bufchain_size(&s->output_data) > 0) {\r
+       int nsent;\r
+       DWORD err;\r
+       void *data;\r
+       int len, urgentflag;\r
+\r
+       if (s->sending_oob) {\r
+           urgentflag = MSG_OOB;\r
+           len = s->sending_oob;\r
+           data = &s->oobdata;\r
+       } else {\r
+           urgentflag = 0;\r
+           bufchain_prefix(&s->output_data, &data, &len);\r
+       }\r
+       nsent = p_send(s->s, data, len, urgentflag);\r
+       noise_ultralight(nsent);\r
+       if (nsent <= 0) {\r
+           err = (nsent < 0 ? p_WSAGetLastError() : 0);\r
+           if ((err < WSABASEERR && nsent < 0) || err == WSAEWOULDBLOCK) {\r
+               /*\r
+                * Perfectly normal: we've sent all we can for the moment.\r
+                * \r
+                * (Some WinSock send() implementations can return\r
+                * <0 but leave no sensible error indication -\r
+                * WSAGetLastError() is called but returns zero or\r
+                * a small number - so we check that case and treat\r
+                * it just like WSAEWOULDBLOCK.)\r
+                */\r
+               s->writable = FALSE;\r
+               return;\r
+           } else if (nsent == 0 ||\r
+                      err == WSAECONNABORTED || err == WSAECONNRESET) {\r
+               /*\r
+                * If send() returns CONNABORTED or CONNRESET, we\r
+                * unfortunately can't just call plug_closing(),\r
+                * because it's quite likely that we're currently\r
+                * _in_ a call from the code we'd be calling back\r
+                * to, so we'd have to make half the SSH code\r
+                * reentrant. Instead we flag a pending error on\r
+                * the socket, to be dealt with (by calling\r
+                * plug_closing()) at some suitable future moment.\r
+                */\r
+               s->pending_error = err;\r
+               return;\r
+           } else {\r
+               /* We're inside the Windows frontend here, so we know\r
+                * that the frontend handle is unnecessary. */\r
+               logevent(NULL, winsock_error_string(err));\r
+               fatalbox("%s", winsock_error_string(err));\r
+           }\r
+       } else {\r
+           if (s->sending_oob) {\r
+               if (nsent < len) {\r
+                   memmove(s->oobdata, s->oobdata+nsent, len-nsent);\r
+                   s->sending_oob = len - nsent;\r
+               } else {\r
+                   s->sending_oob = 0;\r
+               }\r
+           } else {\r
+               bufchain_consume(&s->output_data, nsent);\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+static int sk_tcp_write(Socket sock, const char *buf, int len)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+\r
+    /*\r
+     * Add the data to the buffer list on the socket.\r
+     */\r
+    bufchain_add(&s->output_data, buf, len);\r
+\r
+    /*\r
+     * Now try sending from the start of the buffer list.\r
+     */\r
+    if (s->writable)\r
+       try_send(s);\r
+\r
+    return bufchain_size(&s->output_data);\r
+}\r
+\r
+static int sk_tcp_write_oob(Socket sock, const char *buf, int len)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+\r
+    /*\r
+     * Replace the buffer list on the socket with the data.\r
+     */\r
+    bufchain_clear(&s->output_data);\r
+    assert(len <= sizeof(s->oobdata));\r
+    memcpy(s->oobdata, buf, len);\r
+    s->sending_oob = len;\r
+\r
+    /*\r
+     * Now try sending from the start of the buffer list.\r
+     */\r
+    if (s->writable)\r
+       try_send(s);\r
+\r
+    return s->sending_oob;\r
+}\r
+\r
+int select_result(WPARAM wParam, LPARAM lParam)\r
+{\r
+    int ret, open;\r
+    DWORD err;\r
+    char buf[20480];                  /* nice big buffer for plenty of speed */\r
+    Actual_Socket s;\r
+    u_long atmark;\r
+\r
+    /* wParam is the socket itself */\r
+\r
+    if (wParam == 0)\r
+       return 1;                      /* boggle */\r
+\r
+    s = find234(sktree, (void *) wParam, cmpforsearch);\r
+    if (!s)\r
+       return 1;                      /* boggle */\r
+\r
+    if ((err = WSAGETSELECTERROR(lParam)) != 0) {\r
+       /*\r
+        * An error has occurred on this socket. Pass it to the\r
+        * plug.\r
+        */\r
+       if (s->addr) {\r
+           plug_log(s->plug, 1, s->addr, s->port,\r
+                    winsock_error_string(err), err);\r
+           while (s->addr && sk_nextaddr(s->addr, &s->step)) {\r
+               err = try_connect(s);\r
+           }\r
+       }\r
+       if (err != 0)\r
+           return plug_closing(s->plug, winsock_error_string(err), err, 0);\r
+       else\r
+           return 1;\r
+    }\r
+\r
+    noise_ultralight(lParam);\r
+\r
+    switch (WSAGETSELECTEVENT(lParam)) {\r
+      case FD_CONNECT:\r
+       s->connected = s->writable = 1;\r
+       /*\r
+        * Once a socket is connected, we can stop falling\r
+        * back through the candidate addresses to connect\r
+        * to.\r
+        */\r
+       if (s->addr) {\r
+           sk_addr_free(s->addr);\r
+           s->addr = NULL;\r
+       }\r
+       break;\r
+      case FD_READ:\r
+       /* In the case the socket is still frozen, we don't even bother */\r
+       if (s->frozen) {\r
+           s->frozen_readable = 1;\r
+           break;\r
+       }\r
+\r
+       /*\r
+        * We have received data on the socket. For an oobinline\r
+        * socket, this might be data _before_ an urgent pointer,\r
+        * in which case we send it to the back end with type==1\r
+        * (data prior to urgent).\r
+        */\r
+       if (s->oobinline) {\r
+           atmark = 1;\r
+           p_ioctlsocket(s->s, SIOCATMARK, &atmark);\r
+           /*\r
+            * Avoid checking the return value from ioctlsocket(),\r
+            * on the grounds that some WinSock wrappers don't\r
+            * support it. If it does nothing, we get atmark==1,\r
+            * which is equivalent to `no OOB pending', so the\r
+            * effect will be to non-OOB-ify any OOB data.\r
+            */\r
+       } else\r
+           atmark = 1;\r
+\r
+       ret = p_recv(s->s, buf, sizeof(buf), 0);\r
+       noise_ultralight(ret);\r
+       if (ret < 0) {\r
+           err = p_WSAGetLastError();\r
+           if (err == WSAEWOULDBLOCK) {\r
+               break;\r
+           }\r
+       }\r
+       if (ret < 0) {\r
+           return plug_closing(s->plug, winsock_error_string(err), err,\r
+                               0);\r
+       } else if (0 == ret) {\r
+           return plug_closing(s->plug, NULL, 0, 0);\r
+       } else {\r
+           return plug_receive(s->plug, atmark ? 0 : 1, buf, ret);\r
+       }\r
+       break;\r
+      case FD_OOB:\r
+       /*\r
+        * This will only happen on a non-oobinline socket. It\r
+        * indicates that we can immediately perform an OOB read\r
+        * and get back OOB data, which we will send to the back\r
+        * end with type==2 (urgent data).\r
+        */\r
+       ret = p_recv(s->s, buf, sizeof(buf), MSG_OOB);\r
+       noise_ultralight(ret);\r
+       if (ret <= 0) {\r
+           char *str = (ret == 0 ? "Internal networking trouble" :\r
+                        winsock_error_string(p_WSAGetLastError()));\r
+           /* We're inside the Windows frontend here, so we know\r
+            * that the frontend handle is unnecessary. */\r
+           logevent(NULL, str);\r
+           fatalbox("%s", str);\r
+       } else {\r
+           return plug_receive(s->plug, 2, buf, ret);\r
+       }\r
+       break;\r
+      case FD_WRITE:\r
+       {\r
+           int bufsize_before, bufsize_after;\r
+           s->writable = 1;\r
+           bufsize_before = s->sending_oob + bufchain_size(&s->output_data);\r
+           try_send(s);\r
+           bufsize_after = s->sending_oob + bufchain_size(&s->output_data);\r
+           if (bufsize_after < bufsize_before)\r
+               plug_sent(s->plug, bufsize_after);\r
+       }\r
+       break;\r
+      case FD_CLOSE:\r
+       /* Signal a close on the socket. First read any outstanding data. */\r
+       open = 1;\r
+       do {\r
+           ret = p_recv(s->s, buf, sizeof(buf), 0);\r
+           if (ret < 0) {\r
+               err = p_WSAGetLastError();\r
+               if (err == WSAEWOULDBLOCK)\r
+                   break;\r
+               return plug_closing(s->plug, winsock_error_string(err),\r
+                                   err, 0);\r
+           } else {\r
+               if (ret)\r
+                   open &= plug_receive(s->plug, 0, buf, ret);\r
+               else\r
+                   open &= plug_closing(s->plug, NULL, 0, 0);\r
+           }\r
+       } while (ret > 0);\r
+       return open;\r
+       case FD_ACCEPT:\r
+       {\r
+#ifdef NO_IPV6\r
+           struct sockaddr_in isa;\r
+#else\r
+            struct sockaddr_storage isa;\r
+#endif\r
+           int addrlen = sizeof(isa);\r
+           SOCKET t;  /* socket of connection */\r
+\r
+           memset(&isa, 0, sizeof(isa));\r
+           err = 0;\r
+           t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen);\r
+           if (t == INVALID_SOCKET)\r
+           {\r
+               err = p_WSAGetLastError();\r
+               if (err == WSATRY_AGAIN)\r
+                   break;\r
+           }\r
+#ifndef NO_IPV6\r
+            if (isa.ss_family == AF_INET &&\r
+                s->localhost_only &&\r
+                !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr))\r
+#else\r
+           if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr))\r
+#endif\r
+           {\r
+               p_closesocket(t);      /* dodgy WinSock let nonlocal through */\r
+           } else if (plug_accepting(s->plug, (void*)t)) {\r
+               p_closesocket(t);      /* denied or error */\r
+           }\r
+       }\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Deal with socket errors detected in try_send().\r
+ */\r
+void net_pending_errors(void)\r
+{\r
+    int i;\r
+    Actual_Socket s;\r
+\r
+    /*\r
+     * This might be a fiddly business, because it's just possible\r
+     * that handling a pending error on one socket might cause\r
+     * others to be closed. (I can't think of any reason this might\r
+     * happen in current SSH implementation, but to maintain\r
+     * generality of this network layer I'll assume the worst.)\r
+     * \r
+     * So what we'll do is search the socket list for _one_ socket\r
+     * with a pending error, and then handle it, and then search\r
+     * the list again _from the beginning_. Repeat until we make a\r
+     * pass with no socket errors present. That way we are\r
+     * protected against the socket list changing under our feet.\r
+     */\r
+\r
+    do {\r
+       for (i = 0; (s = index234(sktree, i)) != NULL; i++) {\r
+           if (s->pending_error) {\r
+               /*\r
+                * An error has occurred on this socket. Pass it to the\r
+                * plug.\r
+                */\r
+               plug_closing(s->plug,\r
+                            winsock_error_string(s->pending_error),\r
+                            s->pending_error, 0);\r
+               break;\r
+           }\r
+       }\r
+    } while (s);\r
+}\r
+\r
+/*\r
+ * Each socket abstraction contains a `void *' private field in\r
+ * which the client can keep state.\r
+ */\r
+static void sk_tcp_set_private_ptr(Socket sock, void *ptr)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    s->private_ptr = ptr;\r
+}\r
+\r
+static void *sk_tcp_get_private_ptr(Socket sock)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    return s->private_ptr;\r
+}\r
+\r
+/*\r
+ * Special error values are returned from sk_namelookup and sk_new\r
+ * if there's a problem. These functions extract an error message,\r
+ * or return NULL if there's no problem.\r
+ */\r
+const char *sk_addr_error(SockAddr addr)\r
+{\r
+    return addr->error;\r
+}\r
+static const char *sk_tcp_socket_error(Socket sock)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    return s->error;\r
+}\r
+\r
+static void sk_tcp_set_frozen(Socket sock, int is_frozen)\r
+{\r
+    Actual_Socket s = (Actual_Socket) sock;\r
+    if (s->frozen == is_frozen)\r
+       return;\r
+    s->frozen = is_frozen;\r
+    if (!is_frozen) {\r
+       do_select(s->s, 1);\r
+       if (s->frozen_readable) {\r
+           char c;\r
+           p_recv(s->s, &c, 1, MSG_PEEK);\r
+       }\r
+    }\r
+    s->frozen_readable = 0;\r
+}\r
+\r
+void socket_reselect_all(void)\r
+{\r
+    Actual_Socket s;\r
+    int i;\r
+\r
+    for (i = 0; (s = index234(sktree, i)) != NULL; i++) {\r
+       if (!s->frozen)\r
+           do_select(s->s, 1);\r
+    }\r
+}\r
+\r
+/*\r
+ * For Plink: enumerate all sockets currently active.\r
+ */\r
+SOCKET first_socket(int *state)\r
+{\r
+    Actual_Socket s;\r
+    *state = 0;\r
+    s = index234(sktree, (*state)++);\r
+    return s ? s->s : INVALID_SOCKET;\r
+}\r
+\r
+SOCKET next_socket(int *state)\r
+{\r
+    Actual_Socket s = index234(sktree, (*state)++);\r
+    return s ? s->s : INVALID_SOCKET;\r
+}\r
+\r
+extern int socket_writable(SOCKET skt)\r
+{\r
+    Actual_Socket s = find234(sktree, (void *)skt, cmpforsearch);\r
+\r
+    if (s)\r
+       return bufchain_size(&s->output_data) > 0;\r
+    else\r
+       return 0;\r
+}\r
+\r
+int net_service_lookup(char *service)\r
+{\r
+    struct servent *se;\r
+    se = p_getservbyname(service, NULL);\r
+    if (se != NULL)\r
+       return p_ntohs(se->s_port);\r
+    else\r
+       return 0;\r
+}\r
+\r
+char *get_hostname(void)\r
+{\r
+    int len = 128;\r
+    char *hostname = NULL;\r
+    do {\r
+       len *= 2;\r
+       hostname = sresize(hostname, len, char);\r
+       if (p_gethostname(hostname, len) < 0) {\r
+           sfree(hostname);\r
+           hostname = NULL;\r
+           break;\r
+       }\r
+    } while (strlen(hostname) >= (size_t)(len-1));\r
+    return hostname;\r
+}\r
+\r
+SockAddr platform_get_x11_unix_address(const char *display, int displaynum,\r
+                                      char **canonicalname)\r
+{\r
+    SockAddr ret = snew(struct SockAddr_tag);\r
+    memset(ret, 0, sizeof(struct SockAddr_tag));\r
+    ret->error = "unix sockets not supported on this platform";\r
+    ret->refcount = 1;\r
+    return ret;\r
+}\r
diff --git a/putty/WINDOWS/WINNOISE.C b/putty/WINDOWS/WINNOISE.C
new file mode 100644 (file)
index 0000000..bdf8697
--- /dev/null
@@ -0,0 +1,128 @@
+/*\r
+ * Noise generation for PuTTY's cryptographic random number\r
+ * generator.\r
+ */\r
+\r
+#include <stdio.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "storage.h"\r
+\r
+/*\r
+ * This function is called once, at PuTTY startup, and will do some\r
+ * seriously silly things like listing directories and getting disk\r
+ * free space and a process snapshot.\r
+ */\r
+\r
+void noise_get_heavy(void (*func) (void *, int))\r
+{\r
+    HANDLE srch;\r
+    WIN32_FIND_DATA finddata;\r
+    DWORD pid;\r
+    char winpath[MAX_PATH + 3];\r
+\r
+    GetWindowsDirectory(winpath, sizeof(winpath));\r
+    strcat(winpath, "\\*");\r
+    srch = FindFirstFile(winpath, &finddata);\r
+    if (srch != INVALID_HANDLE_VALUE) {\r
+       do {\r
+           func(&finddata, sizeof(finddata));\r
+       } while (FindNextFile(srch, &finddata));\r
+       FindClose(srch);\r
+    }\r
+\r
+    pid = GetCurrentProcessId();\r
+    func(&pid, sizeof(pid));\r
+\r
+    read_random_seed(func);\r
+    /* Update the seed immediately, in case another instance uses it. */\r
+    random_save_seed();\r
+}\r
+\r
+void random_save_seed(void)\r
+{\r
+    int len;\r
+    void *data;\r
+\r
+    if (random_active) {\r
+       random_get_savedata(&data, &len);\r
+       write_random_seed(data, len);\r
+       sfree(data);\r
+    }\r
+}\r
+\r
+/*\r
+ * This function is called every time the random pool needs\r
+ * stirring, and will acquire the system time in all available\r
+ * forms.\r
+ */\r
+void noise_get_light(void (*func) (void *, int))\r
+{\r
+    SYSTEMTIME systime;\r
+    DWORD adjust[2];\r
+    BOOL rubbish;\r
+\r
+    GetSystemTime(&systime);\r
+    func(&systime, sizeof(systime));\r
+\r
+    GetSystemTimeAdjustment(&adjust[0], &adjust[1], &rubbish);\r
+    func(&adjust, sizeof(adjust));\r
+}\r
+\r
+/*\r
+ * This function is called on a timer, and it will monitor\r
+ * frequently changing quantities such as the state of physical and\r
+ * virtual memory, the state of the process's message queue, which\r
+ * window is in the foreground, which owns the clipboard, etc.\r
+ */\r
+void noise_regular(void)\r
+{\r
+    HWND w;\r
+    DWORD z;\r
+    POINT pt;\r
+    MEMORYSTATUS memstat;\r
+    FILETIME times[4];\r
+\r
+    w = GetForegroundWindow();\r
+    random_add_noise(&w, sizeof(w));\r
+    w = GetCapture();\r
+    random_add_noise(&w, sizeof(w));\r
+    w = GetClipboardOwner();\r
+    random_add_noise(&w, sizeof(w));\r
+    z = GetQueueStatus(QS_ALLEVENTS);\r
+    random_add_noise(&z, sizeof(z));\r
+\r
+    GetCursorPos(&pt);\r
+    random_add_noise(&pt, sizeof(pt));\r
+\r
+    GlobalMemoryStatus(&memstat);\r
+    random_add_noise(&memstat, sizeof(memstat));\r
+\r
+    GetThreadTimes(GetCurrentThread(), times, times + 1, times + 2,\r
+                  times + 3);\r
+    random_add_noise(&times, sizeof(times));\r
+    GetProcessTimes(GetCurrentProcess(), times, times + 1, times + 2,\r
+                   times + 3);\r
+    random_add_noise(&times, sizeof(times));\r
+}\r
+\r
+/*\r
+ * This function is called on every keypress or mouse move, and\r
+ * will add the current Windows time and performance monitor\r
+ * counter to the noise pool. It gets the scan code or mouse\r
+ * position passed in.\r
+ */\r
+void noise_ultralight(unsigned long data)\r
+{\r
+    DWORD wintime;\r
+    LARGE_INTEGER perftime;\r
+\r
+    random_add_noise(&data, sizeof(DWORD));\r
+\r
+    wintime = GetTickCount();\r
+    random_add_noise(&wintime, sizeof(DWORD));\r
+\r
+    if (QueryPerformanceCounter(&perftime))\r
+       random_add_noise(&perftime, sizeof(perftime));\r
+}\r
diff --git a/putty/WINDOWS/WINNOJMP.C b/putty/WINDOWS/WINNOJMP.C
new file mode 100644 (file)
index 0000000..27d969a
--- /dev/null
@@ -0,0 +1,8 @@
+/*\r
+ * winnojmp.c: stub jump list functions for Windows executables that\r
+ * don't update the jump list.\r
+ */\r
+\r
+void add_session_to_jumplist(const char * const sessionname) {}\r
+void remove_session_from_jumplist(const char * const sessionname) {}\r
+void clear_jumplist(void) {}\r
diff --git a/putty/WINDOWS/WINPGEN.C b/putty/WINDOWS/WINPGEN.C
new file mode 100644 (file)
index 0000000..13dfac7
--- /dev/null
@@ -0,0 +1,1431 @@
+/*\r
+ * PuTTY key generation front end (Windows).\r
+ */\r
+\r
+#include <time.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#define PUTTY_DO_GLOBALS\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+\r
+#include <commctrl.h>\r
+\r
+#ifdef MSVC4\r
+#define ICON_BIG        1\r
+#endif\r
+\r
+#define WM_DONEKEY (WM_APP + 1)\r
+\r
+#define DEFAULT_KEYSIZE 1024\r
+\r
+static char *cmdline_keyfile = NULL;\r
+\r
+/*\r
+ * Print a modal (Really Bad) message box and perform a fatal exit.\r
+ */\r
+void modalfatalbox(char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *stuff;\r
+\r
+    va_start(ap, fmt);\r
+    stuff = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    MessageBox(NULL, stuff, "PuTTYgen Fatal Error",\r
+              MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);\r
+    sfree(stuff);\r
+    exit(1);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Progress report code. This is really horrible :-)\r
+ */\r
+#define PROGRESSRANGE 65535\r
+#define MAXPHASE 5\r
+struct progress {\r
+    int nphases;\r
+    struct {\r
+       int exponential;\r
+       unsigned startpoint, total;\r
+       unsigned param, current, n;    /* if exponential */\r
+       unsigned mult;                 /* if linear */\r
+    } phases[MAXPHASE];\r
+    unsigned total, divisor, range;\r
+    HWND progbar;\r
+};\r
+\r
+static void progress_update(void *param, int action, int phase, int iprogress)\r
+{\r
+    struct progress *p = (struct progress *) param;\r
+    unsigned progress = iprogress;\r
+    int position;\r
+\r
+    if (action < PROGFN_READY && p->nphases < phase)\r
+       p->nphases = phase;\r
+    switch (action) {\r
+      case PROGFN_INITIALISE:\r
+       p->nphases = 0;\r
+       break;\r
+      case PROGFN_LIN_PHASE:\r
+       p->phases[phase-1].exponential = 0;\r
+       p->phases[phase-1].mult = p->phases[phase].total / progress;\r
+       break;\r
+      case PROGFN_EXP_PHASE:\r
+       p->phases[phase-1].exponential = 1;\r
+       p->phases[phase-1].param = 0x10000 + progress;\r
+       p->phases[phase-1].current = p->phases[phase-1].total;\r
+       p->phases[phase-1].n = 0;\r
+       break;\r
+      case PROGFN_PHASE_EXTENT:\r
+       p->phases[phase-1].total = progress;\r
+       break;\r
+      case PROGFN_READY:\r
+       {\r
+           unsigned total = 0;\r
+           int i;\r
+           for (i = 0; i < p->nphases; i++) {\r
+               p->phases[i].startpoint = total;\r
+               total += p->phases[i].total;\r
+           }\r
+           p->total = total;\r
+           p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);\r
+           p->range = p->total / p->divisor;\r
+           SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));\r
+       }\r
+       break;\r
+      case PROGFN_PROGRESS:\r
+       if (p->phases[phase-1].exponential) {\r
+           while (p->phases[phase-1].n < progress) {\r
+               p->phases[phase-1].n++;\r
+               p->phases[phase-1].current *= p->phases[phase-1].param;\r
+               p->phases[phase-1].current /= 0x10000;\r
+           }\r
+           position = (p->phases[phase-1].startpoint +\r
+                       p->phases[phase-1].total - p->phases[phase-1].current);\r
+       } else {\r
+           position = (p->phases[phase-1].startpoint +\r
+                       progress * p->phases[phase-1].mult);\r
+       }\r
+       SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);\r
+       break;\r
+    }\r
+}\r
+\r
+extern char ver[];\r
+\r
+#define PASSPHRASE_MAXLEN 512\r
+\r
+struct PassphraseProcStruct {\r
+    char *passphrase;\r
+    char *comment;\r
+};\r
+\r
+/*\r
+ * Dialog-box function for the passphrase box.\r
+ */\r
+static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,\r
+                                  WPARAM wParam, LPARAM lParam)\r
+{\r
+    static char *passphrase = NULL;\r
+    struct PassphraseProcStruct *p;\r
+\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       SetForegroundWindow(hwnd);\r
+       SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,\r
+                    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+\r
+       /*\r
+        * Centre the window.\r
+        */\r
+       {                              /* centre the window */\r
+           RECT rs, rd;\r
+           HWND hw;\r
+\r
+           hw = GetDesktopWindow();\r
+           if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
+               MoveWindow(hwnd,\r
+                          (rs.right + rs.left + rd.left - rd.right) / 2,\r
+                          (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
+                          rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
+       }\r
+\r
+       p = (struct PassphraseProcStruct *) lParam;\r
+       passphrase = p->passphrase;\r
+       if (p->comment)\r
+           SetDlgItemText(hwnd, 101, p->comment);\r
+       *passphrase = 0;\r
+       SetDlgItemText(hwnd, 102, passphrase);\r
+       return 0;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+           if (*passphrase)\r
+               EndDialog(hwnd, 1);\r
+           else\r
+               MessageBeep(0);\r
+           return 0;\r
+         case IDCANCEL:\r
+           EndDialog(hwnd, 0);\r
+           return 0;\r
+         case 102:                    /* edit box */\r
+           if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {\r
+               GetDlgItemText(hwnd, 102, passphrase,\r
+                              PASSPHRASE_MAXLEN - 1);\r
+               passphrase[PASSPHRASE_MAXLEN - 1] = '\0';\r
+           }\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       EndDialog(hwnd, 0);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Prompt for a key file. Assumes the filename buffer is of size\r
+ * FILENAME_MAX.\r
+ */\r
+static int prompt_keyfile(HWND hwnd, char *dlgtitle,\r
+                         char *filename, int save, int ppk)\r
+{\r
+    OPENFILENAME of;\r
+    memset(&of, 0, sizeof(of));\r
+    of.hwndOwner = hwnd;\r
+    if (ppk) {\r
+       of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"\r
+           "All Files (*.*)\0*\0\0\0";\r
+       of.lpstrDefExt = ".ppk";\r
+    } else {\r
+       of.lpstrFilter = "All Files (*.*)\0*\0\0\0";\r
+    }\r
+    of.lpstrCustomFilter = NULL;\r
+    of.nFilterIndex = 1;\r
+    of.lpstrFile = filename;\r
+    *filename = '\0';\r
+    of.nMaxFile = FILENAME_MAX;\r
+    of.lpstrFileTitle = NULL;\r
+    of.lpstrTitle = dlgtitle;\r
+    of.Flags = 0;\r
+    return request_file(NULL, &of, FALSE, save);\r
+}\r
+\r
+/*\r
+ * Dialog-box function for the Licence box.\r
+ */\r
+static int CALLBACK LicenceProc(HWND hwnd, UINT msg,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       /*\r
+        * Centre the window.\r
+        */\r
+       {                              /* centre the window */\r
+           RECT rs, rd;\r
+           HWND hw;\r
+\r
+           hw = GetDesktopWindow();\r
+           if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
+               MoveWindow(hwnd,\r
+                          (rs.right + rs.left + rd.left - rd.right) / 2,\r
+                          (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
+                          rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
+       }\r
+\r
+       return 1;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           EndDialog(hwnd, 1);\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       EndDialog(hwnd, 1);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Dialog-box function for the About box.\r
+ */\r
+static int CALLBACK AboutProc(HWND hwnd, UINT msg,\r
+                             WPARAM wParam, LPARAM lParam)\r
+{\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       /*\r
+        * Centre the window.\r
+        */\r
+       {                              /* centre the window */\r
+           RECT rs, rd;\r
+           HWND hw;\r
+\r
+           hw = GetDesktopWindow();\r
+           if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
+               MoveWindow(hwnd,\r
+                          (rs.right + rs.left + rd.left - rd.right) / 2,\r
+                          (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
+                          rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
+       }\r
+\r
+       SetDlgItemText(hwnd, 100, ver);\r
+       return 1;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           EndDialog(hwnd, 1);\r
+           return 0;\r
+         case 101:\r
+           EnableWindow(hwnd, 0);\r
+           DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);\r
+           EnableWindow(hwnd, 1);\r
+           SetActiveWindow(hwnd);\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       EndDialog(hwnd, 1);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Thread to generate a key.\r
+ */\r
+struct rsa_key_thread_params {\r
+    HWND progressbar;                 /* notify this with progress */\r
+    HWND dialog;                      /* notify this on completion */\r
+    int keysize;                      /* bits in key */\r
+    int is_dsa;\r
+    struct RSAKey *key;\r
+    struct dss_key *dsskey;\r
+};\r
+static DWORD WINAPI generate_rsa_key_thread(void *param)\r
+{\r
+    struct rsa_key_thread_params *params =\r
+       (struct rsa_key_thread_params *) param;\r
+    struct progress prog;\r
+    prog.progbar = params->progressbar;\r
+\r
+    progress_update(&prog, PROGFN_INITIALISE, 0, 0);\r
+\r
+    if (params->is_dsa)\r
+       dsa_generate(params->dsskey, params->keysize, progress_update, &prog);\r
+    else\r
+       rsa_generate(params->key, params->keysize, progress_update, &prog);\r
+\r
+    PostMessage(params->dialog, WM_DONEKEY, 0, 0);\r
+\r
+    sfree(params);\r
+    return 0;\r
+}\r
+\r
+struct MainDlgState {\r
+    int collecting_entropy;\r
+    int generation_thread_exists;\r
+    int key_exists;\r
+    int entropy_got, entropy_required, entropy_size;\r
+    int keysize;\r
+    int ssh2, is_dsa;\r
+    char **commentptr;                /* points to key.comment or ssh2key.comment */\r
+    struct ssh2_userkey ssh2key;\r
+    unsigned *entropy;\r
+    struct RSAKey key;\r
+    struct dss_key dsskey;\r
+    HMENU filemenu, keymenu, cvtmenu;\r
+};\r
+\r
+static void hidemany(HWND hwnd, const int *ids, int hideit)\r
+{\r
+    while (*ids) {\r
+       ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));\r
+    }\r
+}\r
+\r
+static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)\r
+{\r
+    char *buffer;\r
+    char *dec1, *dec2;\r
+\r
+    dec1 = bignum_decimal(key->exponent);\r
+    dec2 = bignum_decimal(key->modulus);\r
+    buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),\r
+                      dec1, dec2, key->comment);\r
+    SetDlgItemText(hwnd, id, buffer);\r
+    SetDlgItemText(hwnd, idstatic,\r
+                  "&Public key for pasting into authorized_keys file:");\r
+    sfree(dec1);\r
+    sfree(dec2);\r
+    sfree(buffer);\r
+}\r
+\r
+static void setupbigedit2(HWND hwnd, int id, int idstatic,\r
+                         struct ssh2_userkey *key)\r
+{\r
+    unsigned char *pub_blob;\r
+    char *buffer, *p;\r
+    int pub_len;\r
+    int i;\r
+\r
+    pub_blob = key->alg->public_blob(key->data, &pub_len);\r
+    buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +\r
+                  strlen(key->comment) + 3, char);\r
+    strcpy(buffer, key->alg->name);\r
+    p = buffer + strlen(buffer);\r
+    *p++ = ' ';\r
+    i = 0;\r
+    while (i < pub_len) {\r
+       int n = (pub_len - i < 3 ? pub_len - i : 3);\r
+       base64_encode_atom(pub_blob + i, n, p);\r
+       i += n;\r
+       p += 4;\r
+    }\r
+    *p++ = ' ';\r
+    strcpy(p, key->comment);\r
+    SetDlgItemText(hwnd, id, buffer);\r
+    SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "\r
+                  "OpenSSH authorized_keys file:");\r
+    sfree(pub_blob);\r
+    sfree(buffer);\r
+}\r
+\r
+static int save_ssh1_pubkey(char *filename, struct RSAKey *key)\r
+{\r
+    char *dec1, *dec2;\r
+    FILE *fp;\r
+\r
+    dec1 = bignum_decimal(key->exponent);\r
+    dec2 = bignum_decimal(key->modulus);\r
+    fp = fopen(filename, "wb");\r
+    if (!fp)\r
+       return 0;\r
+    fprintf(fp, "%d %s %s %s\n",\r
+           bignum_bitcount(key->modulus), dec1, dec2, key->comment);\r
+    fclose(fp);\r
+    sfree(dec1);\r
+    sfree(dec2);\r
+    return 1;\r
+}\r
+\r
+/*\r
+ * Warn about the obsolescent key file format.\r
+ */\r
+void old_keyfile_warning(void)\r
+{\r
+    static const char mbtitle[] = "PuTTY Key File Warning";\r
+    static const char message[] =\r
+       "You are loading an SSH-2 private key which has an\n"\r
+       "old version of the file format. This means your key\n"\r
+       "file is not fully tamperproof. Future versions of\n"\r
+       "PuTTY may stop supporting this private key format,\n"\r
+       "so we recommend you convert your key to the new\n"\r
+       "format.\n"\r
+       "\n"\r
+       "Once the key is loaded into PuTTYgen, you can perform\n"\r
+       "this conversion simply by saving it again.";\r
+\r
+    MessageBox(NULL, message, mbtitle, MB_OK);\r
+}\r
+\r
+static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)\r
+{\r
+    unsigned char *pub_blob;\r
+    char *p;\r
+    int pub_len;\r
+    int i, column;\r
+    FILE *fp;\r
+\r
+    pub_blob = key->alg->public_blob(key->data, &pub_len);\r
+\r
+    fp = fopen(filename, "wb");\r
+    if (!fp)\r
+       return 0;\r
+\r
+    fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");\r
+\r
+    fprintf(fp, "Comment: \"");\r
+    for (p = key->comment; *p; p++) {\r
+       if (*p == '\\' || *p == '\"')\r
+           fputc('\\', fp);\r
+       fputc(*p, fp);\r
+    }\r
+    fprintf(fp, "\"\n");\r
+\r
+    i = 0;\r
+    column = 0;\r
+    while (i < pub_len) {\r
+       char buf[5];\r
+       int n = (pub_len - i < 3 ? pub_len - i : 3);\r
+       base64_encode_atom(pub_blob + i, n, buf);\r
+       i += n;\r
+       buf[4] = '\0';\r
+       fputs(buf, fp);\r
+       if (++column >= 16) {\r
+           fputc('\n', fp);\r
+           column = 0;\r
+       }\r
+    }\r
+    if (column > 0)\r
+       fputc('\n', fp);\r
+    \r
+    fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");\r
+    fclose(fp);\r
+    sfree(pub_blob);\r
+    return 1;\r
+}\r
+\r
+enum {\r
+    controlidstart = 100,\r
+    IDC_QUIT,\r
+    IDC_TITLE,\r
+    IDC_BOX_KEY,\r
+    IDC_NOKEY,\r
+    IDC_GENERATING,\r
+    IDC_PROGRESS,\r
+    IDC_PKSTATIC, IDC_KEYDISPLAY,\r
+    IDC_FPSTATIC, IDC_FINGERPRINT,\r
+    IDC_COMMENTSTATIC, IDC_COMMENTEDIT,\r
+    IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,\r
+    IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,\r
+    IDC_BOX_ACTIONS,\r
+    IDC_GENSTATIC, IDC_GENERATE,\r
+    IDC_LOADSTATIC, IDC_LOAD,\r
+    IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,\r
+    IDC_BOX_PARAMS,\r
+    IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,\r
+    IDC_BITSSTATIC, IDC_BITS,\r
+    IDC_ABOUT,\r
+    IDC_GIVEHELP,\r
+    IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM\r
+};\r
+\r
+static const int nokey_ids[] = { IDC_NOKEY, 0 };\r
+static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };\r
+static const int gotkey_ids[] = {\r
+    IDC_PKSTATIC, IDC_KEYDISPLAY,\r
+    IDC_FPSTATIC, IDC_FINGERPRINT,\r
+    IDC_COMMENTSTATIC, IDC_COMMENTEDIT,\r
+    IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,\r
+    IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0\r
+};\r
+\r
+/*\r
+ * Small UI helper function to switch the state of the main dialog\r
+ * by enabling and disabling controls and menu items.\r
+ */\r
+void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)\r
+{\r
+    int type;\r
+\r
+    switch (status) {\r
+      case 0:                         /* no key */\r
+       hidemany(hwnd, nokey_ids, FALSE);\r
+       hidemany(hwnd, generating_ids, TRUE);\r
+       hidemany(hwnd, gotkey_ids, TRUE);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);\r
+       EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,\r
+                      MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,\r
+                      MF_GRAYED|MF_BYCOMMAND);\r
+       break;\r
+      case 1:                         /* generating key */\r
+       hidemany(hwnd, nokey_ids, TRUE);\r
+       hidemany(hwnd, generating_ids, FALSE);\r
+       hidemany(hwnd, gotkey_ids, TRUE);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);\r
+       EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,\r
+                      MF_GRAYED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,\r
+                      MF_GRAYED|MF_BYCOMMAND);\r
+       break;\r
+      case 2:\r
+       hidemany(hwnd, nokey_ids, TRUE);\r
+       hidemany(hwnd, generating_ids, TRUE);\r
+       hidemany(hwnd, gotkey_ids, FALSE);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);\r
+       EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);\r
+       EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);\r
+       EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);\r
+       /*\r
+        * Enable export menu items if and only if the key type\r
+        * supports this kind of export.\r
+        */\r
+       type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;\r
+#define do_export_menuitem(x,y) \\r
+    EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \\r
+                      (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))\r
+       do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);\r
+       do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);\r
+#undef do_export_menuitem\r
+       break;\r
+    }\r
+}\r
+\r
+void load_key_file(HWND hwnd, struct MainDlgState *state,\r
+                  Filename filename, int was_import_cmd)\r
+{\r
+    char passphrase[PASSPHRASE_MAXLEN];\r
+    int needs_pass;\r
+    int type, realtype;\r
+    int ret;\r
+    const char *errmsg = NULL;\r
+    char *comment;\r
+    struct PassphraseProcStruct pps;\r
+    struct RSAKey newkey1;\r
+    struct ssh2_userkey *newkey2 = NULL;\r
+\r
+    type = realtype = key_type(&filename);\r
+    if (type != SSH_KEYTYPE_SSH1 &&\r
+       type != SSH_KEYTYPE_SSH2 &&\r
+       !import_possible(type)) {\r
+       char *msg = dupprintf("Couldn't load private key (%s)",\r
+                             key_type_to_str(type));\r
+       message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,\r
+                   HELPCTXID(errors_cantloadkey));\r
+       sfree(msg);\r
+       return;\r
+    }\r
+\r
+    if (type != SSH_KEYTYPE_SSH1 &&\r
+       type != SSH_KEYTYPE_SSH2) {\r
+       realtype = type;\r
+       type = import_target_type(type);\r
+    }\r
+\r
+    comment = NULL;\r
+    if (realtype == SSH_KEYTYPE_SSH1)\r
+       needs_pass = rsakey_encrypted(&filename, &comment);\r
+    else if (realtype == SSH_KEYTYPE_SSH2)\r
+       needs_pass =\r
+       ssh2_userkey_encrypted(&filename, &comment);\r
+    else\r
+       needs_pass = import_encrypted(&filename, realtype,\r
+                                     &comment);\r
+    pps.passphrase = passphrase;\r
+    pps.comment = comment;\r
+    do {\r
+       if (needs_pass) {\r
+           int dlgret;\r
+           dlgret = DialogBoxParam(hinst,\r
+                                   MAKEINTRESOURCE(210),\r
+                                   NULL, PassphraseProc,\r
+                                   (LPARAM) &pps);\r
+           if (!dlgret) {\r
+               ret = -2;\r
+               break;\r
+           }\r
+       } else\r
+           *passphrase = '\0';\r
+       if (type == SSH_KEYTYPE_SSH1) {\r
+           if (realtype == type)\r
+               ret = loadrsakey(&filename, &newkey1,\r
+                                passphrase, &errmsg);\r
+           else\r
+               ret = import_ssh1(&filename, realtype,\r
+                                 &newkey1, passphrase, &errmsg);\r
+       } else {\r
+           if (realtype == type)\r
+               newkey2 = ssh2_load_userkey(&filename,\r
+                                           passphrase, &errmsg);\r
+           else\r
+               newkey2 = import_ssh2(&filename, realtype,\r
+                                     passphrase, &errmsg);\r
+           if (newkey2 == SSH2_WRONG_PASSPHRASE)\r
+               ret = -1;\r
+           else if (!newkey2)\r
+               ret = 0;\r
+           else\r
+               ret = 1;\r
+       }\r
+    } while (ret == -1);\r
+    if (comment)\r
+       sfree(comment);\r
+    if (ret == 0) {\r
+       char *msg = dupprintf("Couldn't load private key (%s)", errmsg);\r
+       message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,\r
+                   HELPCTXID(errors_cantloadkey));\r
+       sfree(msg);\r
+    } else if (ret == 1) {\r
+       /*\r
+        * Now update the key controls with all the\r
+        * key data.\r
+        */\r
+       {\r
+           SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,\r
+                          passphrase);\r
+           SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,\r
+                          passphrase);\r
+           if (type == SSH_KEYTYPE_SSH1) {\r
+               char buf[128];\r
+               char *savecomment;\r
+\r
+               state->ssh2 = FALSE;\r
+               state->commentptr = &state->key.comment;\r
+               state->key = newkey1;\r
+\r
+               /*\r
+                * Set the key fingerprint.\r
+                */\r
+               savecomment = state->key.comment;\r
+               state->key.comment = NULL;\r
+               rsa_fingerprint(buf, sizeof(buf),\r
+                               &state->key);\r
+               state->key.comment = savecomment;\r
+\r
+               SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);\r
+               /*\r
+                * Construct a decimal representation\r
+                * of the key, for pasting into\r
+                * .ssh/authorized_keys on a Unix box.\r
+                */\r
+               setupbigedit1(hwnd, IDC_KEYDISPLAY,\r
+                             IDC_PKSTATIC, &state->key);\r
+           } else {\r
+               char *fp;\r
+               char *savecomment;\r
+\r
+               state->ssh2 = TRUE;\r
+               state->commentptr =\r
+                   &state->ssh2key.comment;\r
+               state->ssh2key = *newkey2;      /* structure copy */\r
+               sfree(newkey2);\r
+\r
+               savecomment = state->ssh2key.comment;\r
+               state->ssh2key.comment = NULL;\r
+               fp =\r
+                   state->ssh2key.alg->\r
+                   fingerprint(state->ssh2key.data);\r
+               state->ssh2key.comment = savecomment;\r
+\r
+               SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);\r
+               sfree(fp);\r
+\r
+               setupbigedit2(hwnd, IDC_KEYDISPLAY,\r
+                             IDC_PKSTATIC, &state->ssh2key);\r
+           }\r
+           SetDlgItemText(hwnd, IDC_COMMENTEDIT,\r
+                          *state->commentptr);\r
+       }\r
+       /*\r
+        * Finally, hide the progress bar and show\r
+        * the key data.\r
+        */\r
+       ui_set_state(hwnd, state, 2);\r
+       state->key_exists = TRUE;\r
+\r
+       /*\r
+        * If the user has imported a foreign key\r
+        * using the Load command, let them know.\r
+        * If they've used the Import command, be\r
+        * silent.\r
+        */\r
+       if (realtype != type && !was_import_cmd) {\r
+           char msg[512];\r
+           sprintf(msg, "Successfully imported foreign key\n"\r
+                   "(%s).\n"\r
+                   "To use this key with PuTTY, you need to\n"\r
+                   "use the \"Save private key\" command to\n"\r
+                   "save it in PuTTY's own format.",\r
+                   key_type_to_str(realtype));\r
+           MessageBox(NULL, msg, "PuTTYgen Notice",\r
+                      MB_OK | MB_ICONINFORMATION);\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Dialog-box function for the main PuTTYgen dialog box.\r
+ */\r
+static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    static const char generating_msg[] =\r
+       "Please wait while a key is generated...";\r
+    static const char entropy_msg[] =\r
+       "Please generate some randomness by moving the mouse over the blank area.";\r
+    struct MainDlgState *state;\r
+\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+        if (has_help())\r
+            SetWindowLongPtr(hwnd, GWL_EXSTYLE,\r
+                            GetWindowLongPtr(hwnd, GWL_EXSTYLE) |\r
+                            WS_EX_CONTEXTHELP);\r
+        else {\r
+            /*\r
+             * If we add a Help button, this is where we destroy it\r
+             * if the help file isn't present.\r
+             */\r
+        }\r
+       SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,\r
+                   (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));\r
+\r
+       state = snew(struct MainDlgState);\r
+       state->generation_thread_exists = FALSE;\r
+       state->collecting_entropy = FALSE;\r
+       state->entropy = NULL;\r
+       state->key_exists = FALSE;\r
+       SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);\r
+       {\r
+           HMENU menu, menu1;\r
+\r
+           menu = CreateMenu();\r
+\r
+           menu1 = CreateMenu();\r
+           AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");\r
+           AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");\r
+           AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");\r
+           AppendMenu(menu1, MF_SEPARATOR, 0, 0);\r
+           AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");\r
+           AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");\r
+           state->filemenu = menu1;\r
+\r
+           menu1 = CreateMenu();\r
+           AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");\r
+           AppendMenu(menu1, MF_SEPARATOR, 0, 0);\r
+           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");\r
+           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");\r
+           AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");\r
+           AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");\r
+           state->keymenu = menu1;\r
+\r
+           menu1 = CreateMenu();\r
+           AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");\r
+           AppendMenu(menu1, MF_SEPARATOR, 0, 0);\r
+           AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,\r
+                      "Export &OpenSSH key");\r
+           AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,\r
+                      "Export &ssh.com key");\r
+           AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,\r
+                      "Con&versions");\r
+           state->cvtmenu = menu1;\r
+\r
+           menu1 = CreateMenu();\r
+           AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");\r
+           if (has_help())\r
+               AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");\r
+           AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");\r
+\r
+           SetMenu(hwnd, menu);\r
+       }\r
+\r
+       /*\r
+        * Centre the window.\r
+        */\r
+       {                              /* centre the window */\r
+           RECT rs, rd;\r
+           HWND hw;\r
+\r
+           hw = GetDesktopWindow();\r
+           if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
+               MoveWindow(hwnd,\r
+                          (rs.right + rs.left + rd.left - rd.right) / 2,\r
+                          (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
+                          rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
+       }\r
+\r
+       {\r
+           struct ctlpos cp, cp2;\r
+\r
+           /* Accelerators used: acglops1rbd */\r
+\r
+           ctlposinit(&cp, hwnd, 4, 4, 4);\r
+           beginbox(&cp, "Key", IDC_BOX_KEY);\r
+           cp2 = cp;\r
+           statictext(&cp2, "No key.", 1, IDC_NOKEY);\r
+           cp2 = cp;\r
+           statictext(&cp2, "", 1, IDC_GENERATING);\r
+           progressbar(&cp2, IDC_PROGRESS);\r
+           bigeditctrl(&cp,\r
+                       "&Public key for pasting into authorized_keys file:",\r
+                       IDC_PKSTATIC, IDC_KEYDISPLAY, 5);\r
+           SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);\r
+           staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,\r
+                      IDC_FINGERPRINT, 75);\r
+           SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,\r
+                              0);\r
+           staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,\r
+                      IDC_COMMENTEDIT, 75);\r
+           staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,\r
+                          IDC_PASSPHRASE1EDIT, 75);\r
+           staticpassedit(&cp, "C&onfirm passphrase:",\r
+                          IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);\r
+           endbox(&cp);\r
+           beginbox(&cp, "Actions", IDC_BOX_ACTIONS);\r
+           staticbtn(&cp, "Generate a public/private key pair",\r
+                     IDC_GENSTATIC, "&Generate", IDC_GENERATE);\r
+           staticbtn(&cp, "Load an existing private key file",\r
+                     IDC_LOADSTATIC, "&Load", IDC_LOAD);\r
+           static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,\r
+                      "Save p&ublic key", IDC_SAVEPUB,\r
+                      "&Save private key", IDC_SAVE);\r
+           endbox(&cp);\r
+           beginbox(&cp, "Parameters", IDC_BOX_PARAMS);\r
+           radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,\r
+                     "SSH-&1 (RSA)", IDC_KEYSSH1,\r
+                     "SSH-2 &RSA", IDC_KEYSSH2RSA,\r
+                     "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);\r
+           staticedit(&cp, "Number of &bits in a generated key:",\r
+                      IDC_BITSSTATIC, IDC_BITS, 20);\r
+           endbox(&cp);\r
+       }\r
+       CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);\r
+       CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,\r
+                          IDC_KEYSSH2RSA, MF_BYCOMMAND);\r
+       SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);\r
+\r
+       /*\r
+        * Initially, hide the progress bar and the key display,\r
+        * and show the no-key display. Also disable the Save\r
+        * buttons, because with no key we obviously can't save\r
+        * anything.\r
+        */\r
+       ui_set_state(hwnd, state, 0);\r
+\r
+       /*\r
+        * Load a key file if one was provided on the command line.\r
+        */\r
+       if (cmdline_keyfile)\r
+           load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);\r
+\r
+       return 1;\r
+      case WM_MOUSEMOVE:\r
+       state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+       if (state->collecting_entropy &&\r
+           state->entropy && state->entropy_got < state->entropy_required) {\r
+           state->entropy[state->entropy_got++] = lParam;\r
+           state->entropy[state->entropy_got++] = GetMessageTime();\r
+           SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,\r
+                              state->entropy_got, 0);\r
+           if (state->entropy_got >= state->entropy_required) {\r
+               struct rsa_key_thread_params *params;\r
+               DWORD threadid;\r
+\r
+               /*\r
+                * Seed the entropy pool\r
+                */\r
+               random_add_heavynoise(state->entropy, state->entropy_size);\r
+               memset(state->entropy, 0, state->entropy_size);\r
+               sfree(state->entropy);\r
+               state->collecting_entropy = FALSE;\r
+\r
+               SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);\r
+               SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,\r
+                                  MAKELPARAM(0, PROGRESSRANGE));\r
+               SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);\r
+\r
+               params = snew(struct rsa_key_thread_params);\r
+               params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);\r
+               params->dialog = hwnd;\r
+               params->keysize = state->keysize;\r
+               params->is_dsa = state->is_dsa;\r
+               params->key = &state->key;\r
+               params->dsskey = &state->dsskey;\r
+\r
+               if (!CreateThread(NULL, 0, generate_rsa_key_thread,\r
+                                 params, 0, &threadid)) {\r
+                   MessageBox(hwnd, "Out of thread resources",\r
+                              "Key generation error",\r
+                              MB_OK | MB_ICONERROR);\r
+                   sfree(params);\r
+               } else {\r
+                   state->generation_thread_exists = TRUE;\r
+               }\r
+           }\r
+       }\r
+       break;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDC_KEYSSH1:\r
+         case IDC_KEYSSH2RSA:\r
+         case IDC_KEYSSH2DSA:\r
+           {\r
+               state = (struct MainDlgState *)\r
+                   GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+               if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))\r
+                   CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,\r
+                                    LOWORD(wParam));\r
+               CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,\r
+                                  LOWORD(wParam), MF_BYCOMMAND);\r
+           }\r
+           break;\r
+         case IDC_QUIT:\r
+           PostMessage(hwnd, WM_CLOSE, 0, 0);\r
+           break;\r
+         case IDC_COMMENTEDIT:\r
+           if (HIWORD(wParam) == EN_CHANGE) {\r
+               state = (struct MainDlgState *)\r
+                   GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+               if (state->key_exists) {\r
+                   HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);\r
+                   int len = GetWindowTextLength(editctl);\r
+                   if (*state->commentptr)\r
+                       sfree(*state->commentptr);\r
+                   *state->commentptr = snewn(len + 1, char);\r
+                   GetWindowText(editctl, *state->commentptr, len + 1);\r
+                   if (state->ssh2) {\r
+                       setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,\r
+                                     &state->ssh2key);\r
+                   } else {\r
+                       setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,\r
+                                     &state->key);\r
+                   }\r
+               }\r
+           }\r
+           break;\r
+         case IDC_ABOUT:\r
+           EnableWindow(hwnd, 0);\r
+           DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);\r
+           EnableWindow(hwnd, 1);\r
+           SetActiveWindow(hwnd);\r
+           return 0;\r
+         case IDC_GIVEHELP:\r
+            if (HIWORD(wParam) == BN_CLICKED ||\r
+                HIWORD(wParam) == BN_DOUBLECLICKED) {\r
+               launch_help(hwnd, WINHELP_CTX_puttygen_general);\r
+            }\r
+           return 0;\r
+         case IDC_GENERATE:\r
+            if (HIWORD(wParam) != BN_CLICKED &&\r
+                HIWORD(wParam) != BN_DOUBLECLICKED)\r
+               break;\r
+           state =\r
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+           if (!state->generation_thread_exists) {\r
+               BOOL ok;\r
+               state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);\r
+               if (!ok)\r
+                   state->keysize = DEFAULT_KEYSIZE;\r
+               /* If we ever introduce a new key type, check it here! */\r
+               state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);\r
+               state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);\r
+               if (state->keysize < 256) {\r
+                   int ret = MessageBox(hwnd,\r
+                                        "PuTTYgen will not generate a key"\r
+                                        " smaller than 256 bits.\n"\r
+                                        "Key length reset to 256. Continue?",\r
+                                        "PuTTYgen Warning",\r
+                                        MB_ICONWARNING | MB_OKCANCEL);\r
+                   if (ret != IDOK)\r
+                       break;\r
+                   state->keysize = 256;\r
+                   SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);\r
+               }\r
+               ui_set_state(hwnd, state, 1);\r
+               SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);\r
+               state->key_exists = FALSE;\r
+               state->collecting_entropy = TRUE;\r
+\r
+               /*\r
+                * My brief statistical tests on mouse movements\r
+                * suggest that there are about 2.5 bits of\r
+                * randomness in the x position, 2.5 in the y\r
+                * position, and 1.7 in the message time, making\r
+                * 5.7 bits of unpredictability per mouse movement.\r
+                * However, other people have told me it's far less\r
+                * than that, so I'm going to be stupidly cautious\r
+                * and knock that down to a nice round 2. With this\r
+                * method, we require two words per mouse movement,\r
+                * so with 2 bits per mouse movement we expect 2\r
+                * bits every 2 words.\r
+                */\r
+               state->entropy_required = (state->keysize / 2) * 2;\r
+               state->entropy_got = 0;\r
+               state->entropy_size = (state->entropy_required *\r
+                                      sizeof(unsigned));\r
+               state->entropy = snewn(state->entropy_required, unsigned);\r
+\r
+               SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,\r
+                                  MAKELPARAM(0, state->entropy_required));\r
+               SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);\r
+           }\r
+           break;\r
+         case IDC_SAVE:\r
+          case IDC_EXPORT_OPENSSH:\r
+          case IDC_EXPORT_SSHCOM:\r
+           if (HIWORD(wParam) != BN_CLICKED)\r
+               break;\r
+           state =\r
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+           if (state->key_exists) {\r
+               char filename[FILENAME_MAX];\r
+               char passphrase[PASSPHRASE_MAXLEN];\r
+               char passphrase2[PASSPHRASE_MAXLEN];\r
+                int type, realtype;\r
+\r
+                if (state->ssh2)\r
+                    realtype = SSH_KEYTYPE_SSH2;\r
+                else\r
+                    realtype = SSH_KEYTYPE_SSH1;\r
+\r
+                if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)\r
+                    type = SSH_KEYTYPE_OPENSSH;\r
+                else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)\r
+                    type = SSH_KEYTYPE_SSHCOM;\r
+                else\r
+                    type = realtype;\r
+\r
+                if (type != realtype &&\r
+                    import_target_type(type) != realtype) {\r
+                    char msg[256];\r
+                    sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"\r
+                            " format", (state->ssh2 ? 2 : 1),\r
+                            (state->ssh2 ? 1 : 2));\r
+                   MessageBox(hwnd, msg,\r
+                               "PuTTYgen Error", MB_OK | MB_ICONERROR);\r
+                   break;\r
+                }\r
+\r
+               GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,\r
+                              passphrase, sizeof(passphrase));\r
+               GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,\r
+                              passphrase2, sizeof(passphrase2));\r
+               if (strcmp(passphrase, passphrase2)) {\r
+                   MessageBox(hwnd,\r
+                              "The two passphrases given do not match.",\r
+                              "PuTTYgen Error", MB_OK | MB_ICONERROR);\r
+                   break;\r
+               }\r
+               if (!*passphrase) {\r
+                   int ret;\r
+                   ret = MessageBox(hwnd,\r
+                                    "Are you sure you want to save this key\n"\r
+                                    "without a passphrase to protect it?",\r
+                                    "PuTTYgen Warning",\r
+                                    MB_YESNO | MB_ICONWARNING);\r
+                   if (ret != IDYES)\r
+                       break;\r
+               }\r
+               if (prompt_keyfile(hwnd, "Save private key as:",\r
+                                  filename, 1, (type == realtype))) {\r
+                   int ret;\r
+                   FILE *fp = fopen(filename, "r");\r
+                   if (fp) {\r
+                       char *buffer;\r
+                       fclose(fp);\r
+                       buffer = dupprintf("Overwrite existing file\n%s?",\r
+                                          filename);\r
+                       ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",\r
+                                        MB_YESNO | MB_ICONWARNING);\r
+                       sfree(buffer);\r
+                       if (ret != IDYES)\r
+                           break;\r
+                   }\r
+\r
+                   if (state->ssh2) {\r
+                       Filename fn = filename_from_str(filename);\r
+                        if (type != realtype)\r
+                            ret = export_ssh2(&fn, type, &state->ssh2key,\r
+                                              *passphrase ? passphrase : NULL);\r
+                        else\r
+                            ret = ssh2_save_userkey(&fn, &state->ssh2key,\r
+                                                    *passphrase ? passphrase :\r
+                                                    NULL);\r
+                   } else {\r
+                       Filename fn = filename_from_str(filename);\r
+                        if (type != realtype)\r
+                            ret = export_ssh1(&fn, type, &state->key,\r
+                                              *passphrase ? passphrase : NULL);\r
+                        else\r
+                            ret = saversakey(&fn, &state->key,\r
+                                             *passphrase ? passphrase : NULL);\r
+                   }\r
+                   if (ret <= 0) {\r
+                       MessageBox(hwnd, "Unable to save key file",\r
+                                  "PuTTYgen Error", MB_OK | MB_ICONERROR);\r
+                   }\r
+               }\r
+           }\r
+           break;\r
+         case IDC_SAVEPUB:\r
+           if (HIWORD(wParam) != BN_CLICKED)\r
+               break;\r
+           state =\r
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+           if (state->key_exists) {\r
+               char filename[FILENAME_MAX];\r
+               if (prompt_keyfile(hwnd, "Save public key as:",\r
+                                  filename, 1, 0)) {\r
+                   int ret;\r
+                   FILE *fp = fopen(filename, "r");\r
+                   if (fp) {\r
+                       char *buffer;\r
+                       fclose(fp);\r
+                       buffer = dupprintf("Overwrite existing file\n%s?",\r
+                                          filename);\r
+                       ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",\r
+                                        MB_YESNO | MB_ICONWARNING);\r
+                       sfree(buffer);\r
+                       if (ret != IDYES)\r
+                           break;\r
+                   }\r
+                   if (state->ssh2) {\r
+                       ret = save_ssh2_pubkey(filename, &state->ssh2key);\r
+                   } else {\r
+                       ret = save_ssh1_pubkey(filename, &state->key);\r
+                   }\r
+                   if (ret <= 0) {\r
+                       MessageBox(hwnd, "Unable to save key file",\r
+                                  "PuTTYgen Error", MB_OK | MB_ICONERROR);\r
+                   }\r
+               }\r
+           }\r
+           break;\r
+         case IDC_LOAD:\r
+         case IDC_IMPORT:\r
+           if (HIWORD(wParam) != BN_CLICKED)\r
+               break;\r
+           state =\r
+               (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+           if (!state->generation_thread_exists) {\r
+               char filename[FILENAME_MAX];\r
+               if (prompt_keyfile(hwnd, "Load private key:",\r
+                                  filename, 0, LOWORD(wParam)==IDC_LOAD))\r
+                   load_key_file(hwnd, state, filename_from_str(filename),\r
+                                 LOWORD(wParam) != IDC_LOAD);\r
+           }\r
+           break;\r
+       }\r
+       return 0;\r
+      case WM_DONEKEY:\r
+       state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+       state->generation_thread_exists = FALSE;\r
+       state->key_exists = TRUE;\r
+       SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,\r
+                          MAKELPARAM(0, PROGRESSRANGE));\r
+       SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);\r
+       if (state->ssh2) {\r
+           if (state->is_dsa) {\r
+               state->ssh2key.data = &state->dsskey;\r
+               state->ssh2key.alg = &ssh_dss;\r
+           } else {\r
+               state->ssh2key.data = &state->key;\r
+               state->ssh2key.alg = &ssh_rsa;\r
+           }\r
+           state->commentptr = &state->ssh2key.comment;\r
+       } else {\r
+           state->commentptr = &state->key.comment;\r
+       }\r
+       /*\r
+        * Invent a comment for the key. We'll do this by including\r
+        * the date in it. This will be so horrifyingly ugly that\r
+        * the user will immediately want to change it, which is\r
+        * what we want :-)\r
+        */\r
+       *state->commentptr = snewn(30, char);\r
+       {\r
+           struct tm tm;\r
+           tm = ltime();\r
+           if (state->is_dsa)\r
+               strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);\r
+           else\r
+               strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);\r
+       }\r
+\r
+       /*\r
+        * Now update the key controls with all the key data.\r
+        */\r
+       {\r
+           char *savecomment;\r
+           /*\r
+            * Blank passphrase, initially. This isn't dangerous,\r
+            * because we will warn (Are You Sure?) before allowing\r
+            * the user to save an unprotected private key.\r
+            */\r
+           SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");\r
+           SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");\r
+           /*\r
+            * Set the comment.\r
+            */\r
+           SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);\r
+           /*\r
+            * Set the key fingerprint.\r
+            */\r
+           savecomment = *state->commentptr;\r
+           *state->commentptr = NULL;\r
+           if (state->ssh2) {\r
+               char *fp;\r
+               fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);\r
+               SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);\r
+               sfree(fp);\r
+           } else {\r
+               char buf[128];\r
+               rsa_fingerprint(buf, sizeof(buf), &state->key);\r
+               SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);\r
+           }\r
+           *state->commentptr = savecomment;\r
+           /*\r
+            * Construct a decimal representation of the key, for\r
+            * pasting into .ssh/authorized_keys or\r
+            * .ssh/authorized_keys2 on a Unix box.\r
+            */\r
+           if (state->ssh2) {\r
+               setupbigedit2(hwnd, IDC_KEYDISPLAY,\r
+                             IDC_PKSTATIC, &state->ssh2key);\r
+           } else {\r
+               setupbigedit1(hwnd, IDC_KEYDISPLAY,\r
+                             IDC_PKSTATIC, &state->key);\r
+           }\r
+       }\r
+       /*\r
+        * Finally, hide the progress bar and show the key data.\r
+        */\r
+       ui_set_state(hwnd, state, 2);\r
+       break;\r
+      case WM_HELP:\r
+        {\r
+            int id = ((LPHELPINFO)lParam)->iCtrlId;\r
+            char *topic = NULL;\r
+            switch (id) {\r
+              case IDC_GENERATING:\r
+              case IDC_PROGRESS:\r
+              case IDC_GENSTATIC:\r
+              case IDC_GENERATE:\r
+                topic = WINHELP_CTX_puttygen_generate; break;\r
+              case IDC_PKSTATIC:\r
+              case IDC_KEYDISPLAY:\r
+                topic = WINHELP_CTX_puttygen_pastekey; break;\r
+              case IDC_FPSTATIC:\r
+              case IDC_FINGERPRINT:\r
+                topic = WINHELP_CTX_puttygen_fingerprint; break;\r
+              case IDC_COMMENTSTATIC:\r
+              case IDC_COMMENTEDIT:\r
+                topic = WINHELP_CTX_puttygen_comment; break;\r
+              case IDC_PASSPHRASE1STATIC:\r
+              case IDC_PASSPHRASE1EDIT:\r
+              case IDC_PASSPHRASE2STATIC:\r
+              case IDC_PASSPHRASE2EDIT:\r
+                topic = WINHELP_CTX_puttygen_passphrase; break;\r
+              case IDC_LOADSTATIC:\r
+              case IDC_LOAD:\r
+                topic = WINHELP_CTX_puttygen_load; break;\r
+              case IDC_SAVESTATIC:\r
+              case IDC_SAVE:\r
+                topic = WINHELP_CTX_puttygen_savepriv; break;\r
+              case IDC_SAVEPUB:\r
+                topic = WINHELP_CTX_puttygen_savepub; break;\r
+              case IDC_TYPESTATIC:\r
+              case IDC_KEYSSH1:\r
+              case IDC_KEYSSH2RSA:\r
+              case IDC_KEYSSH2DSA:\r
+                topic = WINHELP_CTX_puttygen_keytype; break;\r
+              case IDC_BITSSTATIC:\r
+              case IDC_BITS:\r
+                topic = WINHELP_CTX_puttygen_bits; break;\r
+              case IDC_IMPORT:\r
+              case IDC_EXPORT_OPENSSH:\r
+              case IDC_EXPORT_SSHCOM:\r
+                topic = WINHELP_CTX_puttygen_conversions; break;\r
+            }\r
+            if (topic) {\r
+                launch_help(hwnd, topic);\r
+            } else {\r
+                MessageBeep(0);\r
+            }\r
+        }\r
+        break;\r
+      case WM_CLOSE:\r
+       state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
+       sfree(state);\r
+       quit_help(hwnd);\r
+       EndDialog(hwnd, 1);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+void cleanup_exit(int code)\r
+{\r
+    shutdown_help();\r
+    exit(code);\r
+}\r
+\r
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)\r
+{\r
+    int argc;\r
+    char **argv;\r
+    int ret;\r
+\r
+    InitCommonControls();\r
+    hinst = inst;\r
+    hwnd = NULL;\r
+\r
+    /*\r
+     * See if we can find our Help file.\r
+     */\r
+    init_help();\r
+\r
+    split_into_argv(cmdline, &argc, &argv, NULL);\r
+\r
+    if (argc > 0) {\r
+       if (!strcmp(argv[0], "-pgpfp")) {\r
+           pgp_fingerprints();\r
+           exit(1);\r
+       } else {\r
+           /*\r
+            * Assume the first argument to be a private key file, and\r
+            * attempt to load it.\r
+            */\r
+           cmdline_keyfile = argv[0];\r
+       }\r
+    }\r
+\r
+    random_ref();\r
+    ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;\r
+\r
+    cleanup_exit(ret);\r
+    return ret;                               /* just in case optimiser complains */\r
+}\r
diff --git a/putty/WINDOWS/WINPGNT.C b/putty/WINDOWS/WINPGNT.C
new file mode 100644 (file)
index 0000000..bf920f1
--- /dev/null
@@ -0,0 +1,2176 @@
+/*\r
+ * Pageant: the PuTTY Authentication Agent.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <assert.h>\r
+#include <tchar.h>\r
+\r
+#define PUTTY_DO_GLOBALS\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "misc.h"\r
+#include "tree234.h"\r
+\r
+#include <shellapi.h>\r
+\r
+#ifndef NO_SECURITY\r
+#include <aclapi.h>\r
+#ifdef DEBUG_IPC\r
+#define _WIN32_WINNT 0x0500            /* for ConvertSidToStringSid */\r
+#include <sddl.h>\r
+#endif\r
+#endif\r
+\r
+#define IDI_MAINICON 200\r
+#define IDI_TRAYICON 201\r
+\r
+#define WM_SYSTRAY   (WM_APP + 6)\r
+#define WM_SYSTRAY2  (WM_APP + 7)\r
+\r
+#define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */\r
+\r
+/*\r
+ * FIXME: maybe some day we can sort this out ...\r
+ */\r
+#define AGENT_MAX_MSGLEN  8192\r
+\r
+/* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of\r
+ * wParam are used by Windows, and should be masked off, so we shouldn't\r
+ * attempt to store information in them. Hence all these identifiers have\r
+ * the low 4 bits clear. Also, identifiers should < 0xF000. */\r
+\r
+#define IDM_CLOSE    0x0010\r
+#define IDM_VIEWKEYS 0x0020\r
+#define IDM_ADDKEY   0x0030\r
+#define IDM_HELP     0x0040\r
+#define IDM_ABOUT    0x0050\r
+\r
+#define APPNAME "Pageant"\r
+\r
+extern char ver[];\r
+\r
+static HWND keylist;\r
+static HWND aboutbox;\r
+static HMENU systray_menu, session_menu;\r
+static int already_running;\r
+\r
+static char *putty_path;\r
+\r
+/* CWD for "add key" file requester. */\r
+static filereq *keypath = NULL;\r
+\r
+#define IDM_PUTTY         0x0060\r
+#define IDM_SESSIONS_BASE 0x1000\r
+#define IDM_SESSIONS_MAX  0x2000\r
+#define PUTTY_REGKEY      "Software\\SimonTatham\\PuTTY\\Sessions"\r
+#define PUTTY_DEFAULT     "Default%20Settings"\r
+static int initial_menuitems_count;\r
+\r
+/*\r
+ * Print a modal (Really Bad) message box and perform a fatal exit.\r
+ */\r
+void modalfatalbox(char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *buf;\r
+\r
+    va_start(ap, fmt);\r
+    buf = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    MessageBox(hwnd, buf, "Pageant Fatal Error",\r
+              MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);\r
+    sfree(buf);\r
+    exit(1);\r
+}\r
+\r
+/* Un-munge session names out of the registry. */\r
+static void unmungestr(char *in, char *out, int outlen)\r
+{\r
+    while (*in) {\r
+       if (*in == '%' && in[1] && in[2]) {\r
+           int i, j;\r
+\r
+           i = in[1] - '0';\r
+           i -= (i > 9 ? 7 : 0);\r
+           j = in[2] - '0';\r
+           j -= (j > 9 ? 7 : 0);\r
+\r
+           *out++ = (i << 4) + j;\r
+           if (!--outlen)\r
+               return;\r
+           in += 3;\r
+       } else {\r
+           *out++ = *in++;\r
+           if (!--outlen)\r
+               return;\r
+       }\r
+    }\r
+    *out = '\0';\r
+    return;\r
+}\r
+\r
+static tree234 *rsakeys, *ssh2keys;\r
+\r
+static int has_security;\r
+#ifndef NO_SECURITY\r
+DECL_WINDOWS_FUNCTION(extern, DWORD, GetSecurityInfo,\r
+                     (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,\r
+                      PSID *, PSID *, PACL *, PACL *,\r
+                      PSECURITY_DESCRIPTOR *));\r
+#endif\r
+\r
+/*\r
+ * Forward references\r
+ */\r
+static void *make_keylist1(int *length);\r
+static void *make_keylist2(int *length);\r
+static void *get_keylist1(int *length);\r
+static void *get_keylist2(int *length);\r
+\r
+/*\r
+ * We need this to link with the RSA code, because rsaencrypt()\r
+ * pads its data with random bytes. Since we only use rsadecrypt()\r
+ * and the signing functions, which are deterministic, this should\r
+ * never be called.\r
+ *\r
+ * If it _is_ called, there is a _serious_ problem, because it\r
+ * won't generate true random numbers. So we must scream, panic,\r
+ * and exit immediately if that should happen.\r
+ */\r
+int random_byte(void)\r
+{\r
+    MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);\r
+    exit(0);\r
+    /* this line can't be reached but it placates MSVC's warnings :-) */\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Blob structure for passing to the asymmetric SSH-2 key compare\r
+ * function, prototyped here.\r
+ */\r
+struct blob {\r
+    unsigned char *blob;\r
+    int len;\r
+};\r
+static int cmpkeys_ssh2_asymm(void *av, void *bv);\r
+\r
+#define PASSPHRASE_MAXLEN 512\r
+\r
+struct PassphraseProcStruct {\r
+    char *passphrase;\r
+    char *comment;\r
+};\r
+\r
+static tree234 *passphrases = NULL;\r
+\r
+/* \r
+ * After processing a list of filenames, we want to forget the\r
+ * passphrases.\r
+ */\r
+static void forget_passphrases(void)\r
+{\r
+    while (count234(passphrases) > 0) {\r
+       char *pp = index234(passphrases, 0);\r
+       memset(pp, 0, strlen(pp));\r
+       delpos234(passphrases, 0);\r
+       free(pp);\r
+    }\r
+}\r
+\r
+/*\r
+ * Dialog-box function for the Licence box.\r
+ */\r
+static int CALLBACK LicenceProc(HWND hwnd, UINT msg,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       return 1;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           EndDialog(hwnd, 1);\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       EndDialog(hwnd, 1);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Dialog-box function for the About box.\r
+ */\r
+static int CALLBACK AboutProc(HWND hwnd, UINT msg,\r
+                             WPARAM wParam, LPARAM lParam)\r
+{\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       SetDlgItemText(hwnd, 100, ver);\r
+       return 1;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           aboutbox = NULL;\r
+           DestroyWindow(hwnd);\r
+           return 0;\r
+         case 101:\r
+           EnableWindow(hwnd, 0);\r
+           DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);\r
+           EnableWindow(hwnd, 1);\r
+           SetActiveWindow(hwnd);\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       aboutbox = NULL;\r
+       DestroyWindow(hwnd);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+static HWND passphrase_box;\r
+\r
+/*\r
+ * Dialog-box function for the passphrase box.\r
+ */\r
+static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,\r
+                                  WPARAM wParam, LPARAM lParam)\r
+{\r
+    static char *passphrase = NULL;\r
+    struct PassphraseProcStruct *p;\r
+\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       passphrase_box = hwnd;\r
+       /*\r
+        * Centre the window.\r
+        */\r
+       {                              /* centre the window */\r
+           RECT rs, rd;\r
+           HWND hw;\r
+\r
+           hw = GetDesktopWindow();\r
+           if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
+               MoveWindow(hwnd,\r
+                          (rs.right + rs.left + rd.left - rd.right) / 2,\r
+                          (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
+                          rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
+       }\r
+\r
+       SetForegroundWindow(hwnd);\r
+       SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,\r
+                    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+       p = (struct PassphraseProcStruct *) lParam;\r
+       passphrase = p->passphrase;\r
+       if (p->comment)\r
+           SetDlgItemText(hwnd, 101, p->comment);\r
+       *passphrase = 0;\r
+       SetDlgItemText(hwnd, 102, passphrase);\r
+       return 0;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+           if (*passphrase)\r
+               EndDialog(hwnd, 1);\r
+           else\r
+               MessageBeep(0);\r
+           return 0;\r
+         case IDCANCEL:\r
+           EndDialog(hwnd, 0);\r
+           return 0;\r
+         case 102:                    /* edit box */\r
+           if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {\r
+               GetDlgItemText(hwnd, 102, passphrase,\r
+                              PASSPHRASE_MAXLEN - 1);\r
+               passphrase[PASSPHRASE_MAXLEN - 1] = '\0';\r
+           }\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_CLOSE:\r
+       EndDialog(hwnd, 0);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Warn about the obsolescent key file format.\r
+ */\r
+void old_keyfile_warning(void)\r
+{\r
+    static const char mbtitle[] = "PuTTY Key File Warning";\r
+    static const char message[] =\r
+       "You are loading an SSH-2 private key which has an\n"\r
+       "old version of the file format. This means your key\n"\r
+       "file is not fully tamperproof. Future versions of\n"\r
+       "PuTTY may stop supporting this private key format,\n"\r
+       "so we recommend you convert your key to the new\n"\r
+       "format.\n"\r
+       "\n"\r
+       "You can perform this conversion by loading the key\n"\r
+       "into PuTTYgen and then saving it again.";\r
+\r
+    MessageBox(NULL, message, mbtitle, MB_OK);\r
+}\r
+\r
+/*\r
+ * Update the visible key list.\r
+ */\r
+static void keylist_update(void)\r
+{\r
+    struct RSAKey *rkey;\r
+    struct ssh2_userkey *skey;\r
+    int i;\r
+\r
+    if (keylist) {\r
+       SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);\r
+       for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {\r
+           char listentry[512], *p;\r
+           /*\r
+            * Replace two spaces in the fingerprint with tabs, for\r
+            * nice alignment in the box.\r
+            */\r
+           strcpy(listentry, "ssh1\t");\r
+           p = listentry + strlen(listentry);\r
+           rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);\r
+           p = strchr(listentry, ' ');\r
+           if (p)\r
+               *p = '\t';\r
+           p = strchr(listentry, ' ');\r
+           if (p)\r
+               *p = '\t';\r
+           SendDlgItemMessage(keylist, 100, LB_ADDSTRING,\r
+                              0, (LPARAM) listentry);\r
+       }\r
+       for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {\r
+           char listentry[512], *p;\r
+           int len;\r
+           /*\r
+            * Replace two spaces in the fingerprint with tabs, for\r
+            * nice alignment in the box.\r
+            */\r
+           p = skey->alg->fingerprint(skey->data);\r
+           strncpy(listentry, p, sizeof(listentry));\r
+           p = strchr(listentry, ' ');\r
+           if (p)\r
+               *p = '\t';\r
+           p = strchr(listentry, ' ');\r
+           if (p)\r
+               *p = '\t';\r
+           len = strlen(listentry);\r
+           if (len < sizeof(listentry) - 2) {\r
+               listentry[len] = '\t';\r
+               strncpy(listentry + len + 1, skey->comment,\r
+                       sizeof(listentry) - len - 1);\r
+           }\r
+           SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,\r
+                              (LPARAM) listentry);\r
+       }\r
+       SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);\r
+    }\r
+}\r
+\r
+/*\r
+ * This function loads a key from a file and adds it.\r
+ */\r
+static void add_keyfile(Filename filename)\r
+{\r
+    char passphrase[PASSPHRASE_MAXLEN];\r
+    struct RSAKey *rkey = NULL;\r
+    struct ssh2_userkey *skey = NULL;\r
+    int needs_pass;\r
+    int ret;\r
+    int attempts;\r
+    char *comment;\r
+    const char *error = NULL;\r
+    struct PassphraseProcStruct pps;\r
+    int type;\r
+    int original_pass;\r
+       \r
+    type = key_type(&filename);\r
+    if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {\r
+       char *msg = dupprintf("Couldn't load this key (%s)",\r
+                             key_type_to_str(type));\r
+       message_box(msg, APPNAME, MB_OK | MB_ICONERROR,\r
+                   HELPCTXID(errors_cantloadkey));\r
+       sfree(msg);\r
+       return;\r
+    }\r
+\r
+    /*\r
+     * See if the key is already loaded (in the primary Pageant,\r
+     * which may or may not be us).\r
+     */\r
+    {\r
+       void *blob;\r
+       unsigned char *keylist, *p;\r
+       int i, nkeys, bloblen, keylistlen;\r
+\r
+       if (type == SSH_KEYTYPE_SSH1) {\r
+           if (!rsakey_pubblob(&filename, &blob, &bloblen, NULL, &error)) {\r
+               char *msg = dupprintf("Couldn't load private key (%s)", error);\r
+               message_box(msg, APPNAME, MB_OK | MB_ICONERROR,\r
+                           HELPCTXID(errors_cantloadkey));\r
+               sfree(msg);\r
+               return;\r
+           }\r
+           keylist = get_keylist1(&keylistlen);\r
+       } else {\r
+           unsigned char *blob2;\r
+           blob = ssh2_userkey_loadpub(&filename, NULL, &bloblen,\r
+                                       NULL, &error);\r
+           if (!blob) {\r
+               char *msg = dupprintf("Couldn't load private key (%s)", error);\r
+               message_box(msg, APPNAME, MB_OK | MB_ICONERROR,\r
+                           HELPCTXID(errors_cantloadkey));\r
+               sfree(msg);\r
+               return;\r
+           }\r
+           /* For our purposes we want the blob prefixed with its length */\r
+           blob2 = snewn(bloblen+4, unsigned char);\r
+           PUT_32BIT(blob2, bloblen);\r
+           memcpy(blob2 + 4, blob, bloblen);\r
+           sfree(blob);\r
+           blob = blob2;\r
+\r
+           keylist = get_keylist2(&keylistlen);\r
+       }\r
+       if (keylist) {\r
+           if (keylistlen < 4) {\r
+               MessageBox(NULL, "Received broken key list?!", APPNAME,\r
+                          MB_OK | MB_ICONERROR);\r
+               return;\r
+           }\r
+           nkeys = GET_32BIT(keylist);\r
+           p = keylist + 4;\r
+           keylistlen -= 4;\r
+\r
+           for (i = 0; i < nkeys; i++) {\r
+               if (!memcmp(blob, p, bloblen)) {\r
+                   /* Key is already present; we can now leave. */\r
+                   sfree(keylist);\r
+                   sfree(blob);\r
+                   return;\r
+               }\r
+               /* Now skip over public blob */\r
+               if (type == SSH_KEYTYPE_SSH1) {\r
+                   int n = rsa_public_blob_len(p, keylistlen);\r
+                   if (n < 0) {\r
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,\r
+                                  MB_OK | MB_ICONERROR);\r
+                       return;\r
+                   }\r
+                   p += n;\r
+                   keylistlen -= n;\r
+               } else {\r
+                   int n;\r
+                   if (keylistlen < 4) {\r
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,\r
+                                  MB_OK | MB_ICONERROR);\r
+                       return;\r
+                   }\r
+                   n = 4 + GET_32BIT(p);\r
+                   if (keylistlen < n) {\r
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,\r
+                                  MB_OK | MB_ICONERROR);\r
+                       return;\r
+                   }\r
+                   p += n;\r
+                   keylistlen -= n;\r
+               }\r
+               /* Now skip over comment field */\r
+               {\r
+                   int n;\r
+                   if (keylistlen < 4) {\r
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,\r
+                                  MB_OK | MB_ICONERROR);\r
+                       return;\r
+                   }\r
+                   n = 4 + GET_32BIT(p);\r
+                   if (keylistlen < n) {\r
+                       MessageBox(NULL, "Received broken key list?!", APPNAME,\r
+                                  MB_OK | MB_ICONERROR);\r
+                       return;\r
+                   }\r
+                   p += n;\r
+                   keylistlen -= n;\r
+               }\r
+           }\r
+\r
+           sfree(keylist);\r
+       }\r
+\r
+       sfree(blob);\r
+    }\r
+\r
+    error = NULL;\r
+    if (type == SSH_KEYTYPE_SSH1)\r
+       needs_pass = rsakey_encrypted(&filename, &comment);\r
+    else\r
+       needs_pass = ssh2_userkey_encrypted(&filename, &comment);\r
+    attempts = 0;\r
+    if (type == SSH_KEYTYPE_SSH1)\r
+       rkey = snew(struct RSAKey);\r
+    pps.passphrase = passphrase;\r
+    pps.comment = comment;\r
+    original_pass = 0;\r
+    do {\r
+       if (needs_pass) {\r
+           /* try all the remembered passphrases first */\r
+           char *pp = index234(passphrases, attempts);\r
+           if(pp) {\r
+               strcpy(passphrase, pp);\r
+           } else {\r
+               int dlgret;\r
+               original_pass = 1;\r
+               dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),\r
+                                       NULL, PassphraseProc, (LPARAM) &pps);\r
+               passphrase_box = NULL;\r
+               if (!dlgret) {\r
+                   if (comment)\r
+                       sfree(comment);\r
+                   if (type == SSH_KEYTYPE_SSH1)\r
+                       sfree(rkey);\r
+                   return;                    /* operation cancelled */\r
+               }\r
+           }\r
+       } else\r
+           *passphrase = '\0';\r
+       if (type == SSH_KEYTYPE_SSH1)\r
+           ret = loadrsakey(&filename, rkey, passphrase, &error);\r
+       else {\r
+           skey = ssh2_load_userkey(&filename, passphrase, &error);\r
+           if (skey == SSH2_WRONG_PASSPHRASE)\r
+               ret = -1;\r
+           else if (!skey)\r
+               ret = 0;\r
+           else\r
+               ret = 1;\r
+       }\r
+       attempts++;\r
+    } while (ret == -1);\r
+\r
+    /* if they typed in an ok passphrase, remember it */\r
+    if(original_pass && ret) {\r
+       char *pp = dupstr(passphrase);\r
+       addpos234(passphrases, pp, 0);\r
+    }\r
+\r
+    if (comment)\r
+       sfree(comment);\r
+    if (ret == 0) {\r
+       char *msg = dupprintf("Couldn't load private key (%s)", error);\r
+       message_box(msg, APPNAME, MB_OK | MB_ICONERROR,\r
+                   HELPCTXID(errors_cantloadkey));\r
+       sfree(msg);\r
+       if (type == SSH_KEYTYPE_SSH1)\r
+           sfree(rkey);\r
+       return;\r
+    }\r
+    if (type == SSH_KEYTYPE_SSH1) {\r
+       if (already_running) {\r
+           unsigned char *request, *response;\r
+           void *vresponse;\r
+           int reqlen, clen, resplen, ret;\r
+\r
+           clen = strlen(rkey->comment);\r
+\r
+           reqlen = 4 + 1 +           /* length, message type */\r
+               4 +                    /* bit count */\r
+               ssh1_bignum_length(rkey->modulus) +\r
+               ssh1_bignum_length(rkey->exponent) +\r
+               ssh1_bignum_length(rkey->private_exponent) +\r
+               ssh1_bignum_length(rkey->iqmp) +\r
+               ssh1_bignum_length(rkey->p) +\r
+               ssh1_bignum_length(rkey->q) + 4 + clen  /* comment */\r
+               ;\r
+\r
+           request = snewn(reqlen, unsigned char);\r
+\r
+           request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;\r
+           reqlen = 5;\r
+           PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));\r
+           reqlen += 4;\r
+           reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);\r
+           reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);\r
+           reqlen +=\r
+               ssh1_write_bignum(request + reqlen,\r
+                                 rkey->private_exponent);\r
+           reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);\r
+           reqlen += ssh1_write_bignum(request + reqlen, rkey->p);\r
+           reqlen += ssh1_write_bignum(request + reqlen, rkey->q);\r
+           PUT_32BIT(request + reqlen, clen);\r
+           memcpy(request + reqlen + 4, rkey->comment, clen);\r
+           reqlen += 4 + clen;\r
+           PUT_32BIT(request, reqlen - 4);\r
+\r
+           ret = agent_query(request, reqlen, &vresponse, &resplen,\r
+                             NULL, NULL);\r
+           assert(ret == 1);\r
+           response = vresponse;\r
+           if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)\r
+               MessageBox(NULL, "The already running Pageant "\r
+                          "refused to add the key.", APPNAME,\r
+                          MB_OK | MB_ICONERROR);\r
+\r
+           sfree(request);\r
+           sfree(response);\r
+       } else {\r
+           if (add234(rsakeys, rkey) != rkey)\r
+               sfree(rkey);           /* already present, don't waste RAM */\r
+       }\r
+    } else {\r
+       if (already_running) {\r
+           unsigned char *request, *response;\r
+           void *vresponse;\r
+           int reqlen, alglen, clen, keybloblen, resplen, ret;\r
+           alglen = strlen(skey->alg->name);\r
+           clen = strlen(skey->comment);\r
+\r
+           keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);\r
+\r
+           reqlen = 4 + 1 +           /* length, message type */\r
+               4 + alglen +           /* algorithm name */\r
+               keybloblen +           /* key data */\r
+               4 + clen               /* comment */\r
+               ;\r
+\r
+           request = snewn(reqlen, unsigned char);\r
+\r
+           request[4] = SSH2_AGENTC_ADD_IDENTITY;\r
+           reqlen = 5;\r
+           PUT_32BIT(request + reqlen, alglen);\r
+           reqlen += 4;\r
+           memcpy(request + reqlen, skey->alg->name, alglen);\r
+           reqlen += alglen;\r
+           reqlen += skey->alg->openssh_fmtkey(skey->data,\r
+                                               request + reqlen,\r
+                                               keybloblen);\r
+           PUT_32BIT(request + reqlen, clen);\r
+           memcpy(request + reqlen + 4, skey->comment, clen);\r
+           reqlen += clen + 4;\r
+           PUT_32BIT(request, reqlen - 4);\r
+\r
+           ret = agent_query(request, reqlen, &vresponse, &resplen,\r
+                             NULL, NULL);\r
+           assert(ret == 1);\r
+           response = vresponse;\r
+           if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)\r
+               MessageBox(NULL, "The already running Pageant "\r
+                          "refused to add the key.", APPNAME,\r
+                          MB_OK | MB_ICONERROR);\r
+\r
+           sfree(request);\r
+           sfree(response);\r
+       } else {\r
+           if (add234(ssh2keys, skey) != skey) {\r
+               skey->alg->freekey(skey->data);\r
+               sfree(skey);           /* already present, don't waste RAM */\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Create an SSH-1 key list in a malloc'ed buffer; return its\r
+ * length.\r
+ */\r
+static void *make_keylist1(int *length)\r
+{\r
+    int i, nkeys, len;\r
+    struct RSAKey *key;\r
+    unsigned char *blob, *p, *ret;\r
+    int bloblen;\r
+\r
+    /*\r
+     * Count up the number and length of keys we hold.\r
+     */\r
+    len = 4;\r
+    nkeys = 0;\r
+    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {\r
+       nkeys++;\r
+       blob = rsa_public_blob(key, &bloblen);\r
+       len += bloblen;\r
+       sfree(blob);\r
+       len += 4 + strlen(key->comment);\r
+    }\r
+\r
+    /* Allocate the buffer. */\r
+    p = ret = snewn(len, unsigned char);\r
+    if (length) *length = len;\r
+\r
+    PUT_32BIT(p, nkeys);\r
+    p += 4;\r
+    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {\r
+       blob = rsa_public_blob(key, &bloblen);\r
+       memcpy(p, blob, bloblen);\r
+       p += bloblen;\r
+       sfree(blob);\r
+       PUT_32BIT(p, strlen(key->comment));\r
+       memcpy(p + 4, key->comment, strlen(key->comment));\r
+       p += 4 + strlen(key->comment);\r
+    }\r
+\r
+    assert(p - ret == len);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Create an SSH-2 key list in a malloc'ed buffer; return its\r
+ * length.\r
+ */\r
+static void *make_keylist2(int *length)\r
+{\r
+    struct ssh2_userkey *key;\r
+    int i, len, nkeys;\r
+    unsigned char *blob, *p, *ret;\r
+    int bloblen;\r
+\r
+    /*\r
+     * Count up the number and length of keys we hold.\r
+     */\r
+    len = 4;\r
+    nkeys = 0;\r
+    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {\r
+       nkeys++;\r
+       len += 4;              /* length field */\r
+       blob = key->alg->public_blob(key->data, &bloblen);\r
+       len += bloblen;\r
+       sfree(blob);\r
+       len += 4 + strlen(key->comment);\r
+    }\r
+\r
+    /* Allocate the buffer. */\r
+    p = ret = snewn(len, unsigned char);\r
+    if (length) *length = len;\r
+\r
+    /*\r
+     * Packet header is the obvious five bytes, plus four\r
+     * bytes for the key count.\r
+     */\r
+    PUT_32BIT(p, nkeys);\r
+    p += 4;\r
+    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {\r
+       blob = key->alg->public_blob(key->data, &bloblen);\r
+       PUT_32BIT(p, bloblen);\r
+       p += 4;\r
+       memcpy(p, blob, bloblen);\r
+       p += bloblen;\r
+       sfree(blob);\r
+       PUT_32BIT(p, strlen(key->comment));\r
+       memcpy(p + 4, key->comment, strlen(key->comment));\r
+       p += 4 + strlen(key->comment);\r
+    }\r
+\r
+    assert(p - ret == len);\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Acquire a keylist1 from the primary Pageant; this means either\r
+ * calling make_keylist1 (if that's us) or sending a message to the\r
+ * primary Pageant (if it's not).\r
+ */\r
+static void *get_keylist1(int *length)\r
+{\r
+    void *ret;\r
+\r
+    if (already_running) {\r
+       unsigned char request[5], *response;\r
+       void *vresponse;\r
+       int resplen, retval;\r
+       request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;\r
+       PUT_32BIT(request, 4);\r
+\r
+       retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);\r
+       assert(retval == 1);\r
+       response = vresponse;\r
+       if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)\r
+           return NULL;\r
+\r
+       ret = snewn(resplen-5, unsigned char);\r
+       memcpy(ret, response+5, resplen-5);\r
+       sfree(response);\r
+\r
+       if (length)\r
+           *length = resplen-5;\r
+    } else {\r
+       ret = make_keylist1(length);\r
+    }\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Acquire a keylist2 from the primary Pageant; this means either\r
+ * calling make_keylist2 (if that's us) or sending a message to the\r
+ * primary Pageant (if it's not).\r
+ */\r
+static void *get_keylist2(int *length)\r
+{\r
+    void *ret;\r
+\r
+    if (already_running) {\r
+       unsigned char request[5], *response;\r
+       void *vresponse;\r
+       int resplen, retval;\r
+\r
+       request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;\r
+       PUT_32BIT(request, 4);\r
+\r
+       retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);\r
+       assert(retval == 1);\r
+       response = vresponse;\r
+       if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)\r
+           return NULL;\r
+\r
+       ret = snewn(resplen-5, unsigned char);\r
+       memcpy(ret, response+5, resplen-5);\r
+       sfree(response);\r
+\r
+       if (length)\r
+           *length = resplen-5;\r
+    } else {\r
+       ret = make_keylist2(length);\r
+    }\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * This is the main agent function that answers messages.\r
+ */\r
+static void answer_msg(void *msg)\r
+{\r
+    unsigned char *p = msg;\r
+    unsigned char *ret = msg;\r
+    unsigned char *msgend;\r
+    int type;\r
+\r
+    /*\r
+     * Get the message length.\r
+     */\r
+    msgend = p + 4 + GET_32BIT(p);\r
+\r
+    /*\r
+     * Get the message type.\r
+     */\r
+    if (msgend < p+5)\r
+       goto failure;\r
+    type = p[4];\r
+\r
+    p += 5;\r
+    switch (type) {\r
+      case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:\r
+       /*\r
+        * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.\r
+        */\r
+       {\r
+           int len;\r
+           void *keylist;\r
+\r
+           ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;\r
+           keylist = make_keylist1(&len);\r
+           if (len + 5 > AGENT_MAX_MSGLEN) {\r
+               sfree(keylist);\r
+               goto failure;\r
+           }\r
+           PUT_32BIT(ret, len + 1);\r
+           memcpy(ret + 5, keylist, len);\r
+           sfree(keylist);\r
+       }\r
+       break;\r
+      case SSH2_AGENTC_REQUEST_IDENTITIES:\r
+       /*\r
+        * Reply with SSH2_AGENT_IDENTITIES_ANSWER.\r
+        */\r
+       {\r
+           int len;\r
+           void *keylist;\r
+\r
+           ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;\r
+           keylist = make_keylist2(&len);\r
+           if (len + 5 > AGENT_MAX_MSGLEN) {\r
+               sfree(keylist);\r
+               goto failure;\r
+           }\r
+           PUT_32BIT(ret, len + 1);\r
+           memcpy(ret + 5, keylist, len);\r
+           sfree(keylist);\r
+       }\r
+       break;\r
+      case SSH1_AGENTC_RSA_CHALLENGE:\r
+       /*\r
+        * Reply with either SSH1_AGENT_RSA_RESPONSE or\r
+        * SSH_AGENT_FAILURE, depending on whether we have that key\r
+        * or not.\r
+        */\r
+       {\r
+           struct RSAKey reqkey, *key;\r
+           Bignum challenge, response;\r
+           unsigned char response_source[48], response_md5[16];\r
+           struct MD5Context md5c;\r
+           int i, len;\r
+\r
+           p += 4;\r
+           i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);\r
+           if (i < 0)\r
+               goto failure;\r
+           p += i;\r
+           i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);\r
+           if (i < 0)\r
+               goto failure;\r
+           p += i;\r
+           i = ssh1_read_bignum(p, msgend - p, &challenge);\r
+           if (i < 0)\r
+               goto failure;\r
+           p += i;\r
+           if (msgend < p+16) {\r
+               freebn(reqkey.exponent);\r
+               freebn(reqkey.modulus);\r
+               freebn(challenge);\r
+               goto failure;\r
+           }\r
+           memcpy(response_source + 32, p, 16);\r
+           p += 16;\r
+           if (msgend < p+4 ||\r
+               GET_32BIT(p) != 1 ||\r
+               (key = find234(rsakeys, &reqkey, NULL)) == NULL) {\r
+               freebn(reqkey.exponent);\r
+               freebn(reqkey.modulus);\r
+               freebn(challenge);\r
+               goto failure;\r
+           }\r
+           response = rsadecrypt(challenge, key);\r
+           for (i = 0; i < 32; i++)\r
+               response_source[i] = bignum_byte(response, 31 - i);\r
+\r
+           MD5Init(&md5c);\r
+           MD5Update(&md5c, response_source, 48);\r
+           MD5Final(response_md5, &md5c);\r
+           memset(response_source, 0, 48);     /* burn the evidence */\r
+           freebn(response);          /* and that evidence */\r
+           freebn(challenge);         /* yes, and that evidence */\r
+           freebn(reqkey.exponent);   /* and free some memory ... */\r
+           freebn(reqkey.modulus);    /* ... while we're at it. */\r
+\r
+           /*\r
+            * Packet is the obvious five byte header, plus sixteen\r
+            * bytes of MD5.\r
+            */\r
+           len = 5 + 16;\r
+           PUT_32BIT(ret, len - 4);\r
+           ret[4] = SSH1_AGENT_RSA_RESPONSE;\r
+           memcpy(ret + 5, response_md5, 16);\r
+       }\r
+       break;\r
+      case SSH2_AGENTC_SIGN_REQUEST:\r
+       /*\r
+        * Reply with either SSH2_AGENT_SIGN_RESPONSE or\r
+        * SSH_AGENT_FAILURE, depending on whether we have that key\r
+        * or not.\r
+        */\r
+       {\r
+           struct ssh2_userkey *key;\r
+           struct blob b;\r
+           unsigned char *data, *signature;\r
+           int datalen, siglen, len;\r
+\r
+           if (msgend < p+4)\r
+               goto failure;\r
+           b.len = GET_32BIT(p);\r
+           p += 4;\r
+           if (msgend < p+b.len)\r
+               goto failure;\r
+           b.blob = p;\r
+           p += b.len;\r
+           if (msgend < p+4)\r
+               goto failure;\r
+           datalen = GET_32BIT(p);\r
+           p += 4;\r
+           if (msgend < p+datalen)\r
+               goto failure;\r
+           data = p;\r
+           key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);\r
+           if (!key)\r
+               goto failure;\r
+           signature = key->alg->sign(key->data, data, datalen, &siglen);\r
+           len = 5 + 4 + siglen;\r
+           PUT_32BIT(ret, len - 4);\r
+           ret[4] = SSH2_AGENT_SIGN_RESPONSE;\r
+           PUT_32BIT(ret + 5, siglen);\r
+           memcpy(ret + 5 + 4, signature, siglen);\r
+           sfree(signature);\r
+       }\r
+       break;\r
+      case SSH1_AGENTC_ADD_RSA_IDENTITY:\r
+       /*\r
+        * Add to the list and return SSH_AGENT_SUCCESS, or\r
+        * SSH_AGENT_FAILURE if the key was malformed.\r
+        */\r
+       {\r
+           struct RSAKey *key;\r
+           char *comment;\r
+            int n, commentlen;\r
+\r
+           key = snew(struct RSAKey);\r
+           memset(key, 0, sizeof(struct RSAKey));\r
+\r
+           n = makekey(p, msgend - p, key, NULL, 1);\r
+           if (n < 0) {\r
+               freersakey(key);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+           p += n;\r
+\r
+           n = makeprivate(p, msgend - p, key);\r
+           if (n < 0) {\r
+               freersakey(key);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+           p += n;\r
+\r
+           n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */\r
+           if (n < 0) {\r
+               freersakey(key);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+           p += n;\r
+\r
+           n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */\r
+           if (n < 0) {\r
+               freersakey(key);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+           p += n;\r
+\r
+           n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */\r
+           if (n < 0) {\r
+               freersakey(key);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+           p += n;\r
+\r
+           if (msgend < p+4) {\r
+               freersakey(key);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+            commentlen = GET_32BIT(p);\r
+\r
+           if (msgend < p+commentlen) {\r
+               freersakey(key);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+\r
+           comment = snewn(commentlen+1, char);\r
+           if (comment) {\r
+               memcpy(comment, p + 4, commentlen);\r
+                comment[commentlen] = '\0';\r
+               key->comment = comment;\r
+           }\r
+           PUT_32BIT(ret, 1);\r
+           ret[4] = SSH_AGENT_FAILURE;\r
+           if (add234(rsakeys, key) == key) {\r
+               keylist_update();\r
+               ret[4] = SSH_AGENT_SUCCESS;\r
+           } else {\r
+               freersakey(key);\r
+               sfree(key);\r
+           }\r
+       }\r
+       break;\r
+      case SSH2_AGENTC_ADD_IDENTITY:\r
+       /*\r
+        * Add to the list and return SSH_AGENT_SUCCESS, or\r
+        * SSH_AGENT_FAILURE if the key was malformed.\r
+        */\r
+       {\r
+           struct ssh2_userkey *key;\r
+           char *comment, *alg;\r
+           int alglen, commlen;\r
+           int bloblen;\r
+\r
+\r
+           if (msgend < p+4)\r
+               goto failure;\r
+           alglen = GET_32BIT(p);\r
+           p += 4;\r
+           if (msgend < p+alglen)\r
+               goto failure;\r
+           alg = p;\r
+           p += alglen;\r
+\r
+           key = snew(struct ssh2_userkey);\r
+           /* Add further algorithm names here. */\r
+           if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))\r
+               key->alg = &ssh_rsa;\r
+           else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))\r
+               key->alg = &ssh_dss;\r
+           else {\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+\r
+           bloblen = msgend - p;\r
+           key->data = key->alg->openssh_createkey(&p, &bloblen);\r
+           if (!key->data) {\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+\r
+           /*\r
+            * p has been advanced by openssh_createkey, but\r
+            * certainly not _beyond_ the end of the buffer.\r
+            */\r
+           assert(p <= msgend);\r
+\r
+           if (msgend < p+4) {\r
+               key->alg->freekey(key->data);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+           commlen = GET_32BIT(p);\r
+           p += 4;\r
+\r
+           if (msgend < p+commlen) {\r
+               key->alg->freekey(key->data);\r
+               sfree(key);\r
+               goto failure;\r
+           }\r
+           comment = snewn(commlen + 1, char);\r
+           if (comment) {\r
+               memcpy(comment, p, commlen);\r
+               comment[commlen] = '\0';\r
+           }\r
+           key->comment = comment;\r
+\r
+           PUT_32BIT(ret, 1);\r
+           ret[4] = SSH_AGENT_FAILURE;\r
+           if (add234(ssh2keys, key) == key) {\r
+               keylist_update();\r
+               ret[4] = SSH_AGENT_SUCCESS;\r
+           } else {\r
+               key->alg->freekey(key->data);\r
+               sfree(key->comment);\r
+               sfree(key);\r
+           }\r
+       }\r
+       break;\r
+      case SSH1_AGENTC_REMOVE_RSA_IDENTITY:\r
+       /*\r
+        * Remove from the list and return SSH_AGENT_SUCCESS, or\r
+        * perhaps SSH_AGENT_FAILURE if it wasn't in the list to\r
+        * start with.\r
+        */\r
+       {\r
+           struct RSAKey reqkey, *key;\r
+           int n;\r
+\r
+           n = makekey(p, msgend - p, &reqkey, NULL, 0);\r
+           if (n < 0)\r
+               goto failure;\r
+\r
+           key = find234(rsakeys, &reqkey, NULL);\r
+           freebn(reqkey.exponent);\r
+           freebn(reqkey.modulus);\r
+           PUT_32BIT(ret, 1);\r
+           ret[4] = SSH_AGENT_FAILURE;\r
+           if (key) {\r
+               del234(rsakeys, key);\r
+               keylist_update();\r
+               freersakey(key);\r
+               sfree(key);\r
+               ret[4] = SSH_AGENT_SUCCESS;\r
+           }\r
+       }\r
+       break;\r
+      case SSH2_AGENTC_REMOVE_IDENTITY:\r
+       /*\r
+        * Remove from the list and return SSH_AGENT_SUCCESS, or\r
+        * perhaps SSH_AGENT_FAILURE if it wasn't in the list to\r
+        * start with.\r
+        */\r
+       {\r
+           struct ssh2_userkey *key;\r
+           struct blob b;\r
+\r
+           if (msgend < p+4)\r
+               goto failure;\r
+           b.len = GET_32BIT(p);\r
+           p += 4;\r
+\r
+           if (msgend < p+b.len)\r
+               goto failure;\r
+           b.blob = p;\r
+           p += b.len;\r
+\r
+           key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);\r
+           if (!key)\r
+               goto failure;\r
+\r
+           PUT_32BIT(ret, 1);\r
+           ret[4] = SSH_AGENT_FAILURE;\r
+           if (key) {\r
+               del234(ssh2keys, key);\r
+               keylist_update();\r
+               key->alg->freekey(key->data);\r
+               sfree(key);\r
+               ret[4] = SSH_AGENT_SUCCESS;\r
+           }\r
+       }\r
+       break;\r
+      case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:\r
+       /*\r
+        * Remove all SSH-1 keys. Always returns success.\r
+        */\r
+       {\r
+           struct RSAKey *rkey;\r
+\r
+           while ((rkey = index234(rsakeys, 0)) != NULL) {\r
+               del234(rsakeys, rkey);\r
+               freersakey(rkey);\r
+               sfree(rkey);\r
+           }\r
+           keylist_update();\r
+\r
+           PUT_32BIT(ret, 1);\r
+           ret[4] = SSH_AGENT_SUCCESS;\r
+       }\r
+       break;\r
+      case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:\r
+       /*\r
+        * Remove all SSH-2 keys. Always returns success.\r
+        */\r
+       {\r
+           struct ssh2_userkey *skey;\r
+\r
+           while ((skey = index234(ssh2keys, 0)) != NULL) {\r
+               del234(ssh2keys, skey);\r
+               skey->alg->freekey(skey->data);\r
+               sfree(skey);\r
+           }\r
+           keylist_update();\r
+\r
+           PUT_32BIT(ret, 1);\r
+           ret[4] = SSH_AGENT_SUCCESS;\r
+       }\r
+       break;\r
+      default:\r
+      failure:\r
+       /*\r
+        * Unrecognised message. Return SSH_AGENT_FAILURE.\r
+        */\r
+       PUT_32BIT(ret, 1);\r
+       ret[4] = SSH_AGENT_FAILURE;\r
+       break;\r
+    }\r
+}\r
+\r
+/*\r
+ * Key comparison function for the 2-3-4 tree of RSA keys.\r
+ */\r
+static int cmpkeys_rsa(void *av, void *bv)\r
+{\r
+    struct RSAKey *a = (struct RSAKey *) av;\r
+    struct RSAKey *b = (struct RSAKey *) bv;\r
+    Bignum am, bm;\r
+    int alen, blen;\r
+\r
+    am = a->modulus;\r
+    bm = b->modulus;\r
+    /*\r
+     * Compare by length of moduli.\r
+     */\r
+    alen = bignum_bitcount(am);\r
+    blen = bignum_bitcount(bm);\r
+    if (alen > blen)\r
+       return +1;\r
+    else if (alen < blen)\r
+       return -1;\r
+    /*\r
+     * Now compare by moduli themselves.\r
+     */\r
+    alen = (alen + 7) / 8;            /* byte count */\r
+    while (alen-- > 0) {\r
+       int abyte, bbyte;\r
+       abyte = bignum_byte(am, alen);\r
+       bbyte = bignum_byte(bm, alen);\r
+       if (abyte > bbyte)\r
+           return +1;\r
+       else if (abyte < bbyte)\r
+           return -1;\r
+    }\r
+    /*\r
+     * Give up.\r
+     */\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Key comparison function for the 2-3-4 tree of SSH-2 keys.\r
+ */\r
+static int cmpkeys_ssh2(void *av, void *bv)\r
+{\r
+    struct ssh2_userkey *a = (struct ssh2_userkey *) av;\r
+    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;\r
+    int i;\r
+    int alen, blen;\r
+    unsigned char *ablob, *bblob;\r
+    int c;\r
+\r
+    /*\r
+     * Compare purely by public blob.\r
+     */\r
+    ablob = a->alg->public_blob(a->data, &alen);\r
+    bblob = b->alg->public_blob(b->data, &blen);\r
+\r
+    c = 0;\r
+    for (i = 0; i < alen && i < blen; i++) {\r
+       if (ablob[i] < bblob[i]) {\r
+           c = -1;\r
+           break;\r
+       } else if (ablob[i] > bblob[i]) {\r
+           c = +1;\r
+           break;\r
+       }\r
+    }\r
+    if (c == 0 && i < alen)\r
+       c = +1;                        /* a is longer */\r
+    if (c == 0 && i < blen)\r
+       c = -1;                        /* a is longer */\r
+\r
+    sfree(ablob);\r
+    sfree(bblob);\r
+\r
+    return c;\r
+}\r
+\r
+/*\r
+ * Key comparison function for looking up a blob in the 2-3-4 tree\r
+ * of SSH-2 keys.\r
+ */\r
+static int cmpkeys_ssh2_asymm(void *av, void *bv)\r
+{\r
+    struct blob *a = (struct blob *) av;\r
+    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;\r
+    int i;\r
+    int alen, blen;\r
+    unsigned char *ablob, *bblob;\r
+    int c;\r
+\r
+    /*\r
+     * Compare purely by public blob.\r
+     */\r
+    ablob = a->blob;\r
+    alen = a->len;\r
+    bblob = b->alg->public_blob(b->data, &blen);\r
+\r
+    c = 0;\r
+    for (i = 0; i < alen && i < blen; i++) {\r
+       if (ablob[i] < bblob[i]) {\r
+           c = -1;\r
+           break;\r
+       } else if (ablob[i] > bblob[i]) {\r
+           c = +1;\r
+           break;\r
+       }\r
+    }\r
+    if (c == 0 && i < alen)\r
+       c = +1;                        /* a is longer */\r
+    if (c == 0 && i < blen)\r
+       c = -1;                        /* a is longer */\r
+\r
+    sfree(bblob);\r
+\r
+    return c;\r
+}\r
+\r
+/*\r
+ * Prompt for a key file to add, and add it.\r
+ */\r
+static void prompt_add_keyfile(void)\r
+{\r
+    OPENFILENAME of;\r
+    char *filelist = snewn(8192, char);\r
+       \r
+    if (!keypath) keypath = filereq_new();\r
+    memset(&of, 0, sizeof(of));\r
+    of.hwndOwner = hwnd;\r
+    of.lpstrFilter = FILTER_KEY_FILES;\r
+    of.lpstrCustomFilter = NULL;\r
+    of.nFilterIndex = 1;\r
+    of.lpstrFile = filelist;\r
+    *filelist = '\0';\r
+    of.nMaxFile = 8192;\r
+    of.lpstrFileTitle = NULL;\r
+    of.lpstrTitle = "Select Private Key File";\r
+    of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;\r
+    if (request_file(keypath, &of, TRUE, FALSE)) {\r
+       if(strlen(filelist) > of.nFileOffset)\r
+           /* Only one filename returned? */\r
+           add_keyfile(filename_from_str(filelist));\r
+       else {\r
+           /* we are returned a bunch of strings, end to\r
+            * end. first string is the directory, the\r
+            * rest the filenames. terminated with an\r
+            * empty string.\r
+            */\r
+           char *dir = filelist;\r
+           char *filewalker = filelist + strlen(dir) + 1;\r
+           while (*filewalker != '\0') {\r
+               char *filename = dupcat(dir, "\\", filewalker, NULL);\r
+               add_keyfile(filename_from_str(filename));\r
+               sfree(filename);\r
+               filewalker += strlen(filewalker) + 1;\r
+           }\r
+       }\r
+\r
+       keylist_update();\r
+       forget_passphrases();\r
+    }\r
+    sfree(filelist);\r
+}\r
+\r
+/*\r
+ * Dialog-box function for the key list box.\r
+ */\r
+static int CALLBACK KeyListProc(HWND hwnd, UINT msg,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    struct RSAKey *rkey;\r
+    struct ssh2_userkey *skey;\r
+\r
+    switch (msg) {\r
+      case WM_INITDIALOG:\r
+       /*\r
+        * Centre the window.\r
+        */\r
+       {                              /* centre the window */\r
+           RECT rs, rd;\r
+           HWND hw;\r
+\r
+           hw = GetDesktopWindow();\r
+           if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
+               MoveWindow(hwnd,\r
+                          (rs.right + rs.left + rd.left - rd.right) / 2,\r
+                          (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
+                          rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
+       }\r
+\r
+        if (has_help())\r
+            SetWindowLongPtr(hwnd, GWL_EXSTYLE,\r
+                            GetWindowLongPtr(hwnd, GWL_EXSTYLE) |\r
+                            WS_EX_CONTEXTHELP);\r
+        else {\r
+            HWND item = GetDlgItem(hwnd, 103);   /* the Help button */\r
+            if (item)\r
+                DestroyWindow(item);\r
+        }\r
+\r
+       keylist = hwnd;\r
+       {\r
+           static int tabs[] = { 35, 60, 210 };\r
+           SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,\r
+                              sizeof(tabs) / sizeof(*tabs),\r
+                              (LPARAM) tabs);\r
+       }\r
+       keylist_update();\r
+       return 0;\r
+      case WM_COMMAND:\r
+       switch (LOWORD(wParam)) {\r
+         case IDOK:\r
+         case IDCANCEL:\r
+           keylist = NULL;\r
+           DestroyWindow(hwnd);\r
+           return 0;\r
+         case 101:                    /* add key */\r
+           if (HIWORD(wParam) == BN_CLICKED ||\r
+               HIWORD(wParam) == BN_DOUBLECLICKED) {\r
+               if (passphrase_box) {\r
+                   MessageBeep(MB_ICONERROR);\r
+                   SetForegroundWindow(passphrase_box);\r
+                   break;\r
+               }\r
+               prompt_add_keyfile();\r
+           }\r
+           return 0;\r
+         case 102:                    /* remove key */\r
+           if (HIWORD(wParam) == BN_CLICKED ||\r
+               HIWORD(wParam) == BN_DOUBLECLICKED) {\r
+               int i;\r
+               int rCount, sCount;\r
+               int *selectedArray;\r
+               \r
+               /* our counter within the array of selected items */\r
+               int itemNum;\r
+               \r
+               /* get the number of items selected in the list */\r
+               int numSelected = \r
+                       SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);\r
+               \r
+               /* none selected? that was silly */\r
+               if (numSelected == 0) {\r
+                   MessageBeep(0);\r
+                   break;\r
+               }\r
+\r
+               /* get item indices in an array */\r
+               selectedArray = snewn(numSelected, int);\r
+               SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,\r
+                               numSelected, (WPARAM)selectedArray);\r
+               \r
+               itemNum = numSelected - 1;\r
+               rCount = count234(rsakeys);\r
+               sCount = count234(ssh2keys);\r
+               \r
+               /* go through the non-rsakeys until we've covered them all, \r
+                * and/or we're out of selected items to check. note that\r
+                * we go *backwards*, to avoid complications from deleting\r
+                * things hence altering the offset of subsequent items\r
+                */\r
+           for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {\r
+                       skey = index234(ssh2keys, i);\r
+                       \r
+                       if (selectedArray[itemNum] == rCount + i) {\r
+                               del234(ssh2keys, skey);\r
+                               skey->alg->freekey(skey->data);\r
+                               sfree(skey);\r
+                               itemNum--; \r
+                       }\r
+               }\r
+               \r
+               /* do the same for the rsa keys */\r
+               for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {\r
+                       rkey = index234(rsakeys, i);\r
+\r
+                       if(selectedArray[itemNum] == i) {\r
+                               del234(rsakeys, rkey);\r
+                               freersakey(rkey);\r
+                               sfree(rkey);\r
+                               itemNum--;\r
+                       }\r
+               }\r
+\r
+               sfree(selectedArray); \r
+               keylist_update();\r
+           }\r
+           return 0;\r
+         case 103:                    /* help */\r
+            if (HIWORD(wParam) == BN_CLICKED ||\r
+                HIWORD(wParam) == BN_DOUBLECLICKED) {\r
+               launch_help(hwnd, WINHELP_CTX_pageant_general);\r
+            }\r
+           return 0;\r
+       }\r
+       return 0;\r
+      case WM_HELP:\r
+        {\r
+            int id = ((LPHELPINFO)lParam)->iCtrlId;\r
+            char *topic = NULL;\r
+            switch (id) {\r
+              case 100: topic = WINHELP_CTX_pageant_keylist; break;\r
+              case 101: topic = WINHELP_CTX_pageant_addkey; break;\r
+              case 102: topic = WINHELP_CTX_pageant_remkey; break;\r
+            }\r
+            if (topic) {\r
+               launch_help(hwnd, topic);\r
+            } else {\r
+                MessageBeep(0);\r
+            }\r
+        }\r
+        break;\r
+      case WM_CLOSE:\r
+       keylist = NULL;\r
+       DestroyWindow(hwnd);\r
+       return 0;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/* Set up a system tray icon */\r
+static BOOL AddTrayIcon(HWND hwnd)\r
+{\r
+    BOOL res;\r
+    NOTIFYICONDATA tnid;\r
+    HICON hicon;\r
+\r
+#ifdef NIM_SETVERSION\r
+    tnid.uVersion = 0;\r
+    res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);\r
+#endif\r
+\r
+    tnid.cbSize = sizeof(NOTIFYICONDATA);\r
+    tnid.hWnd = hwnd;\r
+    tnid.uID = 1;             /* unique within this systray use */\r
+    tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;\r
+    tnid.uCallbackMessage = WM_SYSTRAY;\r
+    tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));\r
+    strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");\r
+\r
+    res = Shell_NotifyIcon(NIM_ADD, &tnid);\r
+\r
+    if (hicon) DestroyIcon(hicon);\r
+    \r
+    return res;\r
+}\r
+\r
+/* Update the saved-sessions menu. */\r
+static void update_sessions(void)\r
+{\r
+    int num_entries;\r
+    HKEY hkey;\r
+    TCHAR buf[MAX_PATH + 1];\r
+    MENUITEMINFO mii;\r
+\r
+    int index_key, index_menu;\r
+\r
+    if (!putty_path)\r
+       return;\r
+\r
+    if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))\r
+       return;\r
+\r
+    for(num_entries = GetMenuItemCount(session_menu);\r
+       num_entries > initial_menuitems_count;\r
+       num_entries--)\r
+       RemoveMenu(session_menu, 0, MF_BYPOSITION);\r
+\r
+    index_key = 0;\r
+    index_menu = 0;\r
+\r
+    while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {\r
+       TCHAR session_name[MAX_PATH + 1];\r
+       unmungestr(buf, session_name, MAX_PATH);\r
+       if(strcmp(buf, PUTTY_DEFAULT) != 0) {\r
+           memset(&mii, 0, sizeof(mii));\r
+           mii.cbSize = sizeof(mii);\r
+           mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;\r
+           mii.fType = MFT_STRING;\r
+           mii.fState = MFS_ENABLED;\r
+           mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;\r
+           mii.dwTypeData = session_name;\r
+           InsertMenuItem(session_menu, index_menu, TRUE, &mii);\r
+           index_menu++;\r
+       }\r
+       index_key++;\r
+    }\r
+\r
+    RegCloseKey(hkey);\r
+\r
+    if(index_menu == 0) {\r
+       mii.cbSize = sizeof(mii);\r
+       mii.fMask = MIIM_TYPE | MIIM_STATE;\r
+       mii.fType = MFT_STRING;\r
+       mii.fState = MFS_GRAYED;\r
+       mii.dwTypeData = _T("(No sessions)");\r
+       InsertMenuItem(session_menu, index_menu, TRUE, &mii);\r
+    }\r
+}\r
+\r
+static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,\r
+                               WPARAM wParam, LPARAM lParam)\r
+{\r
+    int ret;\r
+    static int menuinprogress;\r
+    static UINT msgTaskbarCreated = 0;\r
+\r
+    switch (message) {\r
+      case WM_CREATE:\r
+        msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));\r
+        break;\r
+      default:\r
+        if (message==msgTaskbarCreated) {\r
+            /*\r
+            * Explorer has been restarted, so the tray icon will\r
+            * have been lost.\r
+            */\r
+           AddTrayIcon(hwnd);\r
+        }\r
+        break;\r
+        \r
+      case WM_SYSTRAY:\r
+       if (lParam == WM_RBUTTONUP) {\r
+           POINT cursorpos;\r
+           GetCursorPos(&cursorpos);\r
+           PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);\r
+       } else if (lParam == WM_LBUTTONDBLCLK) {\r
+           /* Run the default menu item. */\r
+           UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);\r
+           if (menuitem != -1)\r
+               PostMessage(hwnd, WM_COMMAND, menuitem, 0);\r
+       }\r
+       break;\r
+      case WM_SYSTRAY2:\r
+       if (!menuinprogress) {\r
+           menuinprogress = 1;\r
+           update_sessions();\r
+           SetForegroundWindow(hwnd);\r
+           ret = TrackPopupMenu(systray_menu,\r
+                                TPM_RIGHTALIGN | TPM_BOTTOMALIGN |\r
+                                TPM_RIGHTBUTTON,\r
+                                wParam, lParam, 0, hwnd, NULL);\r
+           menuinprogress = 0;\r
+       }\r
+       break;\r
+      case WM_COMMAND:\r
+      case WM_SYSCOMMAND:\r
+       switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */\r
+         case IDM_PUTTY:\r
+           if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),\r
+                                SW_SHOW) <= 32) {\r
+               MessageBox(NULL, "Unable to execute PuTTY!",\r
+                          "Error", MB_OK | MB_ICONERROR);\r
+           }\r
+           break;\r
+         case IDM_CLOSE:\r
+           if (passphrase_box)\r
+               SendMessage(passphrase_box, WM_CLOSE, 0, 0);\r
+           SendMessage(hwnd, WM_CLOSE, 0, 0);\r
+           break;\r
+         case IDM_VIEWKEYS:\r
+           if (!keylist) {\r
+               keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),\r
+                                      NULL, KeyListProc);\r
+               ShowWindow(keylist, SW_SHOWNORMAL);\r
+           }\r
+           /* \r
+            * Sometimes the window comes up minimised / hidden for\r
+            * no obvious reason. Prevent this. This also brings it\r
+            * to the front if it's already present (the user\r
+            * selected View Keys because they wanted to _see_ the\r
+            * thing).\r
+            */\r
+           SetForegroundWindow(keylist);\r
+           SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,\r
+                        SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+           break;\r
+         case IDM_ADDKEY:\r
+           if (passphrase_box) {\r
+               MessageBeep(MB_ICONERROR);\r
+               SetForegroundWindow(passphrase_box);\r
+               break;\r
+           }\r
+           prompt_add_keyfile();\r
+           break;\r
+         case IDM_ABOUT:\r
+           if (!aboutbox) {\r
+               aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),\r
+                                       NULL, AboutProc);\r
+               ShowWindow(aboutbox, SW_SHOWNORMAL);\r
+               /* \r
+                * Sometimes the window comes up minimised / hidden\r
+                * for no obvious reason. Prevent this.\r
+                */\r
+               SetForegroundWindow(aboutbox);\r
+               SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,\r
+                            SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);\r
+           }\r
+           break;\r
+         case IDM_HELP:\r
+           launch_help(hwnd, WINHELP_CTX_pageant_general);\r
+           break;\r
+         default:\r
+           {\r
+               if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {\r
+                   MENUITEMINFO mii;\r
+                   TCHAR buf[MAX_PATH + 1];\r
+                   TCHAR param[MAX_PATH + 1];\r
+                   memset(&mii, 0, sizeof(mii));\r
+                   mii.cbSize = sizeof(mii);\r
+                   mii.fMask = MIIM_TYPE;\r
+                   mii.cch = MAX_PATH;\r
+                   mii.dwTypeData = buf;\r
+                   GetMenuItemInfo(session_menu, wParam, FALSE, &mii);\r
+                   strcpy(param, "@");\r
+                   strcat(param, mii.dwTypeData);\r
+                   if((int)ShellExecute(hwnd, NULL, putty_path, param,\r
+                                        _T(""), SW_SHOW) <= 32) {\r
+                       MessageBox(NULL, "Unable to execute PuTTY!", "Error",\r
+                                  MB_OK | MB_ICONERROR);\r
+                   }\r
+               }\r
+           }\r
+           break;\r
+       }\r
+       break;\r
+      case WM_DESTROY:\r
+       quit_help(hwnd);\r
+       PostQuitMessage(0);\r
+       return 0;\r
+      case WM_COPYDATA:\r
+       {\r
+           COPYDATASTRUCT *cds;\r
+           char *mapname;\r
+           void *p;\r
+           HANDLE filemap;\r
+#ifndef NO_SECURITY\r
+           PSID mapowner, ourself;\r
+           PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;\r
+#endif\r
+           int ret = 0;\r
+\r
+           cds = (COPYDATASTRUCT *) lParam;\r
+           if (cds->dwData != AGENT_COPYDATA_ID)\r
+               return 0;              /* not our message, mate */\r
+           mapname = (char *) cds->lpData;\r
+           if (mapname[cds->cbData - 1] != '\0')\r
+               return 0;              /* failure to be ASCIZ! */\r
+#ifdef DEBUG_IPC\r
+           debug(("mapname is :%s:\n", mapname));\r
+#endif\r
+           filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);\r
+#ifdef DEBUG_IPC\r
+           debug(("filemap is %p\n", filemap));\r
+#endif\r
+           if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {\r
+#ifndef NO_SECURITY\r
+               int rc;\r
+               if (has_security) {\r
+                    if ((ourself = get_user_sid()) == NULL) {\r
+#ifdef DEBUG_IPC\r
+                       debug(("couldn't get user SID\n"));\r
+#endif\r
+                       return 0;\r
+                    }\r
+\r
+                   if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,\r
+                                               OWNER_SECURITY_INFORMATION,\r
+                                               &mapowner, NULL, NULL, NULL,\r
+                                               &psd1) != ERROR_SUCCESS)) {\r
+#ifdef DEBUG_IPC\r
+                       debug(("couldn't get owner info for filemap: %d\n",\r
+                               rc));\r
+#endif\r
+                       return 0;\r
+                   }\r
+#ifdef DEBUG_IPC\r
+                    {\r
+                        LPTSTR ours, theirs;\r
+                        ConvertSidToStringSid(mapowner, &theirs);\r
+                        ConvertSidToStringSid(ourself, &ours);\r
+                        debug(("got both sids: ours=%s theirs=%s\n",\r
+                               ours, theirs));\r
+                        LocalFree(ours);\r
+                        LocalFree(theirs);\r
+                    }\r
+#endif\r
+                   if (!EqualSid(mapowner, ourself))\r
+                       return 0;      /* security ID mismatch! */\r
+#ifdef DEBUG_IPC\r
+                   debug(("security stuff matched\n"));\r
+#endif\r
+                   LocalFree(psd1);\r
+                   LocalFree(psd2);\r
+               } else {\r
+#ifdef DEBUG_IPC\r
+                   debug(("security APIs not present\n"));\r
+#endif\r
+               }\r
+#endif\r
+               p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);\r
+#ifdef DEBUG_IPC\r
+               debug(("p is %p\n", p));\r
+               {\r
+                   int i;\r
+                   for (i = 0; i < 5; i++)\r
+                       debug(("p[%d]=%02x\n", i,\r
+                              ((unsigned char *) p)[i]));\r
+                }\r
+#endif\r
+               answer_msg(p);\r
+               ret = 1;\r
+               UnmapViewOfFile(p);\r
+           }\r
+           CloseHandle(filemap);\r
+           return ret;\r
+       }\r
+    }\r
+\r
+    return DefWindowProc(hwnd, message, wParam, lParam);\r
+}\r
+\r
+/*\r
+ * Fork and Exec the command in cmdline. [DBW]\r
+ */\r
+void spawn_cmd(char *cmdline, char * args, int show)\r
+{\r
+    if (ShellExecute(NULL, _T("open"), cmdline,\r
+                    args, NULL, show) <= (HINSTANCE) 32) {\r
+       char *msg;\r
+       msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,\r
+                       (int)GetLastError());\r
+       MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);\r
+       sfree(msg);\r
+    }\r
+}\r
+\r
+/*\r
+ * This is a can't-happen stub, since Pageant never makes\r
+ * asynchronous agent requests.\r
+ */\r
+void agent_schedule_callback(void (*callback)(void *, void *, int),\r
+                            void *callback_ctx, void *data, int len)\r
+{\r
+    assert(!"We shouldn't get here");\r
+}\r
+\r
+void cleanup_exit(int code)\r
+{\r
+    shutdown_help();\r
+    exit(code);\r
+}\r
+\r
+int flags = FLAG_SYNCAGENT;\r
+\r
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)\r
+{\r
+    WNDCLASS wndclass;\r
+    MSG msg;\r
+    HMODULE advapi;\r
+    char *command = NULL;\r
+    int added_keys = 0;\r
+    int argc, i;\r
+    char **argv, **argstart;\r
+\r
+    hinst = inst;\r
+    hwnd = NULL;\r
+\r
+    /*\r
+     * Determine whether we're an NT system (should have security\r
+     * APIs) or a non-NT system (don't do security).\r
+     */\r
+    if (!init_winver())\r
+    {\r
+       modalfatalbox("Windows refuses to report a version");\r
+    }\r
+    if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {\r
+       has_security = TRUE;\r
+    } else\r
+       has_security = FALSE;\r
+\r
+    if (has_security) {\r
+#ifndef NO_SECURITY\r
+       /*\r
+        * Attempt to get the security API we need.\r
+        */\r
+        if (!init_advapi()) {\r
+           MessageBox(NULL,\r
+                      "Unable to access security APIs. Pageant will\n"\r
+                      "not run, in case it causes a security breach.",\r
+                      "Pageant Fatal Error", MB_ICONERROR | MB_OK);\r
+           return 1;\r
+       }\r
+#else\r
+       MessageBox(NULL,\r
+                  "This program has been compiled for Win9X and will\n"\r
+                  "not run on NT, in case it causes a security breach.",\r
+                  "Pageant Fatal Error", MB_ICONERROR | MB_OK);\r
+       return 1;\r
+#endif\r
+    } else\r
+       advapi = NULL;\r
+\r
+    /*\r
+     * See if we can find our Help file.\r
+     */\r
+    init_help();\r
+\r
+    /*\r
+     * Look for the PuTTY binary (we will enable the saved session\r
+     * submenu if we find it).\r
+     */\r
+    {\r
+        char b[2048], *p, *q, *r;\r
+        FILE *fp;\r
+        GetModuleFileName(NULL, b, sizeof(b) - 16);\r
+        r = b;\r
+        p = strrchr(b, '\\');\r
+        if (p && p >= r) r = p+1;\r
+        q = strrchr(b, ':');\r
+        if (q && q >= r) r = q+1;\r
+        strcpy(r, "putty.exe");\r
+        if ( (fp = fopen(b, "r")) != NULL) {\r
+            putty_path = dupstr(b);\r
+            fclose(fp);\r
+        } else\r
+            putty_path = NULL;\r
+    }\r
+\r
+    /*\r
+     * Find out if Pageant is already running.\r
+     */\r
+    already_running = agent_exists();\r
+\r
+    /*\r
+     * Initialise storage for RSA keys.\r
+     */\r
+    if (!already_running) {\r
+       rsakeys = newtree234(cmpkeys_rsa);\r
+       ssh2keys = newtree234(cmpkeys_ssh2);\r
+    }\r
+\r
+    /*\r
+     * Initialise storage for short-term passphrase cache.\r
+     */\r
+    passphrases = newtree234(NULL);\r
+\r
+    /*\r
+     * Process the command line and add keys as listed on it.\r
+     */\r
+    split_into_argv(cmdline, &argc, &argv, &argstart);\r
+    for (i = 0; i < argc; i++) {\r
+       if (!strcmp(argv[i], "-pgpfp")) {\r
+           pgp_fingerprints();\r
+           if (advapi)\r
+               FreeLibrary(advapi);\r
+           return 1;\r
+       } else if (!strcmp(argv[i], "-c")) {\r
+           /*\r
+            * If we see `-c', then the rest of the\r
+            * command line should be treated as a\r
+            * command to be spawned.\r
+            */\r
+           if (i < argc-1)\r
+               command = argstart[i+1];\r
+           else\r
+               command = "";\r
+           break;\r
+       } else {\r
+           add_keyfile(filename_from_str(argv[i]));\r
+           added_keys = TRUE;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Forget any passphrase that we retained while going over\r
+     * command line keyfiles.\r
+     */\r
+    forget_passphrases();\r
+\r
+    if (command) {\r
+       char *args;\r
+       if (command[0] == '"')\r
+           args = strchr(++command, '"');\r
+       else\r
+           args = strchr(command, ' ');\r
+       if (args) {\r
+           *args++ = 0;\r
+           while(*args && isspace(*args)) args++;\r
+       }\r
+       spawn_cmd(command, args, show);\r
+    }\r
+\r
+    /*\r
+     * If Pageant was already running, we leave now. If we haven't\r
+     * even taken any auxiliary action (spawned a command or added\r
+     * keys), complain.\r
+     */\r
+    if (already_running) {\r
+       if (!command && !added_keys) {\r
+           MessageBox(NULL, "Pageant is already running", "Pageant Error",\r
+                      MB_ICONERROR | MB_OK);\r
+       }\r
+       if (advapi)\r
+           FreeLibrary(advapi);\r
+       return 0;\r
+    }\r
+\r
+    if (!prev) {\r
+       wndclass.style = 0;\r
+       wndclass.lpfnWndProc = WndProc;\r
+       wndclass.cbClsExtra = 0;\r
+       wndclass.cbWndExtra = 0;\r
+       wndclass.hInstance = inst;\r
+       wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));\r
+       wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);\r
+       wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);\r
+       wndclass.lpszMenuName = NULL;\r
+       wndclass.lpszClassName = APPNAME;\r
+\r
+       RegisterClass(&wndclass);\r
+    }\r
+\r
+    keylist = NULL;\r
+\r
+    hwnd = CreateWindow(APPNAME, APPNAME,\r
+                       WS_OVERLAPPEDWINDOW | WS_VSCROLL,\r
+                       CW_USEDEFAULT, CW_USEDEFAULT,\r
+                       100, 100, NULL, NULL, inst, NULL);\r
+\r
+    /* Set up a system tray icon */\r
+    AddTrayIcon(hwnd);\r
+\r
+    /* Accelerators used: nsvkxa */\r
+    systray_menu = CreatePopupMenu();\r
+    if (putty_path) {\r
+       session_menu = CreateMenu();\r
+       AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");\r
+       AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,\r
+                  (UINT) session_menu, "&Saved Sessions");\r
+       AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);\r
+    }\r
+    AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,\r
+          "&View Keys");\r
+    AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");\r
+    AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);\r
+    if (has_help())\r
+       AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");\r
+    AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");\r
+    AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);\r
+    AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");\r
+    initial_menuitems_count = GetMenuItemCount(session_menu);\r
+\r
+    /* Set the default menu item. */\r
+    SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);\r
+\r
+    ShowWindow(hwnd, SW_HIDE);\r
+\r
+    /*\r
+     * Main message loop.\r
+     */\r
+    while (GetMessage(&msg, NULL, 0, 0) == 1) {\r
+       if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&\r
+           !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {\r
+           TranslateMessage(&msg);\r
+           DispatchMessage(&msg);\r
+       }\r
+    }\r
+\r
+    /* Clean up the system tray icon */\r
+    {\r
+       NOTIFYICONDATA tnid;\r
+\r
+       tnid.cbSize = sizeof(NOTIFYICONDATA);\r
+       tnid.hWnd = hwnd;\r
+       tnid.uID = 1;\r
+\r
+       Shell_NotifyIcon(NIM_DELETE, &tnid);\r
+\r
+       DestroyMenu(systray_menu);\r
+    }\r
+\r
+    if (keypath) filereq_free(keypath);\r
+\r
+    if (advapi)\r
+       FreeLibrary(advapi);\r
+\r
+    cleanup_exit(msg.wParam);\r
+    return msg.wParam;                /* just in case optimiser complains */\r
+}\r
diff --git a/putty/WINDOWS/WINPGNTC.C b/putty/WINDOWS/WINPGNTC.C
new file mode 100644 (file)
index 0000000..0dabe71
--- /dev/null
@@ -0,0 +1,265 @@
+/*\r
+ * Pageant client code.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+\r
+#ifndef NO_SECURITY\r
+#include <aclapi.h>\r
+#endif\r
+\r
+#define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */\r
+#define AGENT_MAX_MSGLEN  8192\r
+\r
+int agent_exists(void)\r
+{\r
+    HWND hwnd;\r
+    hwnd = FindWindow("Pageant", "Pageant");\r
+    if (!hwnd)\r
+       return FALSE;\r
+    else\r
+       return TRUE;\r
+}\r
+\r
+/*\r
+ * Unfortunately, this asynchronous agent request mechanism doesn't\r
+ * appear to work terribly well. I'm going to comment it out for\r
+ * the moment, and see if I can come up with a better one :-/\r
+ */\r
+#ifdef WINDOWS_ASYNC_AGENT\r
+\r
+struct agent_query_data {\r
+    COPYDATASTRUCT cds;\r
+    unsigned char *mapping;\r
+    HANDLE handle;\r
+    char *mapname;\r
+    HWND hwnd;\r
+    void (*callback)(void *, void *, int);\r
+    void *callback_ctx;\r
+};\r
+\r
+DWORD WINAPI agent_query_thread(LPVOID param)\r
+{\r
+    struct agent_query_data *data = (struct agent_query_data *)param;\r
+    unsigned char *ret;\r
+    int id, retlen;\r
+\r
+    id = SendMessage(data->hwnd, WM_COPYDATA, (WPARAM) NULL,\r
+                    (LPARAM) &data->cds);\r
+    ret = NULL;\r
+    if (id > 0) {\r
+       retlen = 4 + GET_32BIT(data->mapping);\r
+       ret = snewn(retlen, unsigned char);\r
+       if (ret) {\r
+           memcpy(ret, data->mapping, retlen);\r
+       }\r
+    }\r
+    if (!ret)\r
+       retlen = 0;\r
+    UnmapViewOfFile(data->mapping);\r
+    CloseHandle(data->handle);\r
+    sfree(data->mapname);\r
+\r
+    agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen);\r
+\r
+    return 0;\r
+}\r
+\r
+#endif\r
+\r
+/*\r
+ * Dynamically load advapi32.dll for SID manipulation. In its absence,\r
+ * we degrade gracefully.\r
+ */\r
+#ifndef NO_SECURITY\r
+int advapi_initialised = FALSE;\r
+static HMODULE advapi;\r
+DECL_WINDOWS_FUNCTION(static, BOOL, OpenProcessToken,\r
+                     (HANDLE, DWORD, PHANDLE));\r
+DECL_WINDOWS_FUNCTION(static, BOOL, GetTokenInformation,\r
+                     (HANDLE, TOKEN_INFORMATION_CLASS,\r
+                       LPVOID, DWORD, PDWORD));\r
+DECL_WINDOWS_FUNCTION(static, BOOL, InitializeSecurityDescriptor,\r
+                     (PSECURITY_DESCRIPTOR, DWORD));\r
+DECL_WINDOWS_FUNCTION(static, BOOL, SetSecurityDescriptorOwner,\r
+                     (PSECURITY_DESCRIPTOR, PSID, BOOL));\r
+DECL_WINDOWS_FUNCTION(, DWORD, GetSecurityInfo,\r
+                     (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,\r
+                      PSID *, PSID *, PACL *, PACL *,\r
+                      PSECURITY_DESCRIPTOR *));\r
+int init_advapi(void)\r
+{\r
+    advapi = load_system32_dll("advapi32.dll");\r
+    return advapi &&\r
+       GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&\r
+        GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&\r
+       GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&\r
+       GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&\r
+       GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner);\r
+}\r
+\r
+PSID get_user_sid(void)\r
+{\r
+    HANDLE proc = NULL, tok = NULL;\r
+    TOKEN_USER *user = NULL;\r
+    DWORD toklen, sidlen;\r
+    PSID sid = NULL, ret = NULL;\r
+\r
+    if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,\r
+                            GetCurrentProcessId())) == NULL)\r
+        goto cleanup;\r
+\r
+    if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))\r
+        goto cleanup;\r
+\r
+    if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&\r
+        GetLastError() != ERROR_INSUFFICIENT_BUFFER)\r
+        goto cleanup;\r
+\r
+    if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)\r
+        goto cleanup;\r
+\r
+    if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))\r
+        goto cleanup;\r
+\r
+    sidlen = GetLengthSid(user->User.Sid);\r
+\r
+    sid = (PSID)smalloc(sidlen);\r
+\r
+    if (!CopySid(sidlen, sid, user->User.Sid))\r
+        goto cleanup;\r
+\r
+    /* Success. Move sid into the return value slot, and null it out\r
+     * to stop the cleanup code freeing it. */\r
+    ret = sid;\r
+    sid = NULL;\r
+\r
+  cleanup:\r
+    if (proc != NULL)\r
+        CloseHandle(proc);\r
+    if (tok != NULL)\r
+        CloseHandle(tok);\r
+    if (user != NULL)\r
+        LocalFree(user);\r
+    if (sid != NULL)\r
+        sfree(sid);\r
+\r
+    return ret;\r
+}\r
+\r
+#endif\r
+\r
+int agent_query(void *in, int inlen, void **out, int *outlen,\r
+               void (*callback)(void *, void *, int), void *callback_ctx)\r
+{\r
+    HWND hwnd;\r
+    char *mapname;\r
+    HANDLE filemap;\r
+    unsigned char *p, *ret;\r
+    int id, retlen;\r
+    COPYDATASTRUCT cds;\r
+    SECURITY_ATTRIBUTES sa, *psa;\r
+    PSECURITY_DESCRIPTOR psd = NULL;\r
+    PSID usersid = NULL;\r
+\r
+    *out = NULL;\r
+    *outlen = 0;\r
+\r
+    hwnd = FindWindow("Pageant", "Pageant");\r
+    if (!hwnd)\r
+       return 1;                      /* *out == NULL, so failure */\r
+    mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());\r
+\r
+#ifndef NO_SECURITY\r
+    if (advapi_initialised || init_advapi()) {\r
+        /*\r
+         * Make the file mapping we create for communication with\r
+         * Pageant owned by the user SID rather than the default. This\r
+         * should make communication between processes with slightly\r
+         * different contexts more reliable: in particular, command\r
+         * prompts launched as administrator should still be able to\r
+         * run PSFTPs which refer back to the owning user's\r
+         * unprivileged Pageant.\r
+         */\r
+        usersid = get_user_sid();\r
+\r
+        psa = NULL;\r
+        if (usersid) {\r
+            psd = (PSECURITY_DESCRIPTOR)\r
+                LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);\r
+            if (psd) {\r
+                if (p_InitializeSecurityDescriptor\r
+                    (psd, SECURITY_DESCRIPTOR_REVISION) &&\r
+                    p_SetSecurityDescriptorOwner(psd, usersid, FALSE)) {\r
+                    sa.nLength = sizeof(sa);\r
+                    sa.bInheritHandle = TRUE;\r
+                    sa.lpSecurityDescriptor = psd;\r
+                    psa = &sa;\r
+                } else {\r
+                    LocalFree(psd);\r
+                    psd = NULL;\r
+                }\r
+            }\r
+        }\r
+    }\r
+#endif /* NO_SECURITY */\r
+\r
+    filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,\r
+                               0, AGENT_MAX_MSGLEN, mapname);\r
+    if (filemap == NULL || filemap == INVALID_HANDLE_VALUE)\r
+       return 1;                      /* *out == NULL, so failure */\r
+    p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);\r
+    memcpy(p, in, inlen);\r
+    cds.dwData = AGENT_COPYDATA_ID;\r
+    cds.cbData = 1 + strlen(mapname);\r
+    cds.lpData = mapname;\r
+#ifdef WINDOWS_ASYNC_AGENT\r
+    if (callback != NULL && !(flags & FLAG_SYNCAGENT)) {\r
+       /*\r
+        * We need an asynchronous Pageant request. Since I know of\r
+        * no way to stop SendMessage from blocking the thread it's\r
+        * called in, I see no option but to start a fresh thread.\r
+        * When we're done we'll PostMessage the result back to our\r
+        * main window, so that the callback is done in the primary\r
+        * thread to avoid concurrency.\r
+        */\r
+       struct agent_query_data *data = snew(struct agent_query_data);\r
+       DWORD threadid;\r
+       data->mapping = p;\r
+       data->handle = filemap;\r
+       data->mapname = mapname;\r
+       data->callback = callback;\r
+       data->callback_ctx = callback_ctx;\r
+       data->cds = cds;               /* structure copy */\r
+       data->hwnd = hwnd;\r
+       if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid))\r
+           return 0;\r
+       sfree(data);\r
+    }\r
+#endif\r
+\r
+    /*\r
+     * The user either passed a null callback (indicating that the\r
+     * query is required to be synchronous) or CreateThread failed.\r
+     * Either way, we need a synchronous request.\r
+     */\r
+    id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);\r
+    if (id > 0) {\r
+       retlen = 4 + GET_32BIT(p);\r
+       ret = snewn(retlen, unsigned char);\r
+       if (ret) {\r
+           memcpy(ret, p, retlen);\r
+           *out = ret;\r
+           *outlen = retlen;\r
+       }\r
+    }\r
+    UnmapViewOfFile(p);\r
+    CloseHandle(filemap);\r
+    if (psd)\r
+        LocalFree(psd);\r
+    sfree(usersid);\r
+    return 1;\r
+}\r
diff --git a/putty/WINDOWS/WINPLINK.C b/putty/WINDOWS/WINPLINK.C
new file mode 100644 (file)
index 0000000..7af9e1b
--- /dev/null
@@ -0,0 +1,725 @@
+/*\r
+ * PLink - a Windows command-line (stdin/stdout) variant of PuTTY.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <stdarg.h>\r
+\r
+#define PUTTY_DO_GLOBALS              /* actually _define_ globals */\r
+#include "putty.h"\r
+#include "storage.h"\r
+#include "tree234.h"\r
+\r
+#define WM_AGENT_CALLBACK (WM_APP + 4)\r
+\r
+struct agent_callback {\r
+    void (*callback)(void *, void *, int);\r
+    void *callback_ctx;\r
+    void *data;\r
+    int len;\r
+};\r
+\r
+void fatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    if (logctx) {\r
+        log_free(logctx);\r
+        logctx = NULL;\r
+    }\r
+    cleanup_exit(1);\r
+}\r
+void modalfatalbox(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    if (logctx) {\r
+        log_free(logctx);\r
+        logctx = NULL;\r
+    }\r
+    cleanup_exit(1);\r
+}\r
+void connection_fatal(void *frontend, char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "FATAL ERROR: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    if (logctx) {\r
+        log_free(logctx);\r
+        logctx = NULL;\r
+    }\r
+    cleanup_exit(1);\r
+}\r
+void cmdline_error(char *p, ...)\r
+{\r
+    va_list ap;\r
+    fprintf(stderr, "plink: ");\r
+    va_start(ap, p);\r
+    vfprintf(stderr, p, ap);\r
+    va_end(ap);\r
+    fputc('\n', stderr);\r
+    exit(1);\r
+}\r
+\r
+HANDLE inhandle, outhandle, errhandle;\r
+struct handle *stdin_handle, *stdout_handle, *stderr_handle;\r
+DWORD orig_console_mode;\r
+int connopen;\r
+\r
+WSAEVENT netevent;\r
+\r
+static Backend *back;\r
+static void *backhandle;\r
+static Config cfg;\r
+\r
+int term_ldisc(Terminal *term, int mode)\r
+{\r
+    return FALSE;\r
+}\r
+void ldisc_update(void *frontend, int echo, int edit)\r
+{\r
+    /* Update stdin read mode to reflect changes in line discipline. */\r
+    DWORD mode;\r
+\r
+    mode = ENABLE_PROCESSED_INPUT;\r
+    if (echo)\r
+       mode = mode | ENABLE_ECHO_INPUT;\r
+    else\r
+       mode = mode & ~ENABLE_ECHO_INPUT;\r
+    if (edit)\r
+       mode = mode | ENABLE_LINE_INPUT;\r
+    else\r
+       mode = mode & ~ENABLE_LINE_INPUT;\r
+    SetConsoleMode(inhandle, mode);\r
+}\r
+\r
+char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
+\r
+int from_backend(void *frontend_handle, int is_stderr,\r
+                const char *data, int len)\r
+{\r
+    if (is_stderr) {\r
+       handle_write(stderr_handle, data, len);\r
+    } else {\r
+       handle_write(stdout_handle, data, len);\r
+    }\r
+\r
+    return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);\r
+}\r
+\r
+int from_backend_untrusted(void *frontend_handle, const char *data, int len)\r
+{\r
+    /*\r
+     * No "untrusted" output should get here (the way the code is\r
+     * currently, it's all diverted by FLAG_STDERR).\r
+     */\r
+    assert(!"Unexpected call to from_backend_untrusted()");\r
+    return 0; /* not reached */\r
+}\r
+\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    int ret;\r
+    ret = cmdline_get_passwd_input(p, in, inlen);\r
+    if (ret == -1)\r
+       ret = console_get_userpass_input(p, in, inlen);\r
+    return ret;\r
+}\r
+\r
+static DWORD main_thread_id;\r
+\r
+void agent_schedule_callback(void (*callback)(void *, void *, int),\r
+                            void *callback_ctx, void *data, int len)\r
+{\r
+    struct agent_callback *c = snew(struct agent_callback);\r
+    c->callback = callback;\r
+    c->callback_ctx = callback_ctx;\r
+    c->data = data;\r
+    c->len = len;\r
+    PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c);\r
+}\r
+\r
+/*\r
+ *  Short description of parameters.\r
+ */\r
+static void usage(void)\r
+{\r
+    printf("PuTTY Link: command-line connection utility\n");\r
+    printf("%s\n", ver);\r
+    printf("Usage: plink [options] [user@]host [command]\n");\r
+    printf("       (\"host\" can also be a PuTTY saved session name)\n");\r
+    printf("Options:\n");\r
+    printf("  -V        print version information and exit\n");\r
+    printf("  -pgpfp    print PGP key fingerprints and exit\n");\r
+    printf("  -v        show verbose messages\n");\r
+    printf("  -load sessname  Load settings from saved session\n");\r
+    printf("  -ssh -telnet -rlogin -raw -serial\n");\r
+    printf("            force use of a particular protocol\n");\r
+    printf("  -P port   connect to specified port\n");\r
+    printf("  -l user   connect with specified username\n");\r
+    printf("  -batch    disable all interactive prompts\n");\r
+    printf("The following options only apply to SSH connections:\n");\r
+    printf("  -pw passw login with specified password\n");\r
+    printf("  -D [listen-IP:]listen-port\n");\r
+    printf("            Dynamic SOCKS-based port forwarding\n");\r
+    printf("  -L [listen-IP:]listen-port:host:port\n");\r
+    printf("            Forward local port to remote address\n");\r
+    printf("  -R [listen-IP:]listen-port:host:port\n");\r
+    printf("            Forward remote port to local address\n");\r
+    printf("  -X -x     enable / disable X11 forwarding\n");\r
+    printf("  -A -a     enable / disable agent forwarding\n");\r
+    printf("  -t -T     enable / disable pty allocation\n");\r
+    printf("  -1 -2     force use of particular protocol version\n");\r
+    printf("  -4 -6     force use of IPv4 or IPv6\n");\r
+    printf("  -C        enable compression\n");\r
+    printf("  -i key    private key file for authentication\n");\r
+    printf("  -noagent  disable use of Pageant\n");\r
+    printf("  -agent    enable use of Pageant\n");\r
+    printf("  -m file   read remote command(s) from file\n");\r
+    printf("  -s        remote command is an SSH subsystem (SSH-2 only)\n");\r
+    printf("  -N        don't start a shell/command (SSH-2 only)\n");\r
+    printf("  -nc host:port\n");\r
+    printf("            open tunnel in place of session (SSH-2 only)\n");\r
+    printf("  -sercfg configuration-string (e.g. 19200,8,n,1,X)\n");\r
+    printf("            Specify the serial configuration (serial only)\n");\r
+    exit(1);\r
+}\r
+\r
+static void version(void)\r
+{\r
+    printf("plink: %s\n", ver);\r
+    exit(1);\r
+}\r
+\r
+char *do_select(SOCKET skt, int startup)\r
+{\r
+    int events;\r
+    if (startup) {\r
+       events = (FD_CONNECT | FD_READ | FD_WRITE |\r
+                 FD_OOB | FD_CLOSE | FD_ACCEPT);\r
+    } else {\r
+       events = 0;\r
+    }\r
+    if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {\r
+       switch (p_WSAGetLastError()) {\r
+         case WSAENETDOWN:\r
+           return "Network is down";\r
+         default:\r
+           return "WSAEventSelect(): unknown error";\r
+       }\r
+    }\r
+    return NULL;\r
+}\r
+\r
+int stdin_gotdata(struct handle *h, void *data, int len)\r
+{\r
+    if (len < 0) {\r
+       /*\r
+        * Special case: report read error.\r
+        */\r
+       char buf[4096];\r
+       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0,\r
+                     buf, lenof(buf), NULL);\r
+       buf[lenof(buf)-1] = '\0';\r
+       if (buf[strlen(buf)-1] == '\n')\r
+           buf[strlen(buf)-1] = '\0';\r
+       fprintf(stderr, "Unable to read from standard input: %s\n", buf);\r
+       cleanup_exit(0);\r
+    }\r
+    noise_ultralight(len);\r
+    if (connopen && back->connected(backhandle)) {\r
+       if (len > 0) {\r
+           return back->send(backhandle, data, len);\r
+       } else {\r
+           back->special(backhandle, TS_EOF);\r
+           return 0;\r
+       }\r
+    } else\r
+       return 0;\r
+}\r
+\r
+void stdouterr_sent(struct handle *h, int new_backlog)\r
+{\r
+    if (new_backlog < 0) {\r
+       /*\r
+        * Special case: report write error.\r
+        */\r
+       char buf[4096];\r
+       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0,\r
+                     buf, lenof(buf), NULL);\r
+       buf[lenof(buf)-1] = '\0';\r
+       if (buf[strlen(buf)-1] == '\n')\r
+           buf[strlen(buf)-1] = '\0';\r
+       fprintf(stderr, "Unable to write to standard %s: %s\n",\r
+               (h == stdout_handle ? "output" : "error"), buf);\r
+       cleanup_exit(0);\r
+    }\r
+    if (connopen && back->connected(backhandle)) {\r
+       back->unthrottle(backhandle, (handle_backlog(stdout_handle) +\r
+                                     handle_backlog(stderr_handle)));\r
+    }\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    int sending;\r
+    int portnumber = -1;\r
+    SOCKET *sklist;\r
+    int skcount, sksize;\r
+    int exitcode;\r
+    int errors;\r
+    int got_host = FALSE;\r
+    int use_subsystem = 0;\r
+    long now, next;\r
+\r
+    sklist = NULL;\r
+    skcount = sksize = 0;\r
+    /*\r
+     * Initialise port and protocol to sensible defaults. (These\r
+     * will be overridden by more or less anything.)\r
+     */\r
+    default_protocol = PROT_SSH;\r
+    default_port = 22;\r
+\r
+    flags = FLAG_STDERR;\r
+    /*\r
+     * Process the command line.\r
+     */\r
+    do_defaults(NULL, &cfg);\r
+    loaded_session = FALSE;\r
+    default_protocol = cfg.protocol;\r
+    default_port = cfg.port;\r
+    errors = 0;\r
+    {\r
+       /*\r
+        * Override the default protocol if PLINK_PROTOCOL is set.\r
+        */\r
+       char *p = getenv("PLINK_PROTOCOL");\r
+       if (p) {\r
+           const Backend *b = backend_from_name(p);\r
+           if (b) {\r
+               default_protocol = cfg.protocol = b->protocol;\r
+               default_port = cfg.port = b->default_port;\r
+           }\r
+       }\r
+    }\r
+    while (--argc) {\r
+       char *p = *++argv;\r
+       if (*p == '-') {\r
+           int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),\r
+                                           1, &cfg);\r
+           if (ret == -2) {\r
+               fprintf(stderr,\r
+                       "plink: option \"%s\" requires an argument\n", p);\r
+               errors = 1;\r
+           } else if (ret == 2) {\r
+               --argc, ++argv;\r
+           } else if (ret == 1) {\r
+               continue;\r
+           } else if (!strcmp(p, "-batch")) {\r
+               console_batch_mode = 1;\r
+           } else if (!strcmp(p, "-s")) {\r
+               /* Save status to write to cfg later. */\r
+               use_subsystem = 1;\r
+           } else if (!strcmp(p, "-V")) {\r
+                version();\r
+            } else if (!strcmp(p, "-pgpfp")) {\r
+                pgp_fingerprints();\r
+                exit(1);\r
+           } else {\r
+               fprintf(stderr, "plink: unknown option \"%s\"\n", p);\r
+               errors = 1;\r
+           }\r
+       } else if (*p) {\r
+           if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
+               char *q = p;\r
+               /*\r
+                * If the hostname starts with "telnet:", set the\r
+                * protocol to Telnet and process the string as a\r
+                * Telnet URL.\r
+                */\r
+               if (!strncmp(q, "telnet:", 7)) {\r
+                   char c;\r
+\r
+                   q += 7;\r
+                   if (q[0] == '/' && q[1] == '/')\r
+                       q += 2;\r
+                   cfg.protocol = PROT_TELNET;\r
+                   p = q;\r
+                   while (*p && *p != ':' && *p != '/')\r
+                       p++;\r
+                   c = *p;\r
+                   if (*p)\r
+                       *p++ = '\0';\r
+                   if (c == ':')\r
+                       cfg.port = atoi(p);\r
+                   else\r
+                       cfg.port = -1;\r
+                   strncpy(cfg.host, q, sizeof(cfg.host) - 1);\r
+                   cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+                   got_host = TRUE;\r
+               } else {\r
+                   char *r, *user, *host;\r
+                   /*\r
+                    * Before we process the [user@]host string, we\r
+                    * first check for the presence of a protocol\r
+                    * prefix (a protocol name followed by ",").\r
+                    */\r
+                   r = strchr(p, ',');\r
+                   if (r) {\r
+                       const Backend *b;\r
+                       *r = '\0';\r
+                       b = backend_from_name(p);\r
+                       if (b) {\r
+                           default_protocol = cfg.protocol = b->protocol;\r
+                           portnumber = b->default_port;\r
+                       }\r
+                       p = r + 1;\r
+                   }\r
+\r
+                   /*\r
+                    * A nonzero length string followed by an @ is treated\r
+                    * as a username. (We discount an _initial_ @.) The\r
+                    * rest of the string (or the whole string if no @)\r
+                    * is treated as a session name and/or hostname.\r
+                    */\r
+                   r = strrchr(p, '@');\r
+                   if (r == p)\r
+                       p++, r = NULL; /* discount initial @ */\r
+                   if (r) {\r
+                       *r++ = '\0';\r
+                       user = p, host = r;\r
+                   } else {\r
+                       user = NULL, host = p;\r
+                   }\r
+\r
+                   /*\r
+                    * Now attempt to load a saved session with the\r
+                    * same name as the hostname.\r
+                    */\r
+                   {\r
+                       Config cfg2;\r
+                       do_defaults(host, &cfg2);\r
+                       if (loaded_session || !cfg_launchable(&cfg2)) {\r
+                           /* No settings for this host; use defaults */\r
+                           /* (or session was already loaded with -load) */\r
+                           strncpy(cfg.host, host, sizeof(cfg.host) - 1);\r
+                           cfg.host[sizeof(cfg.host) - 1] = '\0';\r
+                           cfg.port = default_port;\r
+                           got_host = TRUE;\r
+                       } else {\r
+                           cfg = cfg2;\r
+                           loaded_session = TRUE;\r
+                       }\r
+                   }\r
+\r
+                   if (user) {\r
+                       /* Patch in specified username. */\r
+                       strncpy(cfg.username, user,\r
+                               sizeof(cfg.username) - 1);\r
+                       cfg.username[sizeof(cfg.username) - 1] = '\0';\r
+                   }\r
+\r
+               }\r
+           } else {\r
+               char *command;\r
+               int cmdlen, cmdsize;\r
+               cmdlen = cmdsize = 0;\r
+               command = NULL;\r
+\r
+               while (argc) {\r
+                   while (*p) {\r
+                       if (cmdlen >= cmdsize) {\r
+                           cmdsize = cmdlen + 512;\r
+                           command = sresize(command, cmdsize, char);\r
+                       }\r
+                       command[cmdlen++]=*p++;\r
+                   }\r
+                   if (cmdlen >= cmdsize) {\r
+                       cmdsize = cmdlen + 512;\r
+                       command = sresize(command, cmdsize, char);\r
+                   }\r
+                   command[cmdlen++]=' '; /* always add trailing space */\r
+                   if (--argc) p = *++argv;\r
+               }\r
+               if (cmdlen) command[--cmdlen]='\0';\r
+                                      /* change trailing blank to NUL */\r
+               cfg.remote_cmd_ptr = command;\r
+               cfg.remote_cmd_ptr2 = NULL;\r
+               cfg.nopty = TRUE;      /* command => no terminal */\r
+\r
+               break;                 /* done with cmdline */\r
+           }\r
+       }\r
+    }\r
+\r
+    if (errors)\r
+       return 1;\r
+\r
+    if (!cfg_launchable(&cfg) || !(got_host || loaded_session)) {\r
+       usage();\r
+    }\r
+\r
+    /*\r
+     * Trim leading whitespace off the hostname if it's there.\r
+     */\r
+    {\r
+       int space = strspn(cfg.host, " \t");\r
+       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);\r
+    }\r
+\r
+    /* See if host is of the form user@host */\r
+    if (cfg_launchable(&cfg)) {\r
+       char *atsign = strrchr(cfg.host, '@');\r
+       /* Make sure we're not overflowing the user field */\r
+       if (atsign) {\r
+           if (atsign - cfg.host < sizeof cfg.username) {\r
+               strncpy(cfg.username, cfg.host, atsign - cfg.host);\r
+               cfg.username[atsign - cfg.host] = '\0';\r
+           }\r
+           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Perform command-line overrides on session configuration.\r
+     */\r
+    cmdline_run_saved(&cfg);\r
+\r
+    /*\r
+     * Apply subsystem status.\r
+     */\r
+    if (use_subsystem)\r
+       cfg.ssh_subsys = TRUE;\r
+\r
+    /*\r
+     * Trim a colon suffix off the hostname if it's there.\r
+     */\r
+    cfg.host[strcspn(cfg.host, ":")] = '\0';\r
+\r
+    /*\r
+     * Remove any remaining whitespace from the hostname.\r
+     */\r
+    {\r
+       int p1 = 0, p2 = 0;\r
+       while (cfg.host[p2] != '\0') {\r
+           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {\r
+               cfg.host[p1] = cfg.host[p2];\r
+               p1++;\r
+           }\r
+           p2++;\r
+       }\r
+       cfg.host[p1] = '\0';\r
+    }\r
+\r
+    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)\r
+       flags |= FLAG_INTERACTIVE;\r
+\r
+    /*\r
+     * Select protocol. This is farmed out into a table in a\r
+     * separate file to enable an ssh-free variant.\r
+     */\r
+    back = backend_from_proto(cfg.protocol);\r
+    if (back == NULL) {\r
+       fprintf(stderr,\r
+               "Internal fault: Unsupported protocol found\n");\r
+       return 1;\r
+    }\r
+\r
+    /*\r
+     * Select port.\r
+     */\r
+    if (portnumber != -1)\r
+       cfg.port = portnumber;\r
+\r
+    sk_init();\r
+    if (p_WSAEventSelect == NULL) {\r
+       fprintf(stderr, "Plink requires WinSock 2\n");\r
+       return 1;\r
+    }\r
+\r
+    logctx = log_init(NULL, &cfg);\r
+    console_provide_logctx(logctx);\r
+\r
+    /*\r
+     * Start up the connection.\r
+     */\r
+    netevent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+    {\r
+       const char *error;\r
+       char *realhost;\r
+       /* nodelay is only useful if stdin is a character device (console) */\r
+       int nodelay = cfg.tcp_nodelay &&\r
+           (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);\r
+\r
+       error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,\r
+                          &realhost, nodelay, cfg.tcp_keepalives);\r
+       if (error) {\r
+           fprintf(stderr, "Unable to open connection:\n%s", error);\r
+           return 1;\r
+       }\r
+       back->provide_logctx(backhandle, logctx);\r
+       sfree(realhost);\r
+    }\r
+    connopen = 1;\r
+\r
+    inhandle = GetStdHandle(STD_INPUT_HANDLE);\r
+    outhandle = GetStdHandle(STD_OUTPUT_HANDLE);\r
+    errhandle = GetStdHandle(STD_ERROR_HANDLE);\r
+\r
+    /*\r
+     * Turn off ECHO and LINE input modes. We don't care if this\r
+     * call fails, because we know we aren't necessarily running in\r
+     * a console.\r
+     */\r
+    GetConsoleMode(inhandle, &orig_console_mode);\r
+    SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);\r
+\r
+    /*\r
+     * Pass the output handles to the handle-handling subsystem.\r
+     * (The input one we leave until we're through the\r
+     * authentication process.)\r
+     */\r
+    stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0);\r
+    stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0);\r
+\r
+    main_thread_id = GetCurrentThreadId();\r
+\r
+    sending = FALSE;\r
+\r
+    now = GETTICKCOUNT();\r
+\r
+    while (1) {\r
+       int nhandles;\r
+       HANDLE *handles;        \r
+       int n;\r
+       DWORD ticks;\r
+\r
+       if (!sending && back->sendok(backhandle)) {\r
+           stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL,\r
+                                           0);\r
+           sending = TRUE;\r
+       }\r
+\r
+       if (run_timers(now, &next)) {\r
+           ticks = next - GETTICKCOUNT();\r
+           if (ticks < 0) ticks = 0;  /* just in case */\r
+       } else {\r
+           ticks = INFINITE;\r
+       }\r
+\r
+       handles = handle_get_events(&nhandles);\r
+       handles = sresize(handles, nhandles+1, HANDLE);\r
+       handles[nhandles] = netevent;\r
+       n = MsgWaitForMultipleObjects(nhandles+1, handles, FALSE, ticks,\r
+                                     QS_POSTMESSAGE);\r
+       if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {\r
+           handle_got_event(handles[n - WAIT_OBJECT_0]);\r
+       } else if (n == WAIT_OBJECT_0 + nhandles) {\r
+           WSANETWORKEVENTS things;\r
+           SOCKET socket;\r
+           extern SOCKET first_socket(int *), next_socket(int *);\r
+           extern int select_result(WPARAM, LPARAM);\r
+           int i, socketstate;\r
+\r
+           /*\r
+            * We must not call select_result() for any socket\r
+            * until we have finished enumerating within the tree.\r
+            * This is because select_result() may close the socket\r
+            * and modify the tree.\r
+            */\r
+           /* Count the active sockets. */\r
+           i = 0;\r
+           for (socket = first_socket(&socketstate);\r
+                socket != INVALID_SOCKET;\r
+                socket = next_socket(&socketstate)) i++;\r
+\r
+           /* Expand the buffer if necessary. */\r
+           if (i > sksize) {\r
+               sksize = i + 16;\r
+               sklist = sresize(sklist, sksize, SOCKET);\r
+           }\r
+\r
+           /* Retrieve the sockets into sklist. */\r
+           skcount = 0;\r
+           for (socket = first_socket(&socketstate);\r
+                socket != INVALID_SOCKET;\r
+                socket = next_socket(&socketstate)) {\r
+               sklist[skcount++] = socket;\r
+           }\r
+\r
+           /* Now we're done enumerating; go through the list. */\r
+           for (i = 0; i < skcount; i++) {\r
+               WPARAM wp;\r
+               socket = sklist[i];\r
+               wp = (WPARAM) socket;\r
+               if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {\r
+                    static const struct { int bit, mask; } eventtypes[] = {\r
+                        {FD_CONNECT_BIT, FD_CONNECT},\r
+                        {FD_READ_BIT, FD_READ},\r
+                        {FD_CLOSE_BIT, FD_CLOSE},\r
+                        {FD_OOB_BIT, FD_OOB},\r
+                        {FD_WRITE_BIT, FD_WRITE},\r
+                        {FD_ACCEPT_BIT, FD_ACCEPT},\r
+                    };\r
+                    int e;\r
+\r
+                   noise_ultralight(socket);\r
+                   noise_ultralight(things.lNetworkEvents);\r
+\r
+                    for (e = 0; e < lenof(eventtypes); e++)\r
+                        if (things.lNetworkEvents & eventtypes[e].mask) {\r
+                            LPARAM lp;\r
+                            int err = things.iErrorCode[eventtypes[e].bit];\r
+                            lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);\r
+                            connopen &= select_result(wp, lp);\r
+                        }\r
+               }\r
+           }\r
+       } else if (n == WAIT_OBJECT_0 + nhandles + 1) {\r
+           MSG msg;\r
+           while (PeekMessage(&msg, INVALID_HANDLE_VALUE,\r
+                              WM_AGENT_CALLBACK, WM_AGENT_CALLBACK,\r
+                              PM_REMOVE)) {\r
+               struct agent_callback *c = (struct agent_callback *)msg.lParam;\r
+               c->callback(c->callback_ctx, c->data, c->len);\r
+               sfree(c);\r
+           }\r
+       }\r
+\r
+       if (n == WAIT_TIMEOUT) {\r
+           now = next;\r
+       } else {\r
+           now = GETTICKCOUNT();\r
+       }\r
+\r
+       sfree(handles);\r
+\r
+       if (sending)\r
+           handle_unthrottle(stdin_handle, back->sendbuffer(backhandle));\r
+\r
+       if ((!connopen || !back->connected(backhandle)) &&\r
+           handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0)\r
+           break;                     /* we closed the connection */\r
+    }\r
+    exitcode = back->exitcode(backhandle);\r
+    if (exitcode < 0) {\r
+       fprintf(stderr, "Remote process exit code unavailable\n");\r
+       exitcode = 1;                  /* this is an error condition */\r
+    }\r
+    cleanup_exit(exitcode);\r
+    return 0;                         /* placate compiler warning */\r
+}\r
diff --git a/putty/WINDOWS/WINPRINT.C b/putty/WINDOWS/WINPRINT.C
new file mode 100644 (file)
index 0000000..4098978
--- /dev/null
@@ -0,0 +1,185 @@
+/*\r
+ * Printing interface for PuTTY.\r
+ */\r
+\r
+#include "putty.h"\r
+#include <winspool.h>\r
+\r
+struct printer_enum_tag {\r
+    int nprinters;\r
+    DWORD enum_level;\r
+    union {\r
+       LPPRINTER_INFO_4 i4;\r
+       LPPRINTER_INFO_5 i5;\r
+    } info;\r
+};\r
+\r
+struct printer_job_tag {\r
+    HANDLE hprinter;\r
+};\r
+\r
+static char *printer_add_enum(int param, DWORD level, char *buffer,\r
+                              int offset, int *nprinters_ptr)\r
+{\r
+    DWORD needed = 0, nprinters = 0;\r
+\r
+    buffer = sresize(buffer, offset+512, char);\r
+\r
+    /*\r
+     * Exploratory call to EnumPrinters to determine how much space\r
+     * we'll need for the output. Discard the return value since it\r
+     * will almost certainly be a failure due to lack of space.\r
+     */\r
+    EnumPrinters(param, NULL, level, buffer+offset, 512,\r
+                &needed, &nprinters);\r
+\r
+    if (needed < 512)\r
+        needed = 512;\r
+\r
+    buffer = sresize(buffer, offset+needed, char);\r
+\r
+    if (EnumPrinters(param, NULL, level, buffer+offset,\r
+                     needed, &needed, &nprinters) == 0)\r
+        return NULL;\r
+\r
+    *nprinters_ptr += nprinters;\r
+\r
+    return buffer;\r
+}\r
+\r
+printer_enum *printer_start_enum(int *nprinters_ptr)\r
+{\r
+    printer_enum *ret = snew(printer_enum);\r
+    char *buffer = NULL, *retval;\r
+\r
+    *nprinters_ptr = 0;                       /* default return value */\r
+    buffer = snewn(512, char);\r
+\r
+    /*\r
+     * Determine what enumeration level to use.\r
+     * When enumerating printers, we need to use PRINTER_INFO_4 on\r
+     * NT-class systems to avoid Windows looking too hard for them and\r
+     * slowing things down; and we need to avoid PRINTER_INFO_5 as\r
+     * we've seen network printers not show up.\r
+     * On 9x-class systems, PRINTER_INFO_4 isn't available and\r
+     * PRINTER_INFO_5 is recommended.\r
+     * Bletch.\r
+     */\r
+    if (osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) {\r
+       ret->enum_level = 5;\r
+    } else {\r
+       ret->enum_level = 4;\r
+    }\r
+\r
+    retval = printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,\r
+                             ret->enum_level, buffer, 0, nprinters_ptr);\r
+    if (!retval)\r
+        goto error;\r
+    else\r
+        buffer = retval;\r
+\r
+    switch (ret->enum_level) {\r
+      case 4:\r
+       ret->info.i4 = (LPPRINTER_INFO_4)buffer;\r
+       break;\r
+      case 5:\r
+       ret->info.i5 = (LPPRINTER_INFO_5)buffer;\r
+       break;\r
+    }\r
+    ret->nprinters = *nprinters_ptr;\r
+    \r
+    return ret;\r
+\r
+    error:\r
+    sfree(buffer);\r
+    sfree(ret);\r
+    *nprinters_ptr = 0;\r
+    return NULL;\r
+}\r
+\r
+char *printer_get_name(printer_enum *pe, int i)\r
+{\r
+    if (!pe)\r
+       return NULL;\r
+    if (i < 0 || i >= pe->nprinters)\r
+       return NULL;\r
+    switch (pe->enum_level) {\r
+      case 4:\r
+       return pe->info.i4[i].pPrinterName;\r
+      case 5:\r
+       return pe->info.i5[i].pPrinterName;\r
+      default:\r
+       return NULL;\r
+    }\r
+}\r
+\r
+void printer_finish_enum(printer_enum *pe)\r
+{\r
+    if (!pe)\r
+       return;\r
+    switch (pe->enum_level) {\r
+      case 4:\r
+       sfree(pe->info.i4);\r
+       break;\r
+      case 5:\r
+       sfree(pe->info.i5);\r
+       break;\r
+    }\r
+    sfree(pe);\r
+}\r
+\r
+printer_job *printer_start_job(char *printer)\r
+{\r
+    printer_job *ret = snew(printer_job);\r
+    DOC_INFO_1 docinfo;\r
+    int jobstarted = 0, pagestarted = 0;\r
+\r
+    ret->hprinter = NULL;\r
+    if (!OpenPrinter(printer, &ret->hprinter, NULL))\r
+       goto error;\r
+\r
+    docinfo.pDocName = "PuTTY remote printer output";\r
+    docinfo.pOutputFile = NULL;\r
+    docinfo.pDatatype = "RAW";\r
+\r
+    if (!StartDocPrinter(ret->hprinter, 1, (LPSTR)&docinfo))\r
+       goto error;\r
+    jobstarted = 1;\r
+\r
+    if (!StartPagePrinter(ret->hprinter))\r
+       goto error;\r
+    pagestarted = 1;\r
+\r
+    return ret;\r
+\r
+    error:\r
+    if (pagestarted)\r
+       EndPagePrinter(ret->hprinter);\r
+    if (jobstarted)\r
+       EndDocPrinter(ret->hprinter);\r
+    if (ret->hprinter)\r
+       ClosePrinter(ret->hprinter);\r
+    sfree(ret);\r
+    return NULL;\r
+}\r
+\r
+void printer_job_data(printer_job *pj, void *data, int len)\r
+{\r
+    DWORD written;\r
+\r
+    if (!pj)\r
+       return;\r
+\r
+    WritePrinter(pj->hprinter, data, len, &written);\r
+}\r
+\r
+void printer_finish_job(printer_job *pj)\r
+{\r
+    if (!pj)\r
+       return;\r
+\r
+    EndPagePrinter(pj->hprinter);\r
+    EndDocPrinter(pj->hprinter);\r
+    ClosePrinter(pj->hprinter);\r
+    sfree(pj);\r
+}\r
diff --git a/putty/WINDOWS/WINPROXY.C b/putty/WINDOWS/WINPROXY.C
new file mode 100644 (file)
index 0000000..4da4d2e
--- /dev/null
@@ -0,0 +1,219 @@
+/*\r
+ * winproxy.c: Windows implementation of platform_new_connection(),\r
+ * supporting an OpenSSH-like proxy command via the winhandl.c\r
+ * mechanism.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <assert.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "tree234.h"\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+typedef struct Socket_localproxy_tag *Local_Proxy_Socket;\r
+\r
+struct Socket_localproxy_tag {\r
+    const struct socket_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+\r
+    HANDLE to_cmd_H, from_cmd_H;\r
+    struct handle *to_cmd_h, *from_cmd_h;\r
+\r
+    char *error;\r
+\r
+    Plug plug;\r
+\r
+    void *privptr;\r
+};\r
+\r
+int localproxy_gotdata(struct handle *h, void *data, int len)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) handle_get_privdata(h);\r
+\r
+    if (len < 0) {\r
+       return plug_closing(ps->plug, "Read error from local proxy command",\r
+                           0, 0);\r
+    } else if (len == 0) {\r
+       return plug_closing(ps->plug, NULL, 0, 0);\r
+    } else {\r
+       return plug_receive(ps->plug, 0, data, len);\r
+    }\r
+}\r
+\r
+void localproxy_sentdata(struct handle *h, int new_backlog)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) handle_get_privdata(h);\r
+    \r
+    plug_sent(ps->plug, new_backlog);\r
+}\r
+\r
+static Plug sk_localproxy_plug (Socket s, Plug p)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    Plug ret = ps->plug;\r
+    if (p)\r
+       ps->plug = p;\r
+    return ret;\r
+}\r
+\r
+static void sk_localproxy_close (Socket s)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+\r
+    handle_free(ps->to_cmd_h);\r
+    handle_free(ps->from_cmd_h);\r
+    CloseHandle(ps->to_cmd_H);\r
+    CloseHandle(ps->from_cmd_H);\r
+\r
+    sfree(ps);\r
+}\r
+\r
+static int sk_localproxy_write (Socket s, const char *data, int len)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+\r
+    return handle_write(ps->to_cmd_h, data, len);\r
+}\r
+\r
+static int sk_localproxy_write_oob(Socket s, const char *data, int len)\r
+{\r
+    /*\r
+     * oob data is treated as inband; nasty, but nothing really\r
+     * better we can do\r
+     */\r
+    return sk_localproxy_write(s, data, len);\r
+}\r
+\r
+static void sk_localproxy_flush(Socket s)\r
+{\r
+    /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */\r
+    /* do nothing */\r
+}\r
+\r
+static void sk_localproxy_set_private_ptr(Socket s, void *ptr)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    ps->privptr = ptr;\r
+}\r
+\r
+static void *sk_localproxy_get_private_ptr(Socket s)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    return ps->privptr;\r
+}\r
+\r
+static void sk_localproxy_set_frozen(Socket s, int is_frozen)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+\r
+    /*\r
+     * FIXME\r
+     */\r
+}\r
+\r
+static const char *sk_localproxy_socket_error(Socket s)\r
+{\r
+    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
+    return ps->error;\r
+}\r
+\r
+Socket platform_new_connection(SockAddr addr, char *hostname,\r
+                              int port, int privport,\r
+                              int oobinline, int nodelay, int keepalive,\r
+                              Plug plug, const Config *cfg)\r
+{\r
+    char *cmd;\r
+\r
+    static const struct socket_function_table socket_fn_table = {\r
+       sk_localproxy_plug,\r
+       sk_localproxy_close,\r
+       sk_localproxy_write,\r
+       sk_localproxy_write_oob,\r
+       sk_localproxy_flush,\r
+       sk_localproxy_set_private_ptr,\r
+       sk_localproxy_get_private_ptr,\r
+       sk_localproxy_set_frozen,\r
+       sk_localproxy_socket_error\r
+    };\r
+\r
+    Local_Proxy_Socket ret;\r
+    HANDLE us_to_cmd, us_from_cmd, cmd_to_us, cmd_from_us;\r
+    SECURITY_ATTRIBUTES sa;\r
+    STARTUPINFO si;\r
+    PROCESS_INFORMATION pi;\r
+\r
+    if (cfg->proxy_type != PROXY_CMD)\r
+       return NULL;\r
+\r
+    cmd = format_telnet_command(addr, port, cfg);\r
+\r
+    {\r
+       char *msg = dupprintf("Starting local proxy command: %s", cmd);\r
+       /* We're allowed to pass NULL here, because we're part of the Windows\r
+        * front end so we know logevent doesn't expect any data. */\r
+       logevent(NULL, msg);\r
+       sfree(msg);\r
+    }\r
+\r
+    ret = snew(struct Socket_localproxy_tag);\r
+    ret->fn = &socket_fn_table;\r
+    ret->plug = plug;\r
+    ret->error = NULL;\r
+\r
+    /*\r
+     * Create the pipes to the proxy command, and spawn the proxy\r
+     * command process.\r
+     */\r
+    sa.nLength = sizeof(sa);\r
+    sa.lpSecurityDescriptor = NULL;    /* default */\r
+    sa.bInheritHandle = TRUE;\r
+    if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) {\r
+       ret->error = dupprintf("Unable to create pipes for proxy command");\r
+       return (Socket)ret;\r
+    }\r
+\r
+    if (!CreatePipe(&cmd_from_us, &us_to_cmd, &sa, 0)) {\r
+       CloseHandle(us_from_cmd);\r
+       CloseHandle(cmd_to_us);\r
+       ret->error = dupprintf("Unable to create pipes for proxy command");\r
+       return (Socket)ret;\r
+    }\r
+\r
+    SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);\r
+    SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0);\r
+\r
+    si.cb = sizeof(si);\r
+    si.lpReserved = NULL;\r
+    si.lpDesktop = NULL;\r
+    si.lpTitle = NULL;\r
+    si.dwFlags = STARTF_USESTDHANDLES;\r
+    si.cbReserved2 = 0;\r
+    si.lpReserved2 = NULL;\r
+    si.hStdInput = cmd_from_us;\r
+    si.hStdOutput = cmd_to_us;\r
+    si.hStdError = NULL;\r
+    CreateProcess(NULL, cmd, NULL, NULL, TRUE,\r
+                 CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,\r
+                 NULL, NULL, &si, &pi);\r
+\r
+    sfree(cmd);\r
+\r
+    CloseHandle(cmd_from_us);\r
+    CloseHandle(cmd_to_us);\r
+\r
+    ret->to_cmd_H = us_to_cmd;\r
+    ret->from_cmd_H = us_from_cmd;\r
+\r
+    ret->from_cmd_h = handle_input_new(ret->from_cmd_H, localproxy_gotdata,\r
+                                      ret, 0);\r
+    ret->to_cmd_h = handle_output_new(ret->to_cmd_H, localproxy_sentdata,\r
+                                     ret, 0);\r
+\r
+    /* We are responsible for this and don't need it any more */\r
+    sk_addr_free(addr);\r
+\r
+    return (Socket) ret;\r
+}\r
diff --git a/putty/WINDOWS/WINSER.C b/putty/WINDOWS/WINSER.C
new file mode 100644 (file)
index 0000000..1928324
--- /dev/null
@@ -0,0 +1,460 @@
+/*\r
+ * Serial back end (Windows-specific).\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <limits.h>\r
+\r
+#include "putty.h"\r
+\r
+#define SERIAL_MAX_BACKLOG 4096\r
+\r
+typedef struct serial_backend_data {\r
+    HANDLE port;\r
+    struct handle *out, *in;\r
+    void *frontend;\r
+    int bufsize;\r
+    long clearbreak_time;\r
+    int break_in_progress;\r
+} *Serial;\r
+\r
+static void serial_terminate(Serial serial)\r
+{\r
+    if (serial->out) {\r
+       handle_free(serial->out);\r
+       serial->out = NULL;\r
+    }\r
+    if (serial->in) {\r
+       handle_free(serial->in);\r
+       serial->in = NULL;\r
+    }\r
+    if (serial->port != INVALID_HANDLE_VALUE) {\r
+       if (serial->break_in_progress)\r
+           ClearCommBreak(serial->port);\r
+       CloseHandle(serial->port);\r
+       serial->port = INVALID_HANDLE_VALUE;\r
+    }\r
+}\r
+\r
+static int serial_gotdata(struct handle *h, void *data, int len)\r
+{\r
+    Serial serial = (Serial)handle_get_privdata(h);\r
+    if (len <= 0) {\r
+       const char *error_msg;\r
+\r
+       /*\r
+        * Currently, len==0 should never happen because we're\r
+        * ignoring EOFs. However, it seems not totally impossible\r
+        * that this same back end might be usable to talk to named\r
+        * pipes or some other non-serial device, in which case EOF\r
+        * may become meaningful here.\r
+        */\r
+       if (len == 0)\r
+           error_msg = "End of file reading from serial device";\r
+       else\r
+           error_msg = "Error reading from serial device";\r
+\r
+       serial_terminate(serial);\r
+\r
+       notify_remote_exit(serial->frontend);\r
+\r
+       logevent(serial->frontend, error_msg);\r
+\r
+       connection_fatal(serial->frontend, "%s", error_msg);\r
+\r
+       return 0;                      /* placate optimiser */\r
+    } else {\r
+       return from_backend(serial->frontend, 0, data, len);\r
+    }\r
+}\r
+\r
+static void serial_sentdata(struct handle *h, int new_backlog)\r
+{\r
+    Serial serial = (Serial)handle_get_privdata(h);\r
+    if (new_backlog < 0) {\r
+       const char *error_msg = "Error writing to serial device";\r
+\r
+       serial_terminate(serial);\r
+\r
+       notify_remote_exit(serial->frontend);\r
+\r
+       logevent(serial->frontend, error_msg);\r
+\r
+       connection_fatal(serial->frontend, "%s", error_msg);\r
+    } else {\r
+       serial->bufsize = new_backlog;\r
+    }\r
+}\r
+\r
+static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)\r
+{\r
+    DCB dcb;\r
+    COMMTIMEOUTS timeouts;\r
+\r
+    /*\r
+     * Set up the serial port parameters. If we can't even\r
+     * GetCommState, we ignore the problem on the grounds that the\r
+     * user might have pointed us at some other type of two-way\r
+     * device instead of a serial port.\r
+     */\r
+    if (GetCommState(serport, &dcb)) {\r
+       char *msg;\r
+       const char *str;\r
+\r
+       /*\r
+        * Boilerplate.\r
+        */\r
+       dcb.fBinary = TRUE;\r
+       dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
+       dcb.fDsrSensitivity = FALSE;\r
+       dcb.fTXContinueOnXoff = FALSE;\r
+       dcb.fOutX = FALSE;\r
+       dcb.fInX = FALSE;\r
+       dcb.fErrorChar = FALSE;\r
+       dcb.fNull = FALSE;\r
+       dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
+       dcb.fAbortOnError = FALSE;\r
+       dcb.fOutxCtsFlow = FALSE;\r
+       dcb.fOutxDsrFlow = FALSE;\r
+\r
+       /*\r
+        * Configurable parameters.\r
+        */\r
+       dcb.BaudRate = cfg->serspeed;\r
+       msg = dupprintf("Configuring baud rate %d", cfg->serspeed);\r
+       logevent(serial->frontend, msg);\r
+       sfree(msg);\r
+\r
+       dcb.ByteSize = cfg->serdatabits;\r
+       msg = dupprintf("Configuring %d data bits", cfg->serdatabits);\r
+       logevent(serial->frontend, msg);\r
+       sfree(msg);\r
+\r
+       switch (cfg->serstopbits) {\r
+         case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;\r
+         case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;\r
+         case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;\r
+         default: return "Invalid number of stop bits (need 1, 1.5 or 2)";\r
+       }\r
+       msg = dupprintf("Configuring %s data bits", str);\r
+       logevent(serial->frontend, msg);\r
+       sfree(msg);\r
+\r
+       switch (cfg->serparity) {\r
+         case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;\r
+         case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;\r
+         case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;\r
+         case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;\r
+         case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;\r
+       }\r
+       msg = dupprintf("Configuring %s parity", str);\r
+       logevent(serial->frontend, msg);\r
+       sfree(msg);\r
+\r
+       switch (cfg->serflow) {\r
+         case SER_FLOW_NONE:\r
+           str = "no";\r
+           break;\r
+         case SER_FLOW_XONXOFF:\r
+           dcb.fOutX = dcb.fInX = TRUE;\r
+           str = "XON/XOFF";\r
+           break;\r
+         case SER_FLOW_RTSCTS:\r
+           dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;\r
+           dcb.fOutxCtsFlow = TRUE;\r
+           str = "RTS/CTS";\r
+           break;\r
+         case SER_FLOW_DSRDTR:\r
+           dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;\r
+           dcb.fOutxDsrFlow = TRUE;\r
+           str = "DSR/DTR";\r
+           break;\r
+       }\r
+       msg = dupprintf("Configuring %s flow control", str);\r
+       logevent(serial->frontend, msg);\r
+       sfree(msg);\r
+\r
+       if (!SetCommState(serport, &dcb))\r
+           return "Unable to configure serial port";\r
+\r
+       timeouts.ReadIntervalTimeout = 1;\r
+       timeouts.ReadTotalTimeoutMultiplier = 0;\r
+       timeouts.ReadTotalTimeoutConstant = 0;\r
+       timeouts.WriteTotalTimeoutMultiplier = 0;\r
+       timeouts.WriteTotalTimeoutConstant = 0;\r
+       if (!SetCommTimeouts(serport, &timeouts))\r
+           return "Unable to configure serial timeouts";\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Called to set up the serial connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *serial_init(void *frontend_handle, void **backend_handle,\r
+                              Config *cfg,\r
+                              char *host, int port, char **realhost, int nodelay,\r
+                              int keepalive)\r
+{\r
+    Serial serial;\r
+    HANDLE serport;\r
+    const char *err;\r
+\r
+    serial = snew(struct serial_backend_data);\r
+    serial->port = INVALID_HANDLE_VALUE;\r
+    serial->out = serial->in = NULL;\r
+    serial->bufsize = 0;\r
+    serial->break_in_progress = FALSE;\r
+    *backend_handle = serial;\r
+\r
+    serial->frontend = frontend_handle;\r
+\r
+    {\r
+       char *msg = dupprintf("Opening serial device %s", cfg->serline);\r
+       logevent(serial->frontend, msg);\r
+    }\r
+\r
+    {\r
+       /*\r
+        * Munge the string supplied by the user into a Windows filename.\r
+        *\r
+        * Windows supports opening a few "legacy" devices (including\r
+        * COM1-9) by specifying their names verbatim as a filename to\r
+        * open. (Thus, no files can ever have these names. See\r
+        * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx>\r
+        * ("Naming a File") for the complete list of reserved names.)\r
+        *\r
+        * However, this doesn't let you get at devices COM10 and above.\r
+        * For that, you need to specify a filename like "\\.\COM10".\r
+        * This is also necessary for special serial and serial-like\r
+        * devices such as \\.\WCEUSBSH001. It also works for the "legacy"\r
+        * names, so you can do \\.\COM1 (verified as far back as Win95).\r
+        * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx>\r
+        * (CreateFile() docs).\r
+        *\r
+        * So, we believe that prepending "\\.\" should always be the\r
+        * Right Thing. However, just in case someone finds something to\r
+        * talk to that doesn't exist under there, if the serial line\r
+        * contains a backslash, we use it verbatim. (This also lets\r
+        * existing configurations using \\.\ continue working.)\r
+        */\r
+       char *serfilename =\r
+           dupprintf("%s%s",\r
+                     strchr(cfg->serline, '\\') ? "" : "\\\\.\\",\r
+                     cfg->serline);\r
+       serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,\r
+                            OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
+       sfree(serfilename);\r
+    }\r
+\r
+    if (serport == INVALID_HANDLE_VALUE)\r
+       return "Unable to open serial port";\r
+\r
+    err = serial_configure(serial, serport, cfg);\r
+    if (err)\r
+       return err;\r
+\r
+    serial->port = serport;\r
+    serial->out = handle_output_new(serport, serial_sentdata, serial,\r
+                                   HANDLE_FLAG_OVERLAPPED);\r
+    serial->in = handle_input_new(serport, serial_gotdata, serial,\r
+                                 HANDLE_FLAG_OVERLAPPED |\r
+                                 HANDLE_FLAG_IGNOREEOF |\r
+                                 HANDLE_FLAG_UNITBUFFER);\r
+\r
+    *realhost = dupstr(cfg->serline);\r
+\r
+    /*\r
+     * Specials are always available.\r
+     */\r
+    update_specials_menu(serial->frontend);\r
+\r
+    return NULL;\r
+}\r
+\r
+static void serial_free(void *handle)\r
+{\r
+    Serial serial = (Serial) handle;\r
+\r
+    serial_terminate(serial);\r
+    expire_timer_context(serial);\r
+    sfree(serial);\r
+}\r
+\r
+static void serial_reconfig(void *handle, Config *cfg)\r
+{\r
+    Serial serial = (Serial) handle;\r
+    const char *err;\r
+\r
+    err = serial_configure(serial, serial->port, cfg);\r
+\r
+    /*\r
+     * FIXME: what should we do if err returns something?\r
+     */\r
+}\r
+\r
+/*\r
+ * Called to send data down the serial connection.\r
+ */\r
+static int serial_send(void *handle, char *buf, int len)\r
+{\r
+    Serial serial = (Serial) handle;\r
+\r
+    if (serial->out == NULL)\r
+       return 0;\r
+\r
+    serial->bufsize = handle_write(serial->out, buf, len);\r
+    return serial->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to query the current sendability status.\r
+ */\r
+static int serial_sendbuffer(void *handle)\r
+{\r
+    Serial serial = (Serial) handle;\r
+    return serial->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void serial_size(void *handle, int width, int height)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+static void serbreak_timer(void *ctx, long now)\r
+{\r
+    Serial serial = (Serial)ctx;\r
+\r
+    if (now >= serial->clearbreak_time && serial->port) {\r
+       ClearCommBreak(serial->port);\r
+       serial->break_in_progress = FALSE;\r
+       logevent(serial->frontend, "Finished serial break");\r
+    }\r
+}\r
+\r
+/*\r
+ * Send serial special codes.\r
+ */\r
+static void serial_special(void *handle, Telnet_Special code)\r
+{\r
+    Serial serial = (Serial) handle;\r
+\r
+    if (serial->port && code == TS_BRK) {\r
+       logevent(serial->frontend, "Starting serial break at user request");\r
+       SetCommBreak(serial->port);\r
+       /*\r
+        * To send a serial break on Windows, we call SetCommBreak\r
+        * to begin the break, then wait a bit, and then call\r
+        * ClearCommBreak to finish it. Hence, I must use timing.c\r
+        * to arrange a callback when it's time to do the latter.\r
+        * \r
+        * SUS says that a default break length must be between 1/4\r
+        * and 1/2 second. FreeBSD apparently goes with 2/5 second,\r
+        * and so will I. \r
+        */\r
+       serial->clearbreak_time =\r
+           schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial);\r
+       serial->break_in_progress = TRUE;\r
+    }\r
+\r
+    return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *serial_get_specials(void *handle)\r
+{\r
+    static const struct telnet_special specials[] = {\r
+       {"Break", TS_BRK},\r
+       {NULL, TS_EXITMENU}\r
+    };\r
+    return specials;\r
+}\r
+\r
+static int serial_connected(void *handle)\r
+{\r
+    return 1;                         /* always connected */\r
+}\r
+\r
+static int serial_sendok(void *handle)\r
+{\r
+    return 1;\r
+}\r
+\r
+static void serial_unthrottle(void *handle, int backlog)\r
+{\r
+    Serial serial = (Serial) handle;\r
+    if (serial->in)\r
+       handle_unthrottle(serial->in, backlog);\r
+}\r
+\r
+static int serial_ldisc(void *handle, int option)\r
+{\r
+    /*\r
+     * Local editing and local echo are off by default.\r
+     */\r
+    return 0;\r
+}\r
+\r
+static void serial_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static void serial_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static int serial_exitcode(void *handle)\r
+{\r
+    Serial serial = (Serial) handle;\r
+    if (serial->port != INVALID_HANDLE_VALUE)\r
+        return -1;                     /* still connected */\r
+    else\r
+        /* Exit codes are a meaningless concept with serial ports */\r
+        return INT_MAX;\r
+}\r
+\r
+/*\r
+ * cfg_info for Serial does nothing at all.\r
+ */\r
+static int serial_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+Backend serial_backend = {\r
+    serial_init,\r
+    serial_free,\r
+    serial_reconfig,\r
+    serial_send,\r
+    serial_sendbuffer,\r
+    serial_size,\r
+    serial_special,\r
+    serial_get_specials,\r
+    serial_connected,\r
+    serial_exitcode,\r
+    serial_sendok,\r
+    serial_ldisc,\r
+    serial_provide_ldisc,\r
+    serial_provide_logctx,\r
+    serial_unthrottle,\r
+    serial_cfg_info,\r
+    "serial",\r
+    PROT_SERIAL,\r
+    0\r
+};\r
diff --git a/putty/WINDOWS/WINSFTP.C b/putty/WINDOWS/WINSFTP.C
new file mode 100644 (file)
index 0000000..8a36dcb
--- /dev/null
@@ -0,0 +1,721 @@
+/*\r
+ * winsftp.c: the Windows-specific parts of PSFTP and PSCP.\r
+ */\r
+\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+#include "psftp.h"\r
+#include "ssh.h"\r
+#include "int64.h"\r
+\r
+char *get_ttymode(void *frontend, const char *mode) { return NULL; }\r
+\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
+{\r
+    int ret;\r
+    ret = cmdline_get_passwd_input(p, in, inlen);\r
+    if (ret == -1)\r
+       ret = console_get_userpass_input(p, in, inlen);\r
+    return ret;\r
+}\r
+\r
+void platform_get_x11_auth(struct X11Display *display, const Config *cfg)\r
+{\r
+    /* Do nothing, therefore no auth. */\r
+}\r
+const int platform_uses_x11_unix_by_default = TRUE;\r
+\r
+/* ----------------------------------------------------------------------\r
+ * File access abstraction.\r
+ */\r
+\r
+/*\r
+ * Set local current directory. Returns NULL on success, or else an\r
+ * error message which must be freed after printing.\r
+ */\r
+char *psftp_lcd(char *dir)\r
+{\r
+    char *ret = NULL;\r
+\r
+    if (!SetCurrentDirectory(dir)) {\r
+       LPVOID message;\r
+       int i;\r
+       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |\r
+                     FORMAT_MESSAGE_FROM_SYSTEM |\r
+                     FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                     NULL, GetLastError(),\r
+                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+                     (LPTSTR)&message, 0, NULL);\r
+       i = strcspn((char *)message, "\n");\r
+       ret = dupprintf("%.*s", i, (LPCTSTR)message);\r
+       LocalFree(message);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+/*\r
+ * Get local current directory. Returns a string which must be\r
+ * freed.\r
+ */\r
+char *psftp_getcwd(void)\r
+{\r
+    char *ret = snewn(256, char);\r
+    int len = GetCurrentDirectory(256, ret);\r
+    if (len > 256)\r
+       ret = sresize(ret, len, char);\r
+    GetCurrentDirectory(len, ret);\r
+    return ret;\r
+}\r
+\r
+#define TIME_POSIX_TO_WIN(t, ft) do { \\r
+    ULARGE_INTEGER uli; \\r
+    uli.QuadPart = ((ULONGLONG)(t) + 11644473600ull) * 10000000ull; \\r
+    (ft).dwLowDateTime  = uli.LowPart; \\r
+    (ft).dwHighDateTime = uli.HighPart; \\r
+} while(0)\r
+#define TIME_WIN_TO_POSIX(ft, t) do { \\r
+    ULARGE_INTEGER uli; \\r
+    uli.LowPart  = (ft).dwLowDateTime; \\r
+    uli.HighPart = (ft).dwHighDateTime; \\r
+    uli.QuadPart = uli.QuadPart / 10000000ull - 11644473600ull; \\r
+    (t) = (unsigned long) uli.QuadPart; \\r
+} while(0)\r
+\r
+struct RFile {\r
+    HANDLE h;\r
+};\r
+\r
+RFile *open_existing_file(char *name, uint64 *size,\r
+                         unsigned long *mtime, unsigned long *atime)\r
+{\r
+    HANDLE h;\r
+    RFile *ret;\r
+\r
+    h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,\r
+                  OPEN_EXISTING, 0, 0);\r
+    if (h == INVALID_HANDLE_VALUE)\r
+       return NULL;\r
+\r
+    ret = snew(RFile);\r
+    ret->h = h;\r
+\r
+    if (size)\r
+        size->lo=GetFileSize(h, &(size->hi));\r
+\r
+    if (mtime || atime) {\r
+       FILETIME actime, wrtime;\r
+       GetFileTime(h, NULL, &actime, &wrtime);\r
+       if (atime)\r
+           TIME_WIN_TO_POSIX(actime, *atime);\r
+       if (mtime)\r
+           TIME_WIN_TO_POSIX(wrtime, *mtime);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+int read_from_file(RFile *f, void *buffer, int length)\r
+{\r
+    int ret;\r
+    DWORD read;\r
+    ret = ReadFile(f->h, buffer, length, &read, NULL);\r
+    if (!ret)\r
+       return -1;                     /* error */\r
+    else\r
+       return read;\r
+}\r
+\r
+void close_rfile(RFile *f)\r
+{\r
+    CloseHandle(f->h);\r
+    sfree(f);\r
+}\r
+\r
+struct WFile {\r
+    HANDLE h;\r
+};\r
+\r
+WFile *open_new_file(char *name)\r
+{\r
+    HANDLE h;\r
+    WFile *ret;\r
+\r
+    h = CreateFile(name, GENERIC_WRITE, 0, NULL,\r
+                  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);\r
+    if (h == INVALID_HANDLE_VALUE)\r
+       return NULL;\r
+\r
+    ret = snew(WFile);\r
+    ret->h = h;\r
+\r
+    return ret;\r
+}\r
+\r
+WFile *open_existing_wfile(char *name, uint64 *size)\r
+{\r
+    HANDLE h;\r
+    WFile *ret;\r
+\r
+    h = CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, NULL,\r
+                  OPEN_EXISTING, 0, 0);\r
+    if (h == INVALID_HANDLE_VALUE)\r
+       return NULL;\r
+\r
+    ret = snew(WFile);\r
+    ret->h = h;\r
+\r
+    if (size)\r
+       size->lo=GetFileSize(h, &(size->hi));\r
+\r
+    return ret;\r
+}\r
+\r
+int write_to_file(WFile *f, void *buffer, int length)\r
+{\r
+    int ret;\r
+    DWORD written;\r
+    ret = WriteFile(f->h, buffer, length, &written, NULL);\r
+    if (!ret)\r
+       return -1;                     /* error */\r
+    else\r
+       return written;\r
+}\r
+\r
+void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)\r
+{\r
+    FILETIME actime, wrtime;\r
+    TIME_POSIX_TO_WIN(atime, actime);\r
+    TIME_POSIX_TO_WIN(mtime, wrtime);\r
+    SetFileTime(f->h, NULL, &actime, &wrtime);\r
+}\r
+\r
+void close_wfile(WFile *f)\r
+{\r
+    CloseHandle(f->h);\r
+    sfree(f);\r
+}\r
+\r
+/* Seek offset bytes through file, from whence, where whence is\r
+   FROM_START, FROM_CURRENT, or FROM_END */\r
+int seek_file(WFile *f, uint64 offset, int whence)\r
+{\r
+    DWORD movemethod;\r
+\r
+    switch (whence) {\r
+    case FROM_START:\r
+       movemethod = FILE_BEGIN;\r
+       break;\r
+    case FROM_CURRENT:\r
+       movemethod = FILE_CURRENT;\r
+       break;\r
+    case FROM_END:\r
+       movemethod = FILE_END;\r
+       break;\r
+    default:\r
+       return -1;\r
+    }\r
+\r
+    SetFilePointer(f->h, offset.lo, &(offset.hi), movemethod);\r
+    \r
+    if (GetLastError() != NO_ERROR)\r
+       return -1;\r
+    else \r
+       return 0;\r
+}\r
+\r
+uint64 get_file_posn(WFile *f)\r
+{\r
+    uint64 ret;\r
+\r
+    ret.hi = 0L;\r
+    ret.lo = SetFilePointer(f->h, 0L, &(ret.hi), FILE_CURRENT);\r
+\r
+    return ret;\r
+}\r
+\r
+int file_type(char *name)\r
+{\r
+    DWORD attr;\r
+    attr = GetFileAttributes(name);\r
+    /* We know of no `weird' files under Windows. */\r
+    if (attr == (DWORD)-1)\r
+       return FILE_TYPE_NONEXISTENT;\r
+    else if (attr & FILE_ATTRIBUTE_DIRECTORY)\r
+       return FILE_TYPE_DIRECTORY;\r
+    else\r
+       return FILE_TYPE_FILE;\r
+}\r
+\r
+struct DirHandle {\r
+    HANDLE h;\r
+    char *name;\r
+};\r
+\r
+DirHandle *open_directory(char *name)\r
+{\r
+    HANDLE h;\r
+    WIN32_FIND_DATA fdat;\r
+    char *findfile;\r
+    DirHandle *ret;\r
+\r
+    /* Enumerate files in dir `foo'. */\r
+    findfile = dupcat(name, "/*", NULL);\r
+    h = FindFirstFile(findfile, &fdat);\r
+    if (h == INVALID_HANDLE_VALUE)\r
+       return NULL;\r
+    sfree(findfile);\r
+\r
+    ret = snew(DirHandle);\r
+    ret->h = h;\r
+    ret->name = dupstr(fdat.cFileName);\r
+    return ret;\r
+}\r
+\r
+char *read_filename(DirHandle *dir)\r
+{\r
+    do {\r
+\r
+       if (!dir->name) {\r
+           WIN32_FIND_DATA fdat;\r
+           int ok = FindNextFile(dir->h, &fdat);\r
+           if (!ok)\r
+               return NULL;\r
+           else\r
+               dir->name = dupstr(fdat.cFileName);\r
+       }\r
+\r
+       assert(dir->name);\r
+       if (dir->name[0] == '.' &&\r
+           (dir->name[1] == '\0' ||\r
+            (dir->name[1] == '.' && dir->name[2] == '\0'))) {\r
+           sfree(dir->name);\r
+           dir->name = NULL;\r
+       }\r
+\r
+    } while (!dir->name);\r
+\r
+    if (dir->name) {\r
+       char *ret = dir->name;\r
+       dir->name = NULL;\r
+       return ret;\r
+    } else\r
+       return NULL;\r
+}\r
+\r
+void close_directory(DirHandle *dir)\r
+{\r
+    FindClose(dir->h);\r
+    if (dir->name)\r
+       sfree(dir->name);\r
+    sfree(dir);\r
+}\r
+\r
+int test_wildcard(char *name, int cmdline)\r
+{\r
+    HANDLE fh;\r
+    WIN32_FIND_DATA fdat;\r
+\r
+    /* First see if the exact name exists. */\r
+    if (GetFileAttributes(name) != (DWORD)-1)\r
+       return WCTYPE_FILENAME;\r
+\r
+    /* Otherwise see if a wildcard match finds anything. */\r
+    fh = FindFirstFile(name, &fdat);\r
+    if (fh == INVALID_HANDLE_VALUE)\r
+       return WCTYPE_NONEXISTENT;\r
+\r
+    FindClose(fh);\r
+    return WCTYPE_WILDCARD;\r
+}\r
+\r
+struct WildcardMatcher {\r
+    HANDLE h;\r
+    char *name;\r
+    char *srcpath;\r
+};\r
+\r
+/*\r
+ * Return a pointer to the portion of str that comes after the last\r
+ * slash (or backslash or colon, if `local' is TRUE).\r
+ */\r
+static char *stripslashes(char *str, int local)\r
+{\r
+    char *p;\r
+\r
+    if (local) {\r
+        p = strchr(str, ':');\r
+        if (p) str = p+1;\r
+    }\r
+\r
+    p = strrchr(str, '/');\r
+    if (p) str = p+1;\r
+\r
+    if (local) {\r
+       p = strrchr(str, '\\');\r
+       if (p) str = p+1;\r
+    }\r
+\r
+    return str;\r
+}\r
+\r
+WildcardMatcher *begin_wildcard_matching(char *name)\r
+{\r
+    HANDLE h;\r
+    WIN32_FIND_DATA fdat;\r
+    WildcardMatcher *ret;\r
+    char *last;\r
+\r
+    h = FindFirstFile(name, &fdat);\r
+    if (h == INVALID_HANDLE_VALUE)\r
+       return NULL;\r
+\r
+    ret = snew(WildcardMatcher);\r
+    ret->h = h;\r
+    ret->srcpath = dupstr(name);\r
+    last = stripslashes(ret->srcpath, 1);\r
+    *last = '\0';\r
+    if (fdat.cFileName[0] == '.' &&\r
+       (fdat.cFileName[1] == '\0' ||\r
+        (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))\r
+       ret->name = NULL;\r
+    else\r
+       ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL);\r
+\r
+    return ret;\r
+}\r
+\r
+char *wildcard_get_filename(WildcardMatcher *dir)\r
+{\r
+    while (!dir->name) {\r
+       WIN32_FIND_DATA fdat;\r
+       int ok = FindNextFile(dir->h, &fdat);\r
+\r
+       if (!ok)\r
+           return NULL;\r
+\r
+       if (fdat.cFileName[0] == '.' &&\r
+           (fdat.cFileName[1] == '\0' ||\r
+            (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))\r
+           dir->name = NULL;\r
+       else\r
+           dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL);\r
+    }\r
+\r
+    if (dir->name) {\r
+       char *ret = dir->name;\r
+       dir->name = NULL;\r
+       return ret;\r
+    } else\r
+       return NULL;\r
+}\r
+\r
+void finish_wildcard_matching(WildcardMatcher *dir)\r
+{\r
+    FindClose(dir->h);\r
+    if (dir->name)\r
+       sfree(dir->name);\r
+    sfree(dir->srcpath);\r
+    sfree(dir);\r
+}\r
+\r
+int vet_filename(char *name)\r
+{\r
+    if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ':'))\r
+       return FALSE;\r
+\r
+    if (!name[strspn(name, ".")])      /* entirely composed of dots */\r
+       return FALSE;\r
+\r
+    return TRUE;\r
+}\r
+\r
+int create_directory(char *name)\r
+{\r
+    return CreateDirectory(name, NULL) != 0;\r
+}\r
+\r
+char *dir_file_cat(char *dir, char *file)\r
+{\r
+    return dupcat(dir, "\\", file, NULL);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Platform-specific network handling.\r
+ */\r
+\r
+/*\r
+ * Be told what socket we're supposed to be using.\r
+ */\r
+static SOCKET sftp_ssh_socket = INVALID_SOCKET;\r
+static HANDLE netevent = INVALID_HANDLE_VALUE;\r
+char *do_select(SOCKET skt, int startup)\r
+{\r
+    int events;\r
+    if (startup)\r
+       sftp_ssh_socket = skt;\r
+    else\r
+       sftp_ssh_socket = INVALID_SOCKET;\r
+\r
+    if (p_WSAEventSelect) {\r
+       if (startup) {\r
+           events = (FD_CONNECT | FD_READ | FD_WRITE |\r
+                     FD_OOB | FD_CLOSE | FD_ACCEPT);\r
+           netevent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+       } else {\r
+           events = 0;\r
+       }\r
+       if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {\r
+           switch (p_WSAGetLastError()) {\r
+             case WSAENETDOWN:\r
+               return "Network is down";\r
+             default:\r
+               return "WSAEventSelect(): unknown error";\r
+           }\r
+       }\r
+    }\r
+    return NULL;\r
+}\r
+extern int select_result(WPARAM, LPARAM);\r
+\r
+int do_eventsel_loop(HANDLE other_event)\r
+{\r
+    int n, nhandles, nallhandles, netindex, otherindex;\r
+    long next, ticks;\r
+    HANDLE *handles;\r
+    SOCKET *sklist;\r
+    int skcount;\r
+    long now = GETTICKCOUNT();\r
+\r
+    if (run_timers(now, &next)) {\r
+       ticks = next - GETTICKCOUNT();\r
+       if (ticks < 0) ticks = 0;  /* just in case */\r
+    } else {\r
+       ticks = INFINITE;\r
+    }\r
+\r
+    handles = handle_get_events(&nhandles);\r
+    handles = sresize(handles, nhandles+2, HANDLE);\r
+    nallhandles = nhandles;\r
+\r
+    if (netevent != INVALID_HANDLE_VALUE)\r
+       handles[netindex = nallhandles++] = netevent;\r
+    else\r
+       netindex = -1;\r
+    if (other_event != INVALID_HANDLE_VALUE)\r
+       handles[otherindex = nallhandles++] = other_event;\r
+    else\r
+       otherindex = -1;\r
+\r
+    n = WaitForMultipleObjects(nallhandles, handles, FALSE, ticks);\r
+\r
+    if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {\r
+       handle_got_event(handles[n - WAIT_OBJECT_0]);\r
+    } else if (netindex >= 0 && n == WAIT_OBJECT_0 + netindex) {\r
+       WSANETWORKEVENTS things;\r
+       SOCKET socket;\r
+       extern SOCKET first_socket(int *), next_socket(int *);\r
+       extern int select_result(WPARAM, LPARAM);\r
+       int i, socketstate;\r
+\r
+       /*\r
+        * We must not call select_result() for any socket\r
+        * until we have finished enumerating within the\r
+        * tree. This is because select_result() may close\r
+        * the socket and modify the tree.\r
+        */\r
+       /* Count the active sockets. */\r
+       i = 0;\r
+       for (socket = first_socket(&socketstate);\r
+            socket != INVALID_SOCKET;\r
+            socket = next_socket(&socketstate)) i++;\r
+\r
+       /* Expand the buffer if necessary. */\r
+       sklist = snewn(i, SOCKET);\r
+\r
+       /* Retrieve the sockets into sklist. */\r
+       skcount = 0;\r
+       for (socket = first_socket(&socketstate);\r
+            socket != INVALID_SOCKET;\r
+            socket = next_socket(&socketstate)) {\r
+           sklist[skcount++] = socket;\r
+       }\r
+\r
+       /* Now we're done enumerating; go through the list. */\r
+       for (i = 0; i < skcount; i++) {\r
+           WPARAM wp;\r
+           socket = sklist[i];\r
+           wp = (WPARAM) socket;\r
+           if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {\r
+               static const struct { int bit, mask; } eventtypes[] = {\r
+                   {FD_CONNECT_BIT, FD_CONNECT},\r
+                   {FD_READ_BIT, FD_READ},\r
+                   {FD_CLOSE_BIT, FD_CLOSE},\r
+                   {FD_OOB_BIT, FD_OOB},\r
+                   {FD_WRITE_BIT, FD_WRITE},\r
+                   {FD_ACCEPT_BIT, FD_ACCEPT},\r
+               };\r
+               int e;\r
+\r
+               noise_ultralight(socket);\r
+               noise_ultralight(things.lNetworkEvents);\r
+\r
+               for (e = 0; e < lenof(eventtypes); e++)\r
+                   if (things.lNetworkEvents & eventtypes[e].mask) {\r
+                       LPARAM lp;\r
+                       int err = things.iErrorCode[eventtypes[e].bit];\r
+                       lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);\r
+                       select_result(wp, lp);\r
+                   }\r
+           }\r
+       }\r
+\r
+       sfree(sklist);\r
+    }\r
+\r
+    sfree(handles);\r
+\r
+    if (n == WAIT_TIMEOUT) {\r
+       now = next;\r
+    } else {\r
+       now = GETTICKCOUNT();\r
+    }\r
+\r
+    if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex)\r
+       return 1;\r
+\r
+    return 0;\r
+}\r
+\r
+/*\r
+ * Wait for some network data and process it.\r
+ *\r
+ * We have two variants of this function. One uses select() so that\r
+ * it's compatible with WinSock 1. The other uses WSAEventSelect\r
+ * and MsgWaitForMultipleObjects, so that we can consistently use\r
+ * WSAEventSelect throughout; this enables us to also implement\r
+ * ssh_sftp_get_cmdline() using a parallel mechanism.\r
+ */\r
+int ssh_sftp_loop_iteration(void)\r
+{\r
+    if (p_WSAEventSelect == NULL) {\r
+       fd_set readfds;\r
+       int ret;\r
+       long now = GETTICKCOUNT();\r
+\r
+       if (sftp_ssh_socket == INVALID_SOCKET)\r
+           return -1;                 /* doom */\r
+\r
+       if (socket_writable(sftp_ssh_socket))\r
+           select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE);\r
+\r
+       do {\r
+           long next, ticks;\r
+           struct timeval tv, *ptv;\r
+\r
+           if (run_timers(now, &next)) {\r
+               ticks = next - GETTICKCOUNT();\r
+               if (ticks <= 0)\r
+                   ticks = 1;         /* just in case */\r
+               tv.tv_sec = ticks / 1000;\r
+               tv.tv_usec = ticks % 1000 * 1000;\r
+               ptv = &tv;\r
+           } else {\r
+               ptv = NULL;\r
+           }\r
+\r
+           FD_ZERO(&readfds);\r
+           FD_SET(sftp_ssh_socket, &readfds);\r
+           ret = p_select(1, &readfds, NULL, NULL, ptv);\r
+\r
+           if (ret < 0)\r
+               return -1;                     /* doom */\r
+           else if (ret == 0)\r
+               now = next;\r
+           else\r
+               now = GETTICKCOUNT();\r
+\r
+       } while (ret == 0);\r
+\r
+       select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);\r
+\r
+       return 0;\r
+    } else {\r
+       return do_eventsel_loop(INVALID_HANDLE_VALUE);\r
+    }\r
+}\r
+\r
+/*\r
+ * Read a command line from standard input.\r
+ * \r
+ * In the presence of WinSock 2, we can use WSAEventSelect to\r
+ * mediate between the socket and stdin, meaning we can send\r
+ * keepalives and respond to server events even while waiting at\r
+ * the PSFTP command prompt. Without WS2, we fall back to a simple\r
+ * fgets.\r
+ */\r
+struct command_read_ctx {\r
+    HANDLE event;\r
+    char *line;\r
+};\r
+\r
+static DWORD WINAPI command_read_thread(void *param)\r
+{\r
+    struct command_read_ctx *ctx = (struct command_read_ctx *) param;\r
+\r
+    ctx->line = fgetline(stdin);\r
+\r
+    SetEvent(ctx->event);\r
+\r
+    return 0;\r
+}\r
+\r
+char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok)\r
+{\r
+    int ret;\r
+    struct command_read_ctx actx, *ctx = &actx;\r
+    DWORD threadid;\r
+\r
+    fputs(prompt, stdout);\r
+    fflush(stdout);\r
+\r
+    if ((sftp_ssh_socket == INVALID_SOCKET && no_fds_ok) ||\r
+       p_WSAEventSelect == NULL) {\r
+       return fgetline(stdin);        /* very simple */\r
+    }\r
+\r
+    /*\r
+     * Create a second thread to read from stdin. Process network\r
+     * and timing events until it terminates.\r
+     */\r
+    ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+    ctx->line = NULL;\r
+\r
+    if (!CreateThread(NULL, 0, command_read_thread,\r
+                     ctx, 0, &threadid)) {\r
+       fprintf(stderr, "Unable to create command input thread\n");\r
+       cleanup_exit(1);\r
+    }\r
+\r
+    do {\r
+       ret = do_eventsel_loop(ctx->event);\r
+\r
+       /* Error return can only occur if netevent==NULL, and it ain't. */\r
+       assert(ret >= 0);\r
+    } while (ret == 0);\r
+\r
+    return ctx->line;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Main program. Parse arguments etc.\r
+ */\r
+int main(int argc, char *argv[])\r
+{\r
+    int ret;\r
+\r
+    ret = psftp_main(argc, argv);\r
+\r
+    return ret;\r
+}\r
diff --git a/putty/WINDOWS/WINSTORE.C b/putty/WINDOWS/WINSTORE.C
new file mode 100644 (file)
index 0000000..13ee184
--- /dev/null
@@ -0,0 +1,826 @@
+/*\r
+ * winstore.c: Windows-specific implementation of the interface\r
+ * defined in storage.h.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <limits.h>\r
+#include "putty.h"\r
+#include "storage.h"\r
+\r
+#include <shlobj.h>\r
+#ifndef CSIDL_APPDATA\r
+#define CSIDL_APPDATA 0x001a\r
+#endif\r
+#ifndef CSIDL_LOCAL_APPDATA\r
+#define CSIDL_LOCAL_APPDATA 0x001c\r
+#endif\r
+\r
+static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist";\r
+static const char *const reg_jumplist_value = "Recent sessions";\r
+static const char *const puttystr = PUTTY_REG_POS "\\Sessions";\r
+\r
+static const char hex[16] = "0123456789ABCDEF";\r
+\r
+static int tried_shgetfolderpath = FALSE;\r
+static HMODULE shell32_module = NULL;\r
+DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA, \r
+                     (HWND, int, HANDLE, DWORD, LPSTR));\r
+\r
+static void mungestr(const char *in, char *out)\r
+{\r
+    int candot = 0;\r
+\r
+    while (*in) {\r
+       if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||\r
+           *in == '%' || *in < ' ' || *in > '~' || (*in == '.'\r
+                                                    && !candot)) {\r
+           *out++ = '%';\r
+           *out++ = hex[((unsigned char) *in) >> 4];\r
+           *out++ = hex[((unsigned char) *in) & 15];\r
+       } else\r
+           *out++ = *in;\r
+       in++;\r
+       candot = 1;\r
+    }\r
+    *out = '\0';\r
+    return;\r
+}\r
+\r
+static void unmungestr(const char *in, char *out, int outlen)\r
+{\r
+    while (*in) {\r
+       if (*in == '%' && in[1] && in[2]) {\r
+           int i, j;\r
+\r
+           i = in[1] - '0';\r
+           i -= (i > 9 ? 7 : 0);\r
+           j = in[2] - '0';\r
+           j -= (j > 9 ? 7 : 0);\r
+\r
+           *out++ = (i << 4) + j;\r
+           if (!--outlen)\r
+               return;\r
+           in += 3;\r
+       } else {\r
+           *out++ = *in++;\r
+           if (!--outlen)\r
+               return;\r
+       }\r
+    }\r
+    *out = '\0';\r
+    return;\r
+}\r
+\r
+void *open_settings_w(const char *sessionname, char **errmsg)\r
+{\r
+    HKEY subkey1, sesskey;\r
+    int ret;\r
+    char *p;\r
+\r
+    *errmsg = NULL;\r
+\r
+    if (!sessionname || !*sessionname)\r
+       sessionname = "Default Settings";\r
+\r
+    p = snewn(3 * strlen(sessionname) + 1, char);\r
+    mungestr(sessionname, p);\r
+\r
+    ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1);\r
+    if (ret != ERROR_SUCCESS) {\r
+       sfree(p);\r
+        *errmsg = dupprintf("Unable to create registry key\n"\r
+                            "HKEY_CURRENT_USER\\%s", puttystr);\r
+       return NULL;\r
+    }\r
+    ret = RegCreateKey(subkey1, p, &sesskey);\r
+    RegCloseKey(subkey1);\r
+    if (ret != ERROR_SUCCESS) {\r
+        *errmsg = dupprintf("Unable to create registry key\n"\r
+                            "HKEY_CURRENT_USER\\%s\\%s", puttystr, p);\r
+       sfree(p);\r
+       return NULL;\r
+    }\r
+    sfree(p);\r
+    return (void *) sesskey;\r
+}\r
+\r
+void write_setting_s(void *handle, const char *key, const char *value)\r
+{\r
+    if (handle)\r
+       RegSetValueEx((HKEY) handle, key, 0, REG_SZ, value,\r
+                     1 + strlen(value));\r
+}\r
+\r
+void write_setting_i(void *handle, const char *key, int value)\r
+{\r
+    if (handle)\r
+       RegSetValueEx((HKEY) handle, key, 0, REG_DWORD,\r
+                     (CONST BYTE *) &value, sizeof(value));\r
+}\r
+\r
+void close_settings_w(void *handle)\r
+{\r
+    RegCloseKey((HKEY) handle);\r
+}\r
+\r
+void *open_settings_r(const char *sessionname)\r
+{\r
+    HKEY subkey1, sesskey;\r
+    char *p;\r
+\r
+    if (!sessionname || !*sessionname)\r
+       sessionname = "Default Settings";\r
+\r
+    p = snewn(3 * strlen(sessionname) + 1, char);\r
+    mungestr(sessionname, p);\r
+\r
+    if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) {\r
+       sesskey = NULL;\r
+    } else {\r
+       if (RegOpenKey(subkey1, p, &sesskey) != ERROR_SUCCESS) {\r
+           sesskey = NULL;\r
+       }\r
+       RegCloseKey(subkey1);\r
+    }\r
+\r
+    sfree(p);\r
+\r
+    return (void *) sesskey;\r
+}\r
+\r
+char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)\r
+{\r
+    DWORD type, size;\r
+    size = buflen;\r
+\r
+    if (!handle ||\r
+       RegQueryValueEx((HKEY) handle, key, 0,\r
+                       &type, buffer, &size) != ERROR_SUCCESS ||\r
+       type != REG_SZ) return NULL;\r
+    else\r
+       return buffer;\r
+}\r
+\r
+int read_setting_i(void *handle, const char *key, int defvalue)\r
+{\r
+    DWORD type, val, size;\r
+    size = sizeof(val);\r
+\r
+    if (!handle ||\r
+       RegQueryValueEx((HKEY) handle, key, 0, &type,\r
+                       (BYTE *) &val, &size) != ERROR_SUCCESS ||\r
+       size != sizeof(val) || type != REG_DWORD)\r
+       return defvalue;\r
+    else\r
+       return val;\r
+}\r
+\r
+int read_setting_fontspec(void *handle, const char *name, FontSpec *result)\r
+{\r
+    char *settingname;\r
+    FontSpec ret;\r
+\r
+    if (!read_setting_s(handle, name, ret.name, sizeof(ret.name)))\r
+       return 0;\r
+    settingname = dupcat(name, "IsBold", NULL);\r
+    ret.isbold = read_setting_i(handle, settingname, -1);\r
+    sfree(settingname);\r
+    if (ret.isbold == -1) return 0;\r
+    settingname = dupcat(name, "CharSet", NULL);\r
+    ret.charset = read_setting_i(handle, settingname, -1);\r
+    sfree(settingname);\r
+    if (ret.charset == -1) return 0;\r
+    settingname = dupcat(name, "Height", NULL);\r
+    ret.height = read_setting_i(handle, settingname, INT_MIN);\r
+    sfree(settingname);\r
+    if (ret.height == INT_MIN) return 0;\r
+    *result = ret;\r
+    return 1;\r
+}\r
+\r
+void write_setting_fontspec(void *handle, const char *name, FontSpec font)\r
+{\r
+    char *settingname;\r
+\r
+    write_setting_s(handle, name, font.name);\r
+    settingname = dupcat(name, "IsBold", NULL);\r
+    write_setting_i(handle, settingname, font.isbold);\r
+    sfree(settingname);\r
+    settingname = dupcat(name, "CharSet", NULL);\r
+    write_setting_i(handle, settingname, font.charset);\r
+    sfree(settingname);\r
+    settingname = dupcat(name, "Height", NULL);\r
+    write_setting_i(handle, settingname, font.height);\r
+    sfree(settingname);\r
+}\r
+\r
+int read_setting_filename(void *handle, const char *name, Filename *result)\r
+{\r
+    return !!read_setting_s(handle, name, result->path, sizeof(result->path));\r
+}\r
+\r
+void write_setting_filename(void *handle, const char *name, Filename result)\r
+{\r
+    write_setting_s(handle, name, result.path);\r
+}\r
+\r
+void close_settings_r(void *handle)\r
+{\r
+    RegCloseKey((HKEY) handle);\r
+}\r
+\r
+void del_settings(const char *sessionname)\r
+{\r
+    HKEY subkey1;\r
+    char *p;\r
+\r
+    if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)\r
+       return;\r
+\r
+    p = snewn(3 * strlen(sessionname) + 1, char);\r
+    mungestr(sessionname, p);\r
+    RegDeleteKey(subkey1, p);\r
+    sfree(p);\r
+\r
+    RegCloseKey(subkey1);\r
+\r
+    remove_session_from_jumplist(sessionname);\r
+}\r
+\r
+struct enumsettings {\r
+    HKEY key;\r
+    int i;\r
+};\r
+\r
+void *enum_settings_start(void)\r
+{\r
+    struct enumsettings *ret;\r
+    HKEY key;\r
+\r
+    if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)\r
+       return NULL;\r
+\r
+    ret = snew(struct enumsettings);\r
+    if (ret) {\r
+       ret->key = key;\r
+       ret->i = 0;\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+char *enum_settings_next(void *handle, char *buffer, int buflen)\r
+{\r
+    struct enumsettings *e = (struct enumsettings *) handle;\r
+    char *otherbuf;\r
+    otherbuf = snewn(3 * buflen, char);\r
+    if (RegEnumKey(e->key, e->i++, otherbuf, 3 * buflen) == ERROR_SUCCESS) {\r
+       unmungestr(otherbuf, buffer, buflen);\r
+       sfree(otherbuf);\r
+       return buffer;\r
+    } else {\r
+       sfree(otherbuf);\r
+       return NULL;\r
+    }\r
+}\r
+\r
+void enum_settings_finish(void *handle)\r
+{\r
+    struct enumsettings *e = (struct enumsettings *) handle;\r
+    RegCloseKey(e->key);\r
+    sfree(e);\r
+}\r
+\r
+static void hostkey_regname(char *buffer, const char *hostname,\r
+                           int port, const char *keytype)\r
+{\r
+    int len;\r
+    strcpy(buffer, keytype);\r
+    strcat(buffer, "@");\r
+    len = strlen(buffer);\r
+    len += sprintf(buffer + len, "%d:", port);\r
+    mungestr(hostname, buffer + strlen(buffer));\r
+}\r
+\r
+int verify_host_key(const char *hostname, int port,\r
+                   const char *keytype, const char *key)\r
+{\r
+    char *otherstr, *regname;\r
+    int len;\r
+    HKEY rkey;\r
+    DWORD readlen;\r
+    DWORD type;\r
+    int ret, compare;\r
+\r
+    len = 1 + strlen(key);\r
+\r
+    /*\r
+     * Now read a saved key in from the registry and see what it\r
+     * says.\r
+     */\r
+    otherstr = snewn(len, char);\r
+    regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);\r
+\r
+    hostkey_regname(regname, hostname, port, keytype);\r
+\r
+    if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",\r
+                  &rkey) != ERROR_SUCCESS)\r
+       return 1;                      /* key does not exist in registry */\r
+\r
+    readlen = len;\r
+    ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen);\r
+\r
+    if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&\r
+       !strcmp(keytype, "rsa")) {\r
+       /*\r
+        * Key didn't exist. If the key type is RSA, we'll try\r
+        * another trick, which is to look up the _old_ key format\r
+        * under just the hostname and translate that.\r
+        */\r
+       char *justhost = regname + 1 + strcspn(regname, ":");\r
+       char *oldstyle = snewn(len + 10, char); /* safety margin */\r
+       readlen = len;\r
+       ret = RegQueryValueEx(rkey, justhost, NULL, &type,\r
+                             oldstyle, &readlen);\r
+\r
+       if (ret == ERROR_SUCCESS && type == REG_SZ) {\r
+           /*\r
+            * The old format is two old-style bignums separated by\r
+            * a slash. An old-style bignum is made of groups of\r
+            * four hex digits: digits are ordered in sensible\r
+            * (most to least significant) order within each group,\r
+            * but groups are ordered in silly (least to most)\r
+            * order within the bignum. The new format is two\r
+            * ordinary C-format hex numbers (0xABCDEFG...XYZ, with\r
+            * A nonzero except in the special case 0x0, which\r
+            * doesn't appear anyway in RSA keys) separated by a\r
+            * comma. All hex digits are lowercase in both formats.\r
+            */\r
+           char *p = otherstr;\r
+           char *q = oldstyle;\r
+           int i, j;\r
+\r
+           for (i = 0; i < 2; i++) {\r
+               int ndigits, nwords;\r
+               *p++ = '0';\r
+               *p++ = 'x';\r
+               ndigits = strcspn(q, "/");      /* find / or end of string */\r
+               nwords = ndigits / 4;\r
+               /* now trim ndigits to remove leading zeros */\r
+               while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1)\r
+                   ndigits--;\r
+               /* now move digits over to new string */\r
+               for (j = 0; j < ndigits; j++)\r
+                   p[ndigits - 1 - j] = q[j ^ 3];\r
+               p += ndigits;\r
+               q += nwords * 4;\r
+               if (*q) {\r
+                   q++;               /* eat the slash */\r
+                   *p++ = ',';        /* add a comma */\r
+               }\r
+               *p = '\0';             /* terminate the string */\r
+           }\r
+\r
+           /*\r
+            * Now _if_ this key matches, we'll enter it in the new\r
+            * format. If not, we'll assume something odd went\r
+            * wrong, and hyper-cautiously do nothing.\r
+            */\r
+           if (!strcmp(otherstr, key))\r
+               RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr,\r
+                             strlen(otherstr) + 1);\r
+       }\r
+    }\r
+\r
+    RegCloseKey(rkey);\r
+\r
+    compare = strcmp(otherstr, key);\r
+\r
+    sfree(otherstr);\r
+    sfree(regname);\r
+\r
+    if (ret == ERROR_MORE_DATA ||\r
+       (ret == ERROR_SUCCESS && type == REG_SZ && compare))\r
+       return 2;                      /* key is different in registry */\r
+    else if (ret != ERROR_SUCCESS || type != REG_SZ)\r
+       return 1;                      /* key does not exist in registry */\r
+    else\r
+       return 0;                      /* key matched OK in registry */\r
+}\r
+\r
+void store_host_key(const char *hostname, int port,\r
+                   const char *keytype, const char *key)\r
+{\r
+    char *regname;\r
+    HKEY rkey;\r
+\r
+    regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);\r
+\r
+    hostkey_regname(regname, hostname, port, keytype);\r
+\r
+    if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",\r
+                    &rkey) == ERROR_SUCCESS) {\r
+       RegSetValueEx(rkey, regname, 0, REG_SZ, key, strlen(key) + 1);\r
+       RegCloseKey(rkey);\r
+    } /* else key does not exist in registry */\r
+\r
+    sfree(regname);\r
+}\r
+\r
+/*\r
+ * Open (or delete) the random seed file.\r
+ */\r
+enum { DEL, OPEN_R, OPEN_W };\r
+static int try_random_seed(char const *path, int action, HANDLE *ret)\r
+{\r
+    if (action == DEL) {\r
+       remove(path);\r
+       *ret = INVALID_HANDLE_VALUE;\r
+       return FALSE;                  /* so we'll do the next ones too */\r
+    }\r
+\r
+    *ret = CreateFile(path,\r
+                     action == OPEN_W ? GENERIC_WRITE : GENERIC_READ,\r
+                     action == OPEN_W ? 0 : (FILE_SHARE_READ |\r
+                                             FILE_SHARE_WRITE),\r
+                     NULL,\r
+                     action == OPEN_W ? CREATE_ALWAYS : OPEN_EXISTING,\r
+                     action == OPEN_W ? FILE_ATTRIBUTE_NORMAL : 0,\r
+                     NULL);\r
+\r
+    return (*ret != INVALID_HANDLE_VALUE);\r
+}\r
+\r
+static HANDLE access_random_seed(int action)\r
+{\r
+    HKEY rkey;\r
+    DWORD type, size;\r
+    HANDLE rethandle;\r
+    char seedpath[2 * MAX_PATH + 10] = "\0";\r
+\r
+    /*\r
+     * Iterate over a selection of possible random seed paths until\r
+     * we find one that works.\r
+     * \r
+     * We do this iteration separately for reading and writing,\r
+     * meaning that we will automatically migrate random seed files\r
+     * if a better location becomes available (by reading from the\r
+     * best location in which we actually find one, and then\r
+     * writing to the best location in which we can _create_ one).\r
+     */\r
+\r
+    /*\r
+     * First, try the location specified by the user in the\r
+     * Registry, if any.\r
+     */\r
+    size = sizeof(seedpath);\r
+    if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==\r
+       ERROR_SUCCESS) {\r
+       int ret = RegQueryValueEx(rkey, "RandSeedFile",\r
+                                 0, &type, seedpath, &size);\r
+       if (ret != ERROR_SUCCESS || type != REG_SZ)\r
+           seedpath[0] = '\0';\r
+       RegCloseKey(rkey);\r
+\r
+       if (*seedpath && try_random_seed(seedpath, action, &rethandle))\r
+           return rethandle;\r
+    }\r
+\r
+    /*\r
+     * Next, try the user's local Application Data directory,\r
+     * followed by their non-local one. This is found using the\r
+     * SHGetFolderPath function, which won't be present on all\r
+     * versions of Windows.\r
+     */\r
+    if (!tried_shgetfolderpath) {\r
+       /* This is likely only to bear fruit on systems with IE5+\r
+        * installed, or WinMe/2K+. There is some faffing with\r
+        * SHFOLDER.DLL we could do to try to find an equivalent\r
+        * on older versions of Windows if we cared enough.\r
+        * However, the invocation below requires IE5+ anyway,\r
+        * so stuff that. */\r
+       shell32_module = load_system32_dll("shell32.dll");\r
+       GET_WINDOWS_FUNCTION(shell32_module, SHGetFolderPathA);\r
+       tried_shgetfolderpath = TRUE;\r
+    }\r
+    if (p_SHGetFolderPathA) {\r
+       if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,\r
+                                        NULL, SHGFP_TYPE_CURRENT, seedpath))) {\r
+           strcat(seedpath, "\\PUTTY.RND");\r
+           if (try_random_seed(seedpath, action, &rethandle))\r
+               return rethandle;\r
+       }\r
+\r
+       if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,\r
+                                        NULL, SHGFP_TYPE_CURRENT, seedpath))) {\r
+           strcat(seedpath, "\\PUTTY.RND");\r
+           if (try_random_seed(seedpath, action, &rethandle))\r
+               return rethandle;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Failing that, try %HOMEDRIVE%%HOMEPATH% as a guess at the\r
+     * user's home directory.\r
+     */\r
+    {\r
+       int len, ret;\r
+\r
+       len =\r
+           GetEnvironmentVariable("HOMEDRIVE", seedpath,\r
+                                  sizeof(seedpath));\r
+       ret =\r
+           GetEnvironmentVariable("HOMEPATH", seedpath + len,\r
+                                  sizeof(seedpath) - len);\r
+       if (ret != 0) {\r
+           strcat(seedpath, "\\PUTTY.RND");\r
+           if (try_random_seed(seedpath, action, &rethandle))\r
+               return rethandle;\r
+       }\r
+    }\r
+\r
+    /*\r
+     * And finally, fall back to C:\WINDOWS.\r
+     */\r
+    GetWindowsDirectory(seedpath, sizeof(seedpath));\r
+    strcat(seedpath, "\\PUTTY.RND");\r
+    if (try_random_seed(seedpath, action, &rethandle))\r
+       return rethandle;\r
+\r
+    /*\r
+     * If even that failed, give up.\r
+     */\r
+    return INVALID_HANDLE_VALUE;\r
+}\r
+\r
+void read_random_seed(noise_consumer_t consumer)\r
+{\r
+    HANDLE seedf = access_random_seed(OPEN_R);\r
+\r
+    if (seedf != INVALID_HANDLE_VALUE) {\r
+       while (1) {\r
+           char buf[1024];\r
+           DWORD len;\r
+\r
+           if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)\r
+               consumer(buf, len);\r
+           else\r
+               break;\r
+       }\r
+       CloseHandle(seedf);\r
+    }\r
+}\r
+\r
+void write_random_seed(void *data, int len)\r
+{\r
+    HANDLE seedf = access_random_seed(OPEN_W);\r
+\r
+    if (seedf != INVALID_HANDLE_VALUE) {\r
+       DWORD lenwritten;\r
+\r
+       WriteFile(seedf, data, len, &lenwritten, NULL);\r
+       CloseHandle(seedf);\r
+    }\r
+}\r
+\r
+/*\r
+ * Internal function supporting the jump list registry code. All the\r
+ * functions to add, remove and read the list have substantially\r
+ * similar content, so this is a generalisation of all of them which\r
+ * transforms the list in the registry by prepending 'add' (if\r
+ * non-null), removing 'rem' from what's left (if non-null), and\r
+ * returning the resulting concatenated list of strings in 'out' (if\r
+ * non-null).\r
+ */\r
+static int transform_jumplist_registry\r
+    (const char *add, const char *rem, char **out)\r
+{\r
+    int ret;\r
+    HKEY pjumplist_key, psettings_tmp;\r
+    DWORD type;\r
+    int value_length;\r
+    char *old_value, *new_value;\r
+    char *piterator_old, *piterator_new, *piterator_tmp;\r
+\r
+    ret = RegCreateKeyEx(HKEY_CURRENT_USER, reg_jumplist_key, 0, NULL,\r
+                         REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL,\r
+                         &pjumplist_key, NULL);\r
+    if (ret != ERROR_SUCCESS) {\r
+       return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE;\r
+    }\r
+\r
+    /* Get current list of saved sessions in the registry. */\r
+    value_length = 200;\r
+    old_value = snewn(value_length, char);\r
+    ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,\r
+                          old_value, &value_length);\r
+    /* When the passed buffer is too small, ERROR_MORE_DATA is\r
+     * returned and the required size is returned in the length\r
+     * argument. */\r
+    if (ret == ERROR_MORE_DATA) {\r
+        sfree(old_value);\r
+        old_value = snewn(value_length, char);\r
+        ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,\r
+                              old_value, &value_length);\r
+    }\r
+\r
+    if (ret == ERROR_FILE_NOT_FOUND) {\r
+        /* Value doesn't exist yet. Start from an empty value. */\r
+        *old_value = '\0';\r
+        *(old_value + 1) = '\0';\r
+    } else if (ret != ERROR_SUCCESS) {\r
+        /* Some non-recoverable error occurred. */\r
+        sfree(old_value);\r
+        RegCloseKey(pjumplist_key);\r
+        return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;\r
+    } else if (type != REG_MULTI_SZ) {\r
+        /* The value present in the registry has the wrong type: we\r
+         * try to delete it and start from an empty value. */\r
+        ret = RegDeleteValue(pjumplist_key, reg_jumplist_value);\r
+        if (ret != ERROR_SUCCESS) {\r
+            sfree(old_value);\r
+            RegCloseKey(pjumplist_key);\r
+            return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;\r
+        }\r
+\r
+        *old_value = '\0';\r
+        *(old_value + 1) = '\0';\r
+    }\r
+\r
+    /* Check validity of registry data: REG_MULTI_SZ value must end\r
+     * with \0\0. */\r
+    piterator_tmp = old_value;\r
+    while (((piterator_tmp - old_value) < (value_length - 1)) &&\r
+           !(*piterator_tmp == '\0' && *(piterator_tmp+1) == '\0')) {\r
+        ++piterator_tmp;\r
+    }\r
+\r
+    if ((piterator_tmp - old_value) >= (value_length-1)) {\r
+        /* Invalid value. Start from an empty value. */\r
+        *old_value = '\0';\r
+        *(old_value + 1) = '\0';\r
+    }\r
+\r
+    /*\r
+     * Modify the list, if we're modifying.\r
+     */\r
+    if (add || rem) {\r
+        /* Walk through the existing list and construct the new list of\r
+         * saved sessions. */\r
+        new_value = snewn(value_length + (add ? strlen(add) + 1 : 0), char);\r
+        piterator_new = new_value;\r
+        piterator_old = old_value;\r
+\r
+        /* First add the new item to the beginning of the list. */\r
+        if (add) {\r
+            strcpy(piterator_new, add);\r
+            piterator_new += strlen(piterator_new) + 1;\r
+        }\r
+        /* Now add the existing list, taking care to leave out the removed\r
+         * item, if it was already in the existing list. */\r
+        while (*piterator_old != '\0') {\r
+            if (!rem || strcmp(piterator_old, rem) != 0) {\r
+                /* Check if this is a valid session, otherwise don't add. */\r
+                psettings_tmp = open_settings_r(piterator_old);\r
+                if (psettings_tmp != NULL) {\r
+                    close_settings_r(psettings_tmp);\r
+                    strcpy(piterator_new, piterator_old);\r
+                    piterator_new += strlen(piterator_new) + 1;\r
+                }\r
+            }\r
+            piterator_old += strlen(piterator_old) + 1;\r
+        }\r
+        *piterator_new = '\0';\r
+        ++piterator_new;\r
+\r
+        /* Save the new list to the registry. */\r
+        ret = RegSetValueEx(pjumplist_key, reg_jumplist_value, 0, REG_MULTI_SZ,\r
+                            new_value, piterator_new - new_value);\r
+\r
+        sfree(old_value);\r
+        old_value = new_value;\r
+    } else\r
+        ret = ERROR_SUCCESS;\r
+\r
+    /*\r
+     * Either return or free the result.\r
+     */\r
+    if (out)\r
+        *out = old_value;\r
+    else\r
+        sfree(old_value);\r
+\r
+    /* Clean up and return. */\r
+    RegCloseKey(pjumplist_key);\r
+\r
+    if (ret != ERROR_SUCCESS) {\r
+        return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE;\r
+    } else {\r
+        return JUMPLISTREG_OK;\r
+    }\r
+}\r
+\r
+/* Adds a new entry to the jumplist entries in the registry. */\r
+int add_to_jumplist_registry(const char *item)\r
+{\r
+    return transform_jumplist_registry(item, item, NULL);\r
+}\r
+\r
+/* Removes an item from the jumplist entries in the registry. */\r
+int remove_from_jumplist_registry(const char *item)\r
+{\r
+    return transform_jumplist_registry(NULL, item, NULL);\r
+}\r
+\r
+/* Returns the jumplist entries from the registry. Caller must free\r
+ * the returned pointer. */\r
+char *get_jumplist_registry_entries (void)\r
+{\r
+    char *list_value;\r
+\r
+    if (transform_jumplist_registry(NULL,NULL,&list_value) != ERROR_SUCCESS) {\r
+       list_value = snewn(2, char);\r
+        *list_value = '\0';\r
+        *(list_value + 1) = '\0';\r
+    }\r
+    return list_value;\r
+}\r
+\r
+/*\r
+ * Recursively delete a registry key and everything under it.\r
+ */\r
+static void registry_recursive_remove(HKEY key)\r
+{\r
+    DWORD i;\r
+    char name[MAX_PATH + 1];\r
+    HKEY subkey;\r
+\r
+    i = 0;\r
+    while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {\r
+       if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {\r
+           registry_recursive_remove(subkey);\r
+           RegCloseKey(subkey);\r
+       }\r
+       RegDeleteKey(key, name);\r
+    }\r
+}\r
+\r
+void cleanup_all(void)\r
+{\r
+    HKEY key;\r
+    int ret;\r
+    char name[MAX_PATH + 1];\r
+\r
+    /* ------------------------------------------------------------\r
+     * Wipe out the random seed file, in all of its possible\r
+     * locations.\r
+     */\r
+    access_random_seed(DEL);\r
+\r
+    /* ------------------------------------------------------------\r
+     * Ask Windows to delete any jump list information associated\r
+     * with this installation of PuTTY.\r
+     */\r
+    clear_jumplist();\r
+\r
+    /* ------------------------------------------------------------\r
+     * Destroy all registry information associated with PuTTY.\r
+     */\r
+\r
+    /*\r
+     * Open the main PuTTY registry key and remove everything in it.\r
+     */\r
+    if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==\r
+       ERROR_SUCCESS) {\r
+       registry_recursive_remove(key);\r
+       RegCloseKey(key);\r
+    }\r
+    /*\r
+     * Now open the parent key and remove the PuTTY main key. Once\r
+     * we've done that, see if the parent key has any other\r
+     * children.\r
+     */\r
+    if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,\r
+                  &key) == ERROR_SUCCESS) {\r
+       RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);\r
+       ret = RegEnumKey(key, 0, name, sizeof(name));\r
+       RegCloseKey(key);\r
+       /*\r
+        * If the parent key had no other children, we must delete\r
+        * it in its turn. That means opening the _grandparent_\r
+        * key.\r
+        */\r
+       if (ret != ERROR_SUCCESS) {\r
+           if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,\r
+                          &key) == ERROR_SUCCESS) {\r
+               RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);\r
+               RegCloseKey(key);\r
+           }\r
+       }\r
+    }\r
+    /*\r
+     * Now we're done.\r
+     */\r
+}\r
diff --git a/putty/WINDOWS/WINSTUFF.H b/putty/WINDOWS/WINSTUFF.H
new file mode 100644 (file)
index 0000000..81890a8
--- /dev/null
@@ -0,0 +1,555 @@
+/*\r
+ * winstuff.h: Windows-specific inter-module stuff.\r
+ */\r
+\r
+#ifndef PUTTY_WINSTUFF_H\r
+#define PUTTY_WINSTUFF_H\r
+\r
+#ifndef AUTO_WINSOCK\r
+#include <winsock2.h>\r
+#endif\r
+#include <windows.h>\r
+#include <stdio.h>                    /* for FILENAME_MAX */\r
+\r
+#include "tree234.h"\r
+\r
+#include "winhelp.h"\r
+\r
+struct Filename {\r
+    char path[FILENAME_MAX];\r
+};\r
+#define f_open(filename, mode, isprivate) ( fopen((filename).path, (mode)) )\r
+\r
+struct FontSpec {\r
+    char name[64];\r
+    int isbold;\r
+    int height;\r
+    int charset;\r
+};\r
+\r
+#ifndef CLEARTYPE_QUALITY\r
+#define CLEARTYPE_QUALITY 5\r
+#endif\r
+#define FONT_QUALITY(fq) ( \\r
+    (fq) == FQ_DEFAULT ? DEFAULT_QUALITY : \\r
+    (fq) == FQ_ANTIALIASED ? ANTIALIASED_QUALITY : \\r
+    (fq) == FQ_NONANTIALIASED ? NONANTIALIASED_QUALITY : \\r
+    CLEARTYPE_QUALITY)\r
+\r
+#define PLATFORM_IS_UTF16 /* enable UTF-16 processing when exchanging\r
+                          * wchar_t strings with environment */\r
+\r
+/*\r
+ * Where we can, we use GetWindowLongPtr and friends because they're\r
+ * more useful on 64-bit platforms, but they're a relatively recent\r
+ * innovation, missing from VC++ 6 and older MinGW.  Degrade nicely.\r
+ * (NB that on some systems, some of these things are available but\r
+ * not others...)\r
+ */\r
+\r
+#ifndef GCLP_HCURSOR\r
+/* GetClassLongPtr and friends */\r
+#undef  GetClassLongPtr\r
+#define GetClassLongPtr GetClassLong\r
+#undef  SetClassLongPtr\r
+#define SetClassLongPtr SetClassLong\r
+#define GCLP_HCURSOR GCL_HCURSOR\r
+/* GetWindowLongPtr and friends */\r
+#undef  GetWindowLongPtr\r
+#define GetWindowLongPtr GetWindowLong\r
+#undef  SetWindowLongPtr\r
+#define SetWindowLongPtr SetWindowLong\r
+#undef  GWLP_USERDATA\r
+#define GWLP_USERDATA GWL_USERDATA\r
+#undef  DWLP_MSGRESULT\r
+#define DWLP_MSGRESULT DWL_MSGRESULT\r
+/* Since we've clobbered the above functions, we should clobber the\r
+ * associated type regardless of whether it's defined. */\r
+#undef LONG_PTR\r
+#define LONG_PTR LONG\r
+#endif\r
+\r
+#define BOXFLAGS DLGWINDOWEXTRA\r
+#define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR))\r
+#define DF_END 0x0001\r
+\r
+/*\r
+ * Dynamically linked functions. These come in two flavours:\r
+ *\r
+ *  - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor,\r
+ *    so will always dynamically link against exactly what is specified\r
+ *    in "name". If you're not sure, use this one.\r
+ *\r
+ *  - GET_WINDOWS_FUNCTION_PP allows "name" to be redirected via\r
+ *    preprocessor definitions like "#define foo bar"; this is principally\r
+ *    intended for the ANSI/Unicode DoSomething/DoSomethingA/DoSomethingW.\r
+ *    If your function has an argument of type "LPTSTR" or similar, this\r
+ *    is the variant to use.\r
+ *    (However, it can't always be used, as it trips over more complicated\r
+ *    macro trickery such as the WspiapiGetAddrInfo wrapper for getaddrinfo.)\r
+ *\r
+ * (DECL_WINDOWS_FUNCTION works with both these variants.)\r
+ */\r
+#define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \\r
+    typedef rettype (WINAPI *t_##name) params; \\r
+    linkage t_##name p_##name\r
+#define STR1(x) #x\r
+#define STR(x) STR1(x)\r
+#define GET_WINDOWS_FUNCTION_PP(module, name) \\r
+    (p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL)\r
+#define GET_WINDOWS_FUNCTION(module, name) \\r
+    (p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL)\r
+\r
+/*\r
+ * Global variables. Most modules declare these `extern', but\r
+ * window.c will do `#define PUTTY_DO_GLOBALS' before including this\r
+ * module, and so will get them properly defined.\r
+*/\r
+#ifndef GLOBAL\r
+#ifdef PUTTY_DO_GLOBALS\r
+#define GLOBAL\r
+#else\r
+#define GLOBAL extern\r
+#endif\r
+#endif\r
+\r
+#ifndef DONE_TYPEDEFS\r
+#define DONE_TYPEDEFS\r
+typedef struct config_tag Config;\r
+typedef struct backend_tag Backend;\r
+typedef struct terminal_tag Terminal;\r
+#endif\r
+\r
+#define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY"\r
+#define PUTTY_REG_PARENT "Software\\SimonTatham"\r
+#define PUTTY_REG_PARENT_CHILD "PuTTY"\r
+#define PUTTY_REG_GPARENT "Software"\r
+#define PUTTY_REG_GPARENT_CHILD "SimonTatham"\r
+\r
+/* Result values for the jumplist registry functions. */\r
+#define JUMPLISTREG_OK 0\r
+#define JUMPLISTREG_ERROR_INVALID_PARAMETER 1\r
+#define JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE 2\r
+#define JUMPLISTREG_ERROR_VALUEREAD_FAILURE 3\r
+#define JUMPLISTREG_ERROR_VALUEWRITE_FAILURE 4\r
+#define JUMPLISTREG_ERROR_INVALID_VALUE 5\r
+\r
+#define PUTTY_HELP_FILE "putty.hlp"\r
+#define PUTTY_CHM_FILE "putty.chm"\r
+#define PUTTY_HELP_CONTENTS "putty.cnt"\r
+\r
+#define GETTICKCOUNT GetTickCount\r
+#define CURSORBLINK GetCaretBlinkTime()\r
+#define TICKSPERSEC 1000              /* GetTickCount returns milliseconds */\r
+\r
+#define DEFAULT_CODEPAGE CP_ACP\r
+\r
+typedef HDC Context;\r
+\r
+typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */\r
+#define PUTTY_UINT32_DEFINED\r
+\r
+#ifndef NO_GSSAPI\r
+/*\r
+ * GSS-API stuff\r
+ */\r
+#define GSS_CC CALLBACK\r
+/*\r
+typedef struct Ssh_gss_buf {\r
+    size_t length;\r
+    char *value;\r
+} Ssh_gss_buf;\r
+\r
+#define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL}\r
+typedef void *Ssh_gss_name;\r
+*/\r
+#endif\r
+\r
+/*\r
+ * Window handles for the windows that can be running during a\r
+ * PuTTY session.\r
+ */\r
+GLOBAL HWND hwnd;      /* the main terminal window */\r
+GLOBAL HWND logbox;\r
+\r
+/*\r
+ * The all-important instance handle.\r
+ */\r
+GLOBAL HINSTANCE hinst;\r
+\r
+/*\r
+ * Help file stuff in winhelp.c.\r
+ */\r
+void init_help(void);\r
+void shutdown_help(void);\r
+int has_help(void);\r
+void launch_help(HWND hwnd, const char *topic);\r
+void quit_help(HWND hwnd);\r
+\r
+/*\r
+ * The terminal and logging context are notionally local to the\r
+ * Windows front end, but they must be shared between window.c and\r
+ * windlg.c. Likewise the saved-sessions list.\r
+ */\r
+GLOBAL Terminal *term;\r
+GLOBAL void *logctx;\r
+\r
+#define WM_NETEVENT  (WM_APP + 5)\r
+\r
+/*\r
+ * On Windows, we send MA_2CLK as the only event marking the second\r
+ * press of a mouse button. Compare unix.h.\r
+ */\r
+#define MULTICLICK_ONLY_EVENT 1\r
+\r
+/*\r
+ * On Windows, data written to the clipboard must be NUL-terminated.\r
+ */\r
+#define SELECTION_NUL_TERMINATED 1\r
+\r
+/*\r
+ * On Windows, copying to the clipboard terminates lines with CRLF.\r
+ */\r
+#define SEL_NL { 13, 10 }\r
+\r
+/*\r
+ * sk_getxdmdata() does not exist under Windows (not that I\r
+ * couldn't write it if I wanted to, but I haven't bothered), so\r
+ * it's a macro which always returns NULL. With any luck this will\r
+ * cause the compiler to notice it can optimise away the\r
+ * implementation of XDM-AUTHORIZATION-1 in x11fwd.c :-)\r
+ */\r
+#define sk_getxdmdata(socket, lenp) (NULL)\r
+\r
+/*\r
+ * File-selector filter strings used in the config box. On Windows,\r
+ * these strings are of exactly the type needed to go in\r
+ * `lpstrFilter' in an OPENFILENAME structure.\r
+ */\r
+#define FILTER_KEY_FILES ("PuTTY Private Key Files (*.ppk)\0*.ppk\0" \\r
+                             "All Files (*.*)\0*\0\0\0")\r
+#define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \\r
+                              "All Files (*.*)\0*\0\0\0")\r
+#define FILTER_DYNLIB_FILES ("Dynamic Library Files (*.dll)\0*.dll\0" \\r
+                                "All Files (*.*)\0*\0\0\0")\r
+\r
+/*\r
+ * On some versions of Windows, it has been known for WM_TIMER to\r
+ * occasionally get its callback time simply wrong, and call us\r
+ * back several minutes early. Defining these symbols enables\r
+ * compensation code in timing.c.\r
+ */\r
+#define TIMING_SYNC\r
+#define TIMING_SYNC_TICKCOUNT\r
+\r
+/*\r
+ * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on\r
+ * what it can get, which means any WinSock routines used outside\r
+ * that module must be exported from it as function pointers. So\r
+ * here they are.\r
+ */\r
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAAsyncSelect,\r
+                     (SOCKET, HWND, u_int, long));\r
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEventSelect,\r
+                     (SOCKET, WSAEVENT, long));\r
+DECL_WINDOWS_FUNCTION(GLOBAL, int, select,\r
+                     (int, fd_set FAR *, fd_set FAR *,\r
+                      fd_set FAR *, const struct timeval FAR *));\r
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAGetLastError, (void));\r
+DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEnumNetworkEvents,\r
+                     (SOCKET, WSAEVENT, LPWSANETWORKEVENTS));\r
+\r
+extern int socket_writable(SOCKET skt);\r
+\r
+extern void socket_reselect_all(void);\r
+\r
+/*\r
+ * Exports from winctrls.c.\r
+ */\r
+\r
+struct ctlpos {\r
+    HWND hwnd;\r
+    WPARAM font;\r
+    int dlu4inpix;\r
+    int ypos, width;\r
+    int xoff;\r
+    int boxystart, boxid;\r
+    char *boxtext;\r
+};\r
+\r
+/*\r
+ * Exports from winutils.c.\r
+ */\r
+typedef struct filereq_tag filereq; /* cwd for file requester */\r
+BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save);\r
+filereq *filereq_new(void);\r
+void filereq_free(filereq *state);\r
+int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid);\r
+void split_into_argv(char *, int *, char ***, char ***);\r
+\r
+/*\r
+ * Private structure for prefslist state. Only in the header file\r
+ * so that we can delegate allocation to callers.\r
+ */\r
+struct prefslist {\r
+    int listid, upbid, dnbid;\r
+    int srcitem;\r
+    int dummyitem;\r
+    int dragging;\r
+};\r
+\r
+/*\r
+ * This structure is passed to event handler functions as the `dlg'\r
+ * parameter, and hence is passed back to winctrls access functions.\r
+ */\r
+struct dlgparam {\r
+    HWND hwnd;                        /* the hwnd of the dialog box */\r
+    struct winctrls *controltrees[8];  /* can have several of these */\r
+    int nctrltrees;\r
+    char *wintitle;                   /* title of actual window */\r
+    char *errtitle;                   /* title of error sub-messageboxes */\r
+    void *data;                               /* data to pass in refresh events */\r
+    union control *focused, *lastfocused; /* which ctrl has focus now/before */\r
+    char shortcuts[128];              /* track which shortcuts in use */\r
+    int coloursel_wanted;             /* has an event handler asked for\r
+                                       * a colour selector? */\r
+    struct { unsigned char r, g, b, ok; } coloursel_result;   /* 0-255 */\r
+    tree234 *privdata;                /* stores per-control private data */\r
+    int ended, endresult;             /* has the dialog been ended? */\r
+    int fixed_pitch_fonts;             /* are we constrained to fixed fonts? */\r
+};\r
+\r
+/*\r
+ * Exports from winctrls.c.\r
+ */\r
+void ctlposinit(struct ctlpos *cp, HWND hwnd,\r
+               int leftborder, int rightborder, int topborder);\r
+HWND doctl(struct ctlpos *cp, RECT r,\r
+          char *wclass, int wstyle, int exstyle, char *wtext, int wid);\r
+void bartitle(struct ctlpos *cp, char *name, int id);\r
+void beginbox(struct ctlpos *cp, char *name, int idbox);\r
+void endbox(struct ctlpos *cp);\r
+void editboxfw(struct ctlpos *cp, int password, char *text,\r
+              int staticid, int editid);\r
+void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...);\r
+void bareradioline(struct ctlpos *cp, int nacross, ...);\r
+void radiobig(struct ctlpos *cp, char *text, int id, ...);\r
+void checkbox(struct ctlpos *cp, char *text, int id);\r
+void statictext(struct ctlpos *cp, char *text, int lines, int id);\r
+void staticbtn(struct ctlpos *cp, char *stext, int sid,\r
+              char *btext, int bid);\r
+void static2btn(struct ctlpos *cp, char *stext, int sid,\r
+               char *btext1, int bid1, char *btext2, int bid2);\r
+void staticedit(struct ctlpos *cp, char *stext,\r
+               int sid, int eid, int percentedit);\r
+void staticddl(struct ctlpos *cp, char *stext,\r
+              int sid, int lid, int percentlist);\r
+void combobox(struct ctlpos *cp, char *text, int staticid, int listid);\r
+void staticpassedit(struct ctlpos *cp, char *stext,\r
+                   int sid, int eid, int percentedit);\r
+void bigeditctrl(struct ctlpos *cp, char *stext,\r
+                int sid, int eid, int lines);\r
+void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id);\r
+void editbutton(struct ctlpos *cp, char *stext, int sid,\r
+               int eid, char *btext, int bid);\r
+void sesssaver(struct ctlpos *cp, char *text,\r
+              int staticid, int editid, int listid, ...);\r
+void envsetter(struct ctlpos *cp, char *stext, int sid,\r
+              char *e1stext, int e1sid, int e1id,\r
+              char *e2stext, int e2sid, int e2id,\r
+              int listid, char *b1text, int b1id, char *b2text, int b2id);\r
+void charclass(struct ctlpos *cp, char *stext, int sid, int listid,\r
+              char *btext, int bid, int eid, char *s2text, int s2id);\r
+void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,\r
+               char *btext, int bid, ...);\r
+void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines,\r
+              char *stext, int sid, int listid, int upbid, int dnbid);\r
+int handle_prefslist(struct prefslist *hdl,\r
+                    int *array, int maxmemb,\r
+                    int is_dlmsg, HWND hwnd,\r
+                    WPARAM wParam, LPARAM lParam);\r
+void progressbar(struct ctlpos *cp, int id);\r
+void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,\r
+              char *e1stext, int e1sid, int e1id,\r
+              char *e2stext, int e2sid, int e2id,\r
+              char *btext, int bid,\r
+              char *r1text, int r1id, char *r2text, int r2id);\r
+\r
+void dlg_auto_set_fixed_pitch_flag(void *dlg);\r
+int dlg_get_fixed_pitch_flag(void *dlg);\r
+void dlg_set_fixed_pitch_flag(void *dlg, int flag);\r
+\r
+#define MAX_SHORTCUTS_PER_CTRL 16\r
+\r
+/*\r
+ * This structure is what's stored for each `union control' in the\r
+ * portable-dialog interface.\r
+ */\r
+struct winctrl {\r
+    union control *ctrl;\r
+    /*\r
+     * The control may have several components at the Windows\r
+     * level, with different dialog IDs. To avoid needing N\r
+     * separate platformsidectrl structures (which could be stored\r
+     * separately in a tree234 so that lookup by ID worked), we\r
+     * impose the constraint that those IDs must be in a contiguous\r
+     * block.\r
+     */\r
+    int base_id;\r
+    int num_ids;\r
+    /*\r
+     * Remember what keyboard shortcuts were used by this control,\r
+     * so that when we remove it again we can take them out of the\r
+     * list in the dlgparam.\r
+     */\r
+    char shortcuts[MAX_SHORTCUTS_PER_CTRL];\r
+    /*\r
+     * Some controls need a piece of allocated memory in which to\r
+     * store temporary data about the control.\r
+     */\r
+    void *data;\r
+};\r
+/*\r
+ * And this structure holds a set of the above, in two separate\r
+ * tree234s so that it can find an item by `union control' or by\r
+ * dialog ID.\r
+ */\r
+struct winctrls {\r
+    tree234 *byctrl, *byid;\r
+};\r
+struct controlset;\r
+struct controlbox;\r
+\r
+void winctrl_init(struct winctrls *);\r
+void winctrl_cleanup(struct winctrls *);\r
+void winctrl_add(struct winctrls *, struct winctrl *);\r
+void winctrl_remove(struct winctrls *, struct winctrl *);\r
+struct winctrl *winctrl_findbyctrl(struct winctrls *, union control *);\r
+struct winctrl *winctrl_findbyid(struct winctrls *, int);\r
+struct winctrl *winctrl_findbyindex(struct winctrls *, int);\r
+void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,\r
+                   struct ctlpos *cp, struct controlset *s, int *id);\r
+int winctrl_handle_command(struct dlgparam *dp, UINT msg,\r
+                          WPARAM wParam, LPARAM lParam);\r
+void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c);\r
+int winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id);\r
+\r
+void dp_init(struct dlgparam *dp);\r
+void dp_add_tree(struct dlgparam *dp, struct winctrls *tree);\r
+void dp_cleanup(struct dlgparam *dp);\r
+\r
+/*\r
+ * Exports from wincfg.c.\r
+ */\r
+void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,\r
+                         int midsession, int protocol);\r
+\r
+/*\r
+ * Exports from windlg.c.\r
+ */\r
+void defuse_showwindow(void);\r
+int do_config(void);\r
+int do_reconfig(HWND, int);\r
+void showeventlog(HWND);\r
+void showabout(HWND);\r
+void force_normal(HWND hwnd);\r
+void modal_about_box(HWND hwnd);\r
+void show_help(HWND hwnd);\r
+\r
+/*\r
+ * Exports from winmisc.c.\r
+ */\r
+extern OSVERSIONINFO osVersion;\r
+BOOL init_winver(void);\r
+HMODULE load_system32_dll(const char *libname);\r
+\r
+/*\r
+ * Exports from sizetip.c.\r
+ */\r
+void UpdateSizeTip(HWND src, int cx, int cy);\r
+void EnableSizeTip(int bEnable);\r
+\r
+/*\r
+ * Exports from unicode.c.\r
+ */\r
+struct unicode_data;\r
+void init_ucs(Config *, struct unicode_data *);\r
+\r
+/*\r
+ * Exports from winhandl.c.\r
+ */\r
+#define HANDLE_FLAG_OVERLAPPED 1\r
+#define HANDLE_FLAG_IGNOREEOF 2\r
+#define HANDLE_FLAG_UNITBUFFER 4\r
+struct handle;\r
+typedef int (*handle_inputfn_t)(struct handle *h, void *data, int len);\r
+typedef void (*handle_outputfn_t)(struct handle *h, int new_backlog);\r
+struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,\r
+                               void *privdata, int flags);\r
+struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,\r
+                                void *privdata, int flags);\r
+int handle_write(struct handle *h, const void *data, int len);\r
+HANDLE *handle_get_events(int *nevents);\r
+void handle_free(struct handle *h);\r
+void handle_got_event(HANDLE event);\r
+void handle_unthrottle(struct handle *h, int backlog);\r
+int handle_backlog(struct handle *h);\r
+void *handle_get_privdata(struct handle *h);\r
+\r
+/*\r
+ * winpgntc.c needs to schedule callbacks for asynchronous agent\r
+ * requests. This has to be done differently in GUI and console, so\r
+ * there's an exported function used for the purpose.\r
+ * \r
+ * Also, we supply FLAG_SYNCAGENT to force agent requests to be\r
+ * synchronous in pscp and psftp.\r
+ */\r
+void agent_schedule_callback(void (*callback)(void *, void *, int),\r
+                            void *callback_ctx, void *data, int len);\r
+#define FLAG_SYNCAGENT 0x1000\r
+\r
+/*\r
+ * winpgntc.c also exports these two functions which are used by the\r
+ * server side of Pageant as well, to get the user SID for comparing\r
+ * with clients'.\r
+ */\r
+int init_advapi(void);  /* initialises everything needed by get_user_sid */\r
+PSID get_user_sid(void);\r
+\r
+/*\r
+ * Exports from winser.c.\r
+ */\r
+extern Backend serial_backend;\r
+\r
+/*\r
+ * Exports from winjump.c.\r
+ */\r
+#define JUMPLIST_SUPPORTED             /* suppress #defines in putty.h */\r
+void add_session_to_jumplist(const char * const sessionname);\r
+void remove_session_from_jumplist(const char * const sessionname);\r
+void clear_jumplist(void);\r
+\r
+/*\r
+ * Extra functions in winstore.c over and above the interface in\r
+ * storage.h.\r
+ *\r
+ * These functions manipulate the Registry section which mirrors the\r
+ * current Windows 7 jump list. (Because the real jump list storage is\r
+ * write-only, we need to keep another copy of whatever we put in it,\r
+ * so that we can put in a slightly modified version the next time.)\r
+ */\r
+\r
+/* Adds a saved session to the registry jump list mirror. 'item' is a\r
+ * string naming a saved session. */\r
+int add_to_jumplist_registry(const char *item);\r
+\r
+/* Removes an item from the registry jump list mirror. */\r
+int remove_from_jumplist_registry(const char *item);\r
+\r
+/* Returns the current jump list entries from the registry. Caller\r
+ * must free the returned pointer, which points to a contiguous\r
+ * sequence of NUL-terminated strings in memory, terminated with an\r
+ * empty one. */\r
+char *get_jumplist_registry_entries(void);\r
+\r
+#endif\r
diff --git a/putty/WINDOWS/WINTIME.C b/putty/WINDOWS/WINTIME.C
new file mode 100644 (file)
index 0000000..99564d2
--- /dev/null
@@ -0,0 +1,24 @@
+/*\r
+ * wintime.c - Avoid trouble with time() returning (time_t)-1 on Windows.\r
+ */\r
+\r
+#include "putty.h"\r
+#include <time.h>\r
+\r
+struct tm ltime(void)\r
+{\r
+    SYSTEMTIME st;\r
+    struct tm tm;\r
+\r
+    GetLocalTime(&st);\r
+    tm.tm_sec=st.wSecond;\r
+    tm.tm_min=st.wMinute;\r
+    tm.tm_hour=st.wHour;\r
+    tm.tm_mday=st.wDay;\r
+    tm.tm_mon=st.wMonth-1;\r
+    tm.tm_year=(st.wYear>=1900?st.wYear-1900:0);\r
+    tm.tm_wday=st.wDayOfWeek;\r
+    tm.tm_yday=-1; /* GetLocalTime doesn't tell us */\r
+    tm.tm_isdst=0; /* GetLocalTime doesn't tell us */\r
+    return tm;\r
+}\r
diff --git a/putty/WINDOWS/WINUCS.C b/putty/WINDOWS/WINUCS.C
new file mode 100644 (file)
index 0000000..1b72147
--- /dev/null
@@ -0,0 +1,1251 @@
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <time.h>\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+#include "terminal.h"\r
+#include "misc.h"\r
+\r
+/* Character conversion arrays; they are usually taken from windows,\r
+ * the xterm one has the four scanlines that have no unicode 2.0\r
+ * equivalents mapped to their unicode 3.0 locations.\r
+ */\r
+static const WCHAR unitab_xterm_std[32] = {\r
+    0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,\r
+    0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,\r
+    0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,\r
+    0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020\r
+};\r
+\r
+/*\r
+ * If the codepage is non-zero it's a window codepage, zero means use a\r
+ * local codepage. The name is always converted to the first of any\r
+ * duplicate definitions.\r
+ */\r
+\r
+/* \r
+ * Tables for ISO-8859-{1-10,13-16} derived from those downloaded\r
+ * 2001-10-02 from <http://www.unicode.org/Public/MAPPINGS/> -- jtn\r
+ * Table for ISO-8859-11 derived from same on 2002-11-18. -- bjh21\r
+ */\r
+\r
+/* XXX: This could be done algorithmically, but I'm not sure it's\r
+ *      worth the hassle -- jtn */\r
+/* ISO/IEC 8859-1:1998 (Latin-1, "Western", "West European") */\r
+static const wchar_t iso_8859_1[] = {\r
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,\r
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+    0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,\r
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,\r
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+    0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,\r
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF\r
+};\r
+\r
+/* ISO/IEC 8859-2:1999 (Latin-2, "Central European", "East European") */\r
+static const wchar_t iso_8859_2[] = {\r
+    0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7,\r
+    0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,\r
+    0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7,\r
+    0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,\r
+    0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,\r
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,\r
+    0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,\r
+    0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,\r
+    0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,\r
+    0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,\r
+    0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,\r
+    0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9\r
+};\r
+\r
+/* ISO/IEC 8859-3:1999 (Latin-3, "South European", "Maltese & Esperanto") */\r
+static const wchar_t iso_8859_3[] = {\r
+    0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xFFFD, 0x0124, 0x00A7,\r
+    0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0xFFFD, 0x017B,\r
+    0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7,\r
+    0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0xFFFD, 0x017C,\r
+    0x00C0, 0x00C1, 0x00C2, 0xFFFD, 0x00C4, 0x010A, 0x0108, 0x00C7,\r
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+    0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7,\r
+    0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF,\r
+    0x00E0, 0x00E1, 0x00E2, 0xFFFD, 0x00E4, 0x010B, 0x0109, 0x00E7,\r
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+    0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7,\r
+    0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9\r
+};\r
+\r
+/* ISO/IEC 8859-4:1998 (Latin-4, "North European") */\r
+static const wchar_t iso_8859_4[] = {\r
+    0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7,\r
+    0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF,\r
+    0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7,\r
+    0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B,\r
+    0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E,\r
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A,\r
+    0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7,\r
+    0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF,\r
+    0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F,\r
+    0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B,\r
+    0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7,\r
+    0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9\r
+};\r
+\r
+/* ISO/IEC 8859-5:1999 (Latin/Cyrillic) */\r
+static const wchar_t iso_8859_5[] = {\r
+    0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,\r
+    0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F,\r
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,\r
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+    0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,\r
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+    0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,\r
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+    0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,\r
+    0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,\r
+    0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F\r
+};\r
+\r
+/* ISO/IEC 8859-6:1999 (Latin/Arabic) */\r
+static const wchar_t iso_8859_6[] = {\r
+    0x00A0, 0xFFFD, 0xFFFD, 0xFFFD, 0x00A4, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x060C, 0x00AD, 0xFFFD, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0x061B, 0xFFFD, 0xFFFD, 0xFFFD, 0x061F,\r
+    0xFFFD, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,\r
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,\r
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637,\r
+    0x0638, 0x0639, 0x063A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647,\r
+    0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F,\r
+    0x0650, 0x0651, 0x0652, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD\r
+};\r
+\r
+/* ISO 8859-7:1987 (Latin/Greek) */\r
+static const wchar_t iso_8859_7[] = {\r
+    0x00A0, 0x2018, 0x2019, 0x00A3, 0xFFFD, 0xFFFD, 0x00A6, 0x00A7,\r
+    0x00A8, 0x00A9, 0xFFFD, 0x00AB, 0x00AC, 0x00AD, 0xFFFD, 0x2015,\r
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7,\r
+    0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,\r
+    0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,\r
+    0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,\r
+    0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,\r
+    0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,\r
+    0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,\r
+    0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,\r
+    0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,\r
+    0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0xFFFD\r
+};\r
+\r
+/* ISO/IEC 8859-8:1999 (Latin/Hebrew) */\r
+static const wchar_t iso_8859_8[] = {\r
+    0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+    0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+    0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x2017,\r
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,\r
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,\r
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,\r
+    0x05E8, 0x05E9, 0x05EA, 0xFFFD, 0xFFFD, 0x200E, 0x200F, 0xFFFD\r
+};\r
+\r
+/* ISO/IEC 8859-9:1999 (Latin-5, "Turkish") */\r
+static const wchar_t iso_8859_9[] = {\r
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,\r
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+    0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,\r
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,\r
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+    0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,\r
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF\r
+};\r
+\r
+/* ISO/IEC 8859-10:1998 (Latin-6, "Nordic" [Sami, Inuit, Icelandic]) */\r
+static const wchar_t iso_8859_10[] = {\r
+    0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7,\r
+    0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A,\r
+    0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7,\r
+    0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B,\r
+    0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E,\r
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF,\r
+    0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168,\r
+    0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,\r
+    0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F,\r
+    0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF,\r
+    0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169,\r
+    0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138\r
+};\r
+\r
+/* ISO/IEC 8859-11:2001 ("Thai", "TIS620") */\r
+static const wchar_t iso_8859_11[] = {\r
+    0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,\r
+    0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,\r
+    0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,\r
+    0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,\r
+    0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,\r
+    0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,\r
+    0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,\r
+    0x0E38, 0x0E39, 0x0E3A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0E3F,\r
+    0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,\r
+    0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,\r
+    0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,\r
+    0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD\r
+};\r
+\r
+/* ISO/IEC 8859-13:1998 (Latin-7, "Baltic Rim") */\r
+static const wchar_t iso_8859_13[] = {\r
+    0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7,\r
+    0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,\r
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7,\r
+    0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,\r
+    0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,\r
+    0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,\r
+    0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,\r
+    0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,\r
+    0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,\r
+    0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,\r
+    0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,\r
+    0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019\r
+};\r
+\r
+/* ISO/IEC 8859-14:1998 (Latin-8, "Celtic", "Gaelic/Welsh") */\r
+static const wchar_t iso_8859_14[] = {\r
+    0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7,\r
+    0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178,\r
+    0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56,\r
+    0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61,\r
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+    0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A,\r
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,\r
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+    0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B,\r
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF\r
+};\r
+\r
+/* ISO/IEC 8859-15:1999 (Latin-9 aka -0, "euro") */\r
+static const wchar_t iso_8859_15[] = {\r
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7,\r
+    0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7,\r
+    0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF,\r
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+    0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,\r
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,\r
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+    0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,\r
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF\r
+};\r
+\r
+/* ISO/IEC 8859-16:2001 (Latin-10, "Balkan") */\r
+static const wchar_t iso_8859_16[] = {\r
+    0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00A7,\r
+    0x0161, 0x00A9, 0x0218, 0x00AB, 0x0179, 0x00AD, 0x017A, 0x017B,\r
+    0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7,\r
+    0x017E, 0x010D, 0x0219, 0x00BB, 0x0152, 0x0153, 0x0178, 0x017C,\r
+    0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7,\r
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+    0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A,\r
+    0x0170, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0118, 0x021A, 0x00DF,\r
+    0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7,\r
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+    0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B,\r
+    0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF\r
+};\r
+\r
+static const wchar_t roman8[] = {\r
+    0x00A0, 0x00C0, 0x00C2, 0x00C8, 0x00CA, 0x00CB, 0x00CE, 0x00CF,\r
+    0x00B4, 0x02CB, 0x02C6, 0x00A8, 0x02DC, 0x00D9, 0x00DB, 0x20A4,\r
+    0x00AF, 0x00DD, 0x00FD, 0x00B0, 0x00C7, 0x00E7, 0x00D1, 0x00F1,\r
+    0x00A1, 0x00BF, 0x00A4, 0x00A3, 0x00A5, 0x00A7, 0x0192, 0x00A2,\r
+    0x00E2, 0x00EA, 0x00F4, 0x00FB, 0x00E1, 0x00E9, 0x00F3, 0x00FA,\r
+    0x00E0, 0x00E8, 0x00F2, 0x00F9, 0x00E4, 0x00EB, 0x00F6, 0x00FC,\r
+    0x00C5, 0x00EE, 0x00D8, 0x00C6, 0x00E5, 0x00ED, 0x00F8, 0x00E6,\r
+    0x00C4, 0x00EC, 0x00D6, 0x00DC, 0x00C9, 0x00EF, 0x00DF, 0x00D4,\r
+    0x00C1, 0x00C3, 0x00E3, 0x00D0, 0x00F0, 0x00CD, 0x00CC, 0x00D3,\r
+    0x00D2, 0x00D5, 0x00F5, 0x0160, 0x0161, 0x00DA, 0x0178, 0x00FF,\r
+    0x00DE, 0x00FE, 0x00B7, 0x00B5, 0x00B6, 0x00BE, 0x2014, 0x00BC,\r
+    0x00BD, 0x00AA, 0x00BA, 0x00AB, 0x25A0, 0x00BB, 0x00B1, 0xFFFD\r
+};\r
+\r
+static const wchar_t koi8_u[] = {\r
+    0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,\r
+    0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,\r
+    0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2022, 0x221A, 0x2248,\r
+    0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,\r
+    0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457,\r
+    0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E,\r
+    0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407,\r
+    0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9,\r
+    0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,\r
+    0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,\r
+    0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,\r
+    0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A,\r
+    0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,\r
+    0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,\r
+    0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,\r
+    0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A\r
+};\r
+\r
+static const wchar_t vscii[] = {\r
+    0x0000, 0x0001, 0x1EB2, 0x0003, 0x0004, 0x1EB4, 0x1EAA, 0x0007,\r
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,\r
+    0x0010, 0x0011, 0x0012, 0x0013, 0x1EF6, 0x0015, 0x0016, 0x0017,\r
+    0x0018, 0x1EF8, 0x001a, 0x001b, 0x001c, 0x001d, 0x1EF4, 0x001f,\r
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,\r
+    0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,\r
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,\r
+    0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,\r
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,\r
+    0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,\r
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,\r
+    0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,\r
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,\r
+    0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,\r
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,\r
+    0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007f,\r
+    0x1EA0, 0x1EAE, 0x1EB0, 0x1EB6, 0x1EA4, 0x1EA6, 0x1EA8, 0x1EAC,\r
+    0x1EBC, 0x1EB8, 0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1ED0,\r
+    0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EE2, 0x1EDA, 0x1EDC, 0x1EDE,\r
+    0x1ECA, 0x1ECE, 0x1ECC, 0x1EC8, 0x1EE6, 0x0168, 0x1EE4, 0x1EF2,\r
+    0x00D5, 0x1EAF, 0x1EB1, 0x1EB7, 0x1EA5, 0x1EA7, 0x1EA8, 0x1EAD,\r
+    0x1EBD, 0x1EB9, 0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1ED1,\r
+    0x1ED3, 0x1ED5, 0x1ED7, 0x1EE0, 0x01A0, 0x1ED9, 0x1EDD, 0x1EDF,\r
+    0x1ECB, 0x1EF0, 0x1EE8, 0x1EEA, 0x1EEC, 0x01A1, 0x1EDB, 0x01AF,\r
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x1EA2, 0x0102, 0x1EB3, 0x1EB5,\r
+    0x00C8, 0x00C9, 0x00CA, 0x1EBA, 0x00CC, 0x00CD, 0x0128, 0x1EF3,\r
+    0x0110, 0x1EE9, 0x00D2, 0x00D3, 0x00D4, 0x1EA1, 0x1EF7, 0x1EEB,\r
+    0x1EED, 0x00D9, 0x00DA, 0x1EF9, 0x1EF5, 0x00DD, 0x1EE1, 0x01B0,\r
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x1EA3, 0x0103, 0x1EEF, 0x1EAB,\r
+    0x00E8, 0x00E9, 0x00EA, 0x1EBB, 0x00EC, 0x00ED, 0x0129, 0x1EC9,\r
+    0x0111, 0x1EF1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x1ECF, 0x1ECD,\r
+    0x1EE5, 0x00F9, 0x00FA, 0x0169, 0x1EE7, 0x00FD, 0x1EE3, 0x1EEE\r
+};\r
+\r
+static const wchar_t dec_mcs[] = {\r
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0xFFFD, 0x00A5, 0xFFFD, 0x00A7,\r
+    0x00A4, 0x00A9, 0x00AA, 0x00AB, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,\r
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0xFFFD, 0x00B5, 0x00B6, 0x00B7,\r
+    0xFFFD, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0xFFFD, 0x00BF,\r
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+    0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0152,\r
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0178, 0xFFFD, 0x00DF,\r
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+    0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0153,\r
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FF, 0xFFFD, 0xFFFD\r
+};\r
+\r
+/* Mazovia (Polish) aka CP620\r
+ * from "Mazowia to Unicode table", 04/24/96, Mikolaj Jedrzejak */\r
+static const wchar_t mazovia[] = {\r
+    /* Code point 0x9B is "zloty" symbol (z&#0142;), which is not\r
+     *   widely used and for which there is no Unicode equivalent.\r
+     * One reference shows 0xA8 as U+00A7 SECTION SIGN, but we're\r
+     *   told that's incorrect. */\r
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x0105, 0x00E7,\r
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0107, 0x00C4, 0x0104,\r
+    0x0118, 0x0119, 0x0142, 0x00F4, 0x00F6, 0x0106, 0x00FB, 0x00F9,\r
+    0x015a, 0x00D6, 0x00DC, 0xFFFD, 0x0141, 0x00A5, 0x015b, 0x0192,\r
+    0x0179, 0x017b, 0x00F3, 0x00d3, 0x0144, 0x0143, 0x017a, 0x017c,\r
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\r
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\r
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,\r
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\r
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,\r
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\r
+    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,\r
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\r
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,\r
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+struct cp_list_item {\r
+    char *name;\r
+    int codepage;\r
+    int cp_size;\r
+    const wchar_t *cp_table;\r
+};\r
+\r
+static const struct cp_list_item cp_list[] = {\r
+    {"ISO-8859-1:1998 (Latin-1, West Europe)", 0, 96, iso_8859_1},\r
+    {"ISO-8859-2:1999 (Latin-2, East Europe)", 0, 96, iso_8859_2},\r
+    {"ISO-8859-3:1999 (Latin-3, South Europe)", 0, 96, iso_8859_3},\r
+    {"ISO-8859-4:1998 (Latin-4, North Europe)", 0, 96, iso_8859_4},\r
+    {"ISO-8859-5:1999 (Latin/Cyrillic)", 0, 96, iso_8859_5},\r
+    {"ISO-8859-6:1999 (Latin/Arabic)", 0, 96, iso_8859_6},\r
+    {"ISO-8859-7:1987 (Latin/Greek)", 0, 96, iso_8859_7},\r
+    {"ISO-8859-8:1999 (Latin/Hebrew)", 0, 96, iso_8859_8},\r
+    {"ISO-8859-9:1999 (Latin-5, Turkish)", 0, 96, iso_8859_9},\r
+    {"ISO-8859-10:1998 (Latin-6, Nordic)", 0, 96, iso_8859_10},\r
+    {"ISO-8859-11:2001 (Latin/Thai)", 0, 96, iso_8859_11},\r
+    {"ISO-8859-13:1998 (Latin-7, Baltic)", 0, 96, iso_8859_13},\r
+    {"ISO-8859-14:1998 (Latin-8, Celtic)", 0, 96, iso_8859_14},\r
+    {"ISO-8859-15:1999 (Latin-9, \"euro\")", 0, 96, iso_8859_15},\r
+    {"ISO-8859-16:2001 (Latin-10, Balkan)", 0, 96, iso_8859_16},\r
+\r
+    {"UTF-8", CP_UTF8},\r
+\r
+    {"KOI8-U", 0, 128, koi8_u},\r
+    {"KOI8-R", 20866},\r
+    {"HP-ROMAN8", 0, 96, roman8},\r
+    {"VSCII", 0, 256, vscii},\r
+    {"DEC-MCS", 0, 96, dec_mcs},\r
+\r
+    {"Win1250 (Central European)", 1250},\r
+    {"Win1251 (Cyrillic)", 1251},\r
+    {"Win1252 (Western)", 1252},\r
+    {"Win1253 (Greek)", 1253},\r
+    {"Win1254 (Turkish)", 1254},\r
+    {"Win1255 (Hebrew)", 1255},\r
+    {"Win1256 (Arabic)", 1256},\r
+    {"Win1257 (Baltic)", 1257},\r
+    {"Win1258 (Vietnamese)", 1258},\r
+\r
+    {"CP437", 437},\r
+    {"CP620 (Mazovia)", 0, 128, mazovia},\r
+    {"CP819", 28591},\r
+    {"CP878", 20866},\r
+\r
+    {"Use font encoding", -1},\r
+\r
+    {0, 0}\r
+};\r
+\r
+static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr);\r
+\r
+void init_ucs(Config *cfg, struct unicode_data *ucsdata)\r
+{\r
+    int i, j;\r
+    int used_dtf = 0;\r
+    char tbuf[256];\r
+\r
+    for (i = 0; i < 256; i++)\r
+       tbuf[i] = i;\r
+\r
+    /* Decide on the Line and Font codepages */\r
+    ucsdata->line_codepage = decode_codepage(cfg->line_codepage);\r
+\r
+    if (ucsdata->font_codepage <= 0) { \r
+       ucsdata->font_codepage=0; \r
+       ucsdata->dbcs_screenfont=0; \r
+    }\r
+\r
+    if (cfg->vtmode == VT_OEMONLY) {\r
+       ucsdata->font_codepage = 437;\r
+       ucsdata->dbcs_screenfont = 0;\r
+       if (ucsdata->line_codepage <= 0)\r
+           ucsdata->line_codepage = GetACP();\r
+    } else if (ucsdata->line_codepage <= 0)\r
+       ucsdata->line_codepage = ucsdata->font_codepage;\r
+\r
+    /* Collect screen font ucs table */\r
+    if (ucsdata->dbcs_screenfont || ucsdata->font_codepage == 0) {\r
+       get_unitab(ucsdata->font_codepage, ucsdata->unitab_font, 2);\r
+       for (i = 128; i < 256; i++)\r
+           ucsdata->unitab_font[i] = (WCHAR) (CSET_ACP + i);\r
+    } else {\r
+       get_unitab(ucsdata->font_codepage, ucsdata->unitab_font, 1);\r
+\r
+       /* CP437 fonts are often broken ... */\r
+       if (ucsdata->font_codepage == 437)\r
+           ucsdata->unitab_font[0] = ucsdata->unitab_font[255] = 0xFFFF;\r
+    }\r
+    if (cfg->vtmode == VT_XWINDOWS)\r
+       memcpy(ucsdata->unitab_font + 1, unitab_xterm_std,\r
+              sizeof(unitab_xterm_std));\r
+\r
+    /* Collect OEMCP ucs table */\r
+    get_unitab(CP_OEMCP, ucsdata->unitab_oemcp, 1);\r
+\r
+    /* Collect CP437 ucs table for SCO acs */\r
+    if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS)\r
+       memcpy(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp,\r
+              sizeof(ucsdata->unitab_scoacs));\r
+    else\r
+       get_unitab(437, ucsdata->unitab_scoacs, 1);\r
+\r
+    /* Collect line set ucs table */\r
+    if (ucsdata->line_codepage == ucsdata->font_codepage &&\r
+       (ucsdata->dbcs_screenfont ||\r
+        cfg->vtmode == VT_POORMAN || ucsdata->font_codepage==0)) {\r
+\r
+       /* For DBCS and POOR fonts force direct to font */\r
+       used_dtf = 1;\r
+       for (i = 0; i < 32; i++)\r
+           ucsdata->unitab_line[i] = (WCHAR) i;\r
+       for (i = 32; i < 256; i++)\r
+           ucsdata->unitab_line[i] = (WCHAR) (CSET_ACP + i);\r
+       ucsdata->unitab_line[127] = (WCHAR) 127;\r
+    } else {\r
+       get_unitab(ucsdata->line_codepage, ucsdata->unitab_line, 0);\r
+    }\r
+\r
+#if 0\r
+    debug(\r
+         ("Line cp%d, Font cp%d%s\n", ucsdata->line_codepage,\r
+          ucsdata->font_codepage, ucsdata->dbcs_screenfont ? " DBCS" : ""));\r
+\r
+    for (i = 0; i < 256; i += 16) {\r
+       for (j = 0; j < 16; j++) {\r
+           debug(("%04x%s", ucsdata->unitab_line[i + j], j == 15 ? "" : ","));\r
+       }\r
+       debug(("\n"));\r
+    }\r
+#endif\r
+\r
+    /* VT100 graphics - NB: Broken for non-ascii CP's */\r
+    memcpy(ucsdata->unitab_xterm, ucsdata->unitab_line,\r
+          sizeof(ucsdata->unitab_xterm));\r
+    memcpy(ucsdata->unitab_xterm + '`', unitab_xterm_std,\r
+          sizeof(unitab_xterm_std));\r
+    ucsdata->unitab_xterm['_'] = ' ';\r
+\r
+    /* Generate UCS ->line page table. */\r
+    if (ucsdata->uni_tbl) {\r
+       for (i = 0; i < 256; i++)\r
+           if (ucsdata->uni_tbl[i])\r
+               sfree(ucsdata->uni_tbl[i]);\r
+       sfree(ucsdata->uni_tbl);\r
+       ucsdata->uni_tbl = 0;\r
+    }\r
+    if (!used_dtf) {\r
+       for (i = 0; i < 256; i++) {\r
+           if (DIRECT_CHAR(ucsdata->unitab_line[i]))\r
+               continue;\r
+           if (DIRECT_FONT(ucsdata->unitab_line[i]))\r
+               continue;\r
+           if (!ucsdata->uni_tbl) {\r
+               ucsdata->uni_tbl = snewn(256, char *);\r
+               memset(ucsdata->uni_tbl, 0, 256 * sizeof(char *));\r
+           }\r
+           j = ((ucsdata->unitab_line[i] >> 8) & 0xFF);\r
+           if (!ucsdata->uni_tbl[j]) {\r
+               ucsdata->uni_tbl[j] = snewn(256, char);\r
+               memset(ucsdata->uni_tbl[j], 0, 256 * sizeof(char));\r
+           }\r
+           ucsdata->uni_tbl[j][ucsdata->unitab_line[i] & 0xFF] = i;\r
+       }\r
+    }\r
+\r
+    /* Find the line control characters. */\r
+    for (i = 0; i < 256; i++)\r
+       if (ucsdata->unitab_line[i] < ' '\r
+           || (ucsdata->unitab_line[i] >= 0x7F && \r
+               ucsdata->unitab_line[i] < 0xA0))\r
+           ucsdata->unitab_ctrl[i] = i;\r
+       else\r
+           ucsdata->unitab_ctrl[i] = 0xFF;\r
+\r
+    /* Generate line->screen direct conversion links. */\r
+    if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS)\r
+       link_font(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, CSET_OEMCP);\r
+\r
+    link_font(ucsdata->unitab_line, ucsdata->unitab_font, CSET_ACP);\r
+    link_font(ucsdata->unitab_scoacs, ucsdata->unitab_font, CSET_ACP);\r
+    link_font(ucsdata->unitab_xterm, ucsdata->unitab_font, CSET_ACP);\r
+\r
+    if (cfg->vtmode == VT_OEMANSI || cfg->vtmode == VT_XWINDOWS) {\r
+       link_font(ucsdata->unitab_line, ucsdata->unitab_oemcp, CSET_OEMCP);\r
+       link_font(ucsdata->unitab_xterm, ucsdata->unitab_oemcp, CSET_OEMCP);\r
+    }\r
+\r
+    if (ucsdata->dbcs_screenfont &&\r
+       ucsdata->font_codepage != ucsdata->line_codepage) {\r
+       /* F***ing Microsoft fonts, Japanese and Korean codepage fonts\r
+        * have a currency symbol at 0x5C but their unicode value is \r
+        * still given as U+005C not the correct U+00A5. */\r
+       ucsdata->unitab_line['\\'] = CSET_OEMCP + '\\';\r
+    }\r
+\r
+    /* Last chance, if !unicode then try poorman links. */\r
+    if (cfg->vtmode != VT_UNICODE) {\r
+       static const char poorman_scoacs[] = \r
+           "CueaaaaceeeiiiAAE**ooouuyOUc$YPsaiounNao?++**!<>###||||++||++++++--|-+||++--|-+----++++++++##||#aBTPEsyt******EN=+><++-=... n2* ";\r
+       static const char poorman_latin1[] =\r
+           " !cL.Y|S\"Ca<--R~o+23'u|.,1o>///?AAAAAAACEEEEIIIIDNOOOOOxOUUUUYPBaaaaaaaceeeeiiiionooooo/ouuuuypy";\r
+       static const char poorman_vt100[] = "*#****o~**+++++-----++++|****L.";\r
+\r
+       for (i = 160; i < 256; i++)\r
+           if (!DIRECT_FONT(ucsdata->unitab_line[i]) &&\r
+               ucsdata->unitab_line[i] >= 160 &&\r
+               ucsdata->unitab_line[i] < 256) {\r
+               ucsdata->unitab_line[i] =\r
+                   (WCHAR) (CSET_ACP +\r
+                            poorman_latin1[ucsdata->unitab_line[i] - 160]);\r
+           }\r
+       for (i = 96; i < 127; i++)\r
+           if (!DIRECT_FONT(ucsdata->unitab_xterm[i]))\r
+               ucsdata->unitab_xterm[i] =\r
+           (WCHAR) (CSET_ACP + poorman_vt100[i - 96]);\r
+       for(i=128;i<256;i++) \r
+           if (!DIRECT_FONT(ucsdata->unitab_scoacs[i]))\r
+               ucsdata->unitab_scoacs[i] = \r
+                   (WCHAR) (CSET_ACP + poorman_scoacs[i - 128]);\r
+    }\r
+}\r
+\r
+static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr)\r
+{\r
+    int font_index, line_index, i;\r
+    for (line_index = 0; line_index < 256; line_index++) {\r
+       if (DIRECT_FONT(line_tbl[line_index]))\r
+           continue;\r
+       for(i = 0; i < 256; i++) {\r
+           font_index = ((32 + i) & 0xFF);\r
+           if (line_tbl[line_index] == font_tbl[font_index]) {\r
+               line_tbl[line_index] = (WCHAR) (attr + font_index);\r
+               break;\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+wchar_t xlat_uskbd2cyrllic(int ch)\r
+{\r
+    static const wchar_t cyrtab[] = {\r
+            0,      1,       2,      3,      4,      5,      6,      7,\r
+            8,      9,      10,     11,     12,     13,     14,     15,\r
+            16,     17,     18,     19,     20,     21,     22,     23,\r
+            24,     25,     26,     27,     28,     29,     30,     31,\r
+            32,     33, 0x042d,     35,     36,     37,     38, 0x044d,\r
+            40,     41,     42, 0x0406, 0x0431, 0x0454, 0x044e, 0x002e,\r
+            48,     49,     50,     51,     52,     53,     54,     55,\r
+            56,     57, 0x0416, 0x0436, 0x0411, 0x0456, 0x042e, 0x002c,\r
+            64, 0x0424, 0x0418, 0x0421, 0x0412, 0x0423, 0x0410, 0x041f,\r
+        0x0420, 0x0428, 0x041e, 0x041b, 0x0414, 0x042c, 0x0422, 0x0429,\r
+        0x0417, 0x0419, 0x041a, 0x042b, 0x0415, 0x0413, 0x041c, 0x0426,\r
+        0x0427, 0x041d, 0x042f, 0x0445, 0x0457, 0x044a,     94, 0x0404,\r
+            96, 0x0444, 0x0438, 0x0441, 0x0432, 0x0443, 0x0430, 0x043f,\r
+        0x0440, 0x0448, 0x043e, 0x043b, 0x0434, 0x044c, 0x0442, 0x0449,\r
+        0x0437, 0x0439, 0x043a, 0x044b, 0x0435, 0x0433, 0x043c, 0x0446,\r
+        0x0447, 0x043d, 0x044f, 0x0425, 0x0407, 0x042a,    126,    127\r
+       };\r
+    return cyrtab[ch&0x7F];\r
+}\r
+\r
+int check_compose_internal(int first, int second, int recurse)\r
+{\r
+\r
+    static const struct {\r
+       char first, second;\r
+       wchar_t composed;\r
+    } composetbl[] = {\r
+       {\r
+       0x2b, 0x2b, 0x0023}, {\r
+       0x41, 0x41, 0x0040}, {\r
+       0x28, 0x28, 0x005b}, {\r
+       0x2f, 0x2f, 0x005c}, {\r
+       0x29, 0x29, 0x005d}, {\r
+       0x28, 0x2d, 0x007b}, {\r
+       0x2d, 0x29, 0x007d}, {\r
+       0x2f, 0x5e, 0x007c}, {\r
+       0x21, 0x21, 0x00a1}, {\r
+       0x43, 0x2f, 0x00a2}, {\r
+       0x43, 0x7c, 0x00a2}, {\r
+       0x4c, 0x2d, 0x00a3}, {\r
+       0x4c, 0x3d, 0x20a4}, {\r
+       0x58, 0x4f, 0x00a4}, {\r
+       0x58, 0x30, 0x00a4}, {\r
+       0x59, 0x2d, 0x00a5}, {\r
+       0x59, 0x3d, 0x00a5}, {\r
+       0x7c, 0x7c, 0x00a6}, {\r
+       0x53, 0x4f, 0x00a7}, {\r
+       0x53, 0x21, 0x00a7}, {\r
+       0x53, 0x30, 0x00a7}, {\r
+       0x22, 0x22, 0x00a8}, {\r
+       0x43, 0x4f, 0x00a9}, {\r
+       0x43, 0x30, 0x00a9}, {\r
+       0x41, 0x5f, 0x00aa}, {\r
+       0x3c, 0x3c, 0x00ab}, {\r
+       0x2c, 0x2d, 0x00ac}, {\r
+       0x2d, 0x2d, 0x00ad}, {\r
+       0x52, 0x4f, 0x00ae}, {\r
+       0x2d, 0x5e, 0x00af}, {\r
+       0x30, 0x5e, 0x00b0}, {\r
+       0x2b, 0x2d, 0x00b1}, {\r
+       0x32, 0x5e, 0x00b2}, {\r
+       0x33, 0x5e, 0x00b3}, {\r
+       0x27, 0x27, 0x00b4}, {\r
+       0x2f, 0x55, 0x00b5}, {\r
+       0x50, 0x21, 0x00b6}, {\r
+       0x2e, 0x5e, 0x00b7}, {\r
+       0x2c, 0x2c, 0x00b8}, {\r
+       0x31, 0x5e, 0x00b9}, {\r
+       0x4f, 0x5f, 0x00ba}, {\r
+       0x3e, 0x3e, 0x00bb}, {\r
+       0x31, 0x34, 0x00bc}, {\r
+       0x31, 0x32, 0x00bd}, {\r
+       0x33, 0x34, 0x00be}, {\r
+       0x3f, 0x3f, 0x00bf}, {\r
+       0x60, 0x41, 0x00c0}, {\r
+       0x27, 0x41, 0x00c1}, {\r
+       0x5e, 0x41, 0x00c2}, {\r
+       0x7e, 0x41, 0x00c3}, {\r
+       0x22, 0x41, 0x00c4}, {\r
+       0x2a, 0x41, 0x00c5}, {\r
+       0x41, 0x45, 0x00c6}, {\r
+       0x2c, 0x43, 0x00c7}, {\r
+       0x60, 0x45, 0x00c8}, {\r
+       0x27, 0x45, 0x00c9}, {\r
+       0x5e, 0x45, 0x00ca}, {\r
+       0x22, 0x45, 0x00cb}, {\r
+       0x60, 0x49, 0x00cc}, {\r
+       0x27, 0x49, 0x00cd}, {\r
+       0x5e, 0x49, 0x00ce}, {\r
+       0x22, 0x49, 0x00cf}, {\r
+       0x2d, 0x44, 0x00d0}, {\r
+       0x7e, 0x4e, 0x00d1}, {\r
+       0x60, 0x4f, 0x00d2}, {\r
+       0x27, 0x4f, 0x00d3}, {\r
+       0x5e, 0x4f, 0x00d4}, {\r
+       0x7e, 0x4f, 0x00d5}, {\r
+       0x22, 0x4f, 0x00d6}, {\r
+       0x58, 0x58, 0x00d7}, {\r
+       0x2f, 0x4f, 0x00d8}, {\r
+       0x60, 0x55, 0x00d9}, {\r
+       0x27, 0x55, 0x00da}, {\r
+       0x5e, 0x55, 0x00db}, {\r
+       0x22, 0x55, 0x00dc}, {\r
+       0x27, 0x59, 0x00dd}, {\r
+       0x48, 0x54, 0x00de}, {\r
+       0x73, 0x73, 0x00df}, {\r
+       0x60, 0x61, 0x00e0}, {\r
+       0x27, 0x61, 0x00e1}, {\r
+       0x5e, 0x61, 0x00e2}, {\r
+       0x7e, 0x61, 0x00e3}, {\r
+       0x22, 0x61, 0x00e4}, {\r
+       0x2a, 0x61, 0x00e5}, {\r
+       0x61, 0x65, 0x00e6}, {\r
+       0x2c, 0x63, 0x00e7}, {\r
+       0x60, 0x65, 0x00e8}, {\r
+       0x27, 0x65, 0x00e9}, {\r
+       0x5e, 0x65, 0x00ea}, {\r
+       0x22, 0x65, 0x00eb}, {\r
+       0x60, 0x69, 0x00ec}, {\r
+       0x27, 0x69, 0x00ed}, {\r
+       0x5e, 0x69, 0x00ee}, {\r
+       0x22, 0x69, 0x00ef}, {\r
+       0x2d, 0x64, 0x00f0}, {\r
+       0x7e, 0x6e, 0x00f1}, {\r
+       0x60, 0x6f, 0x00f2}, {\r
+       0x27, 0x6f, 0x00f3}, {\r
+       0x5e, 0x6f, 0x00f4}, {\r
+       0x7e, 0x6f, 0x00f5}, {\r
+       0x22, 0x6f, 0x00f6}, {\r
+       0x3a, 0x2d, 0x00f7}, {\r
+       0x6f, 0x2f, 0x00f8}, {\r
+       0x60, 0x75, 0x00f9}, {\r
+       0x27, 0x75, 0x00fa}, {\r
+       0x5e, 0x75, 0x00fb}, {\r
+       0x22, 0x75, 0x00fc}, {\r
+       0x27, 0x79, 0x00fd}, {\r
+       0x68, 0x74, 0x00fe}, {\r
+       0x22, 0x79, 0x00ff},\r
+           /* Unicode extras. */\r
+       {\r
+       0x6f, 0x65, 0x0153}, {\r
+       0x4f, 0x45, 0x0152},\r
+           /* Compose pairs from UCS */\r
+       {\r
+       0x41, 0x2D, 0x0100}, {\r
+       0x61, 0x2D, 0x0101}, {\r
+       0x43, 0x27, 0x0106}, {\r
+       0x63, 0x27, 0x0107}, {\r
+       0x43, 0x5E, 0x0108}, {\r
+       0x63, 0x5E, 0x0109}, {\r
+       0x45, 0x2D, 0x0112}, {\r
+       0x65, 0x2D, 0x0113}, {\r
+       0x47, 0x5E, 0x011C}, {\r
+       0x67, 0x5E, 0x011D}, {\r
+       0x47, 0x2C, 0x0122}, {\r
+       0x67, 0x2C, 0x0123}, {\r
+       0x48, 0x5E, 0x0124}, {\r
+       0x68, 0x5E, 0x0125}, {\r
+       0x49, 0x7E, 0x0128}, {\r
+       0x69, 0x7E, 0x0129}, {\r
+       0x49, 0x2D, 0x012A}, {\r
+       0x69, 0x2D, 0x012B}, {\r
+       0x4A, 0x5E, 0x0134}, {\r
+       0x6A, 0x5E, 0x0135}, {\r
+       0x4B, 0x2C, 0x0136}, {\r
+       0x6B, 0x2C, 0x0137}, {\r
+       0x4C, 0x27, 0x0139}, {\r
+       0x6C, 0x27, 0x013A}, {\r
+       0x4C, 0x2C, 0x013B}, {\r
+       0x6C, 0x2C, 0x013C}, {\r
+       0x4E, 0x27, 0x0143}, {\r
+       0x6E, 0x27, 0x0144}, {\r
+       0x4E, 0x2C, 0x0145}, {\r
+       0x6E, 0x2C, 0x0146}, {\r
+       0x4F, 0x2D, 0x014C}, {\r
+       0x6F, 0x2D, 0x014D}, {\r
+       0x52, 0x27, 0x0154}, {\r
+       0x72, 0x27, 0x0155}, {\r
+       0x52, 0x2C, 0x0156}, {\r
+       0x72, 0x2C, 0x0157}, {\r
+       0x53, 0x27, 0x015A}, {\r
+       0x73, 0x27, 0x015B}, {\r
+       0x53, 0x5E, 0x015C}, {\r
+       0x73, 0x5E, 0x015D}, {\r
+       0x53, 0x2C, 0x015E}, {\r
+       0x73, 0x2C, 0x015F}, {\r
+       0x54, 0x2C, 0x0162}, {\r
+       0x74, 0x2C, 0x0163}, {\r
+       0x55, 0x7E, 0x0168}, {\r
+       0x75, 0x7E, 0x0169}, {\r
+       0x55, 0x2D, 0x016A}, {\r
+       0x75, 0x2D, 0x016B}, {\r
+       0x55, 0x2A, 0x016E}, {\r
+       0x75, 0x2A, 0x016F}, {\r
+       0x57, 0x5E, 0x0174}, {\r
+       0x77, 0x5E, 0x0175}, {\r
+       0x59, 0x5E, 0x0176}, {\r
+       0x79, 0x5E, 0x0177}, {\r
+       0x59, 0x22, 0x0178}, {\r
+       0x5A, 0x27, 0x0179}, {\r
+       0x7A, 0x27, 0x017A}, {\r
+       0x47, 0x27, 0x01F4}, {\r
+       0x67, 0x27, 0x01F5}, {\r
+       0x4E, 0x60, 0x01F8}, {\r
+       0x6E, 0x60, 0x01F9}, {\r
+       0x45, 0x2C, 0x0228}, {\r
+       0x65, 0x2C, 0x0229}, {\r
+       0x59, 0x2D, 0x0232}, {\r
+       0x79, 0x2D, 0x0233}, {\r
+       0x44, 0x2C, 0x1E10}, {\r
+       0x64, 0x2C, 0x1E11}, {\r
+       0x47, 0x2D, 0x1E20}, {\r
+       0x67, 0x2D, 0x1E21}, {\r
+       0x48, 0x22, 0x1E26}, {\r
+       0x68, 0x22, 0x1E27}, {\r
+       0x48, 0x2C, 0x1E28}, {\r
+       0x68, 0x2C, 0x1E29}, {\r
+       0x4B, 0x27, 0x1E30}, {\r
+       0x6B, 0x27, 0x1E31}, {\r
+       0x4D, 0x27, 0x1E3E}, {\r
+       0x6D, 0x27, 0x1E3F}, {\r
+       0x50, 0x27, 0x1E54}, {\r
+       0x70, 0x27, 0x1E55}, {\r
+       0x56, 0x7E, 0x1E7C}, {\r
+       0x76, 0x7E, 0x1E7D}, {\r
+       0x57, 0x60, 0x1E80}, {\r
+       0x77, 0x60, 0x1E81}, {\r
+       0x57, 0x27, 0x1E82}, {\r
+       0x77, 0x27, 0x1E83}, {\r
+       0x57, 0x22, 0x1E84}, {\r
+       0x77, 0x22, 0x1E85}, {\r
+       0x58, 0x22, 0x1E8C}, {\r
+       0x78, 0x22, 0x1E8D}, {\r
+       0x5A, 0x5E, 0x1E90}, {\r
+       0x7A, 0x5E, 0x1E91}, {\r
+       0x74, 0x22, 0x1E97}, {\r
+       0x77, 0x2A, 0x1E98}, {\r
+       0x79, 0x2A, 0x1E99}, {\r
+       0x45, 0x7E, 0x1EBC}, {\r
+       0x65, 0x7E, 0x1EBD}, {\r
+       0x59, 0x60, 0x1EF2}, {\r
+       0x79, 0x60, 0x1EF3}, {\r
+       0x59, 0x7E, 0x1EF8}, {\r
+       0x79, 0x7E, 0x1EF9},\r
+           /* Compatible/possibles from UCS */\r
+       {\r
+       0x49, 0x4A, 0x0132}, {\r
+       0x69, 0x6A, 0x0133}, {\r
+       0x4C, 0x4A, 0x01C7}, {\r
+       0x4C, 0x6A, 0x01C8}, {\r
+       0x6C, 0x6A, 0x01C9}, {\r
+       0x4E, 0x4A, 0x01CA}, {\r
+       0x4E, 0x6A, 0x01CB}, {\r
+       0x6E, 0x6A, 0x01CC}, {\r
+       0x44, 0x5A, 0x01F1}, {\r
+       0x44, 0x7A, 0x01F2}, {\r
+       0x64, 0x7A, 0x01F3}, {\r
+       0x2E, 0x2E, 0x2025}, {\r
+       0x21, 0x21, 0x203C}, {\r
+       0x3F, 0x21, 0x2048}, {\r
+       0x21, 0x3F, 0x2049}, {\r
+       0x52, 0x73, 0x20A8}, {\r
+       0x4E, 0x6F, 0x2116}, {\r
+       0x53, 0x4D, 0x2120}, {\r
+       0x54, 0x4D, 0x2122}, {\r
+       0x49, 0x49, 0x2161}, {\r
+       0x49, 0x56, 0x2163}, {\r
+       0x56, 0x49, 0x2165}, {\r
+       0x49, 0x58, 0x2168}, {\r
+       0x58, 0x49, 0x216A}, {\r
+       0x69, 0x69, 0x2171}, {\r
+       0x69, 0x76, 0x2173}, {\r
+       0x76, 0x69, 0x2175}, {\r
+       0x69, 0x78, 0x2178}, {\r
+       0x78, 0x69, 0x217A}, {\r
+       0x31, 0x30, 0x2469}, {\r
+       0x31, 0x31, 0x246A}, {\r
+       0x31, 0x32, 0x246B}, {\r
+       0x31, 0x33, 0x246C}, {\r
+       0x31, 0x34, 0x246D}, {\r
+       0x31, 0x35, 0x246E}, {\r
+       0x31, 0x36, 0x246F}, {\r
+       0x31, 0x37, 0x2470}, {\r
+       0x31, 0x38, 0x2471}, {\r
+       0x31, 0x39, 0x2472}, {\r
+       0x32, 0x30, 0x2473}, {\r
+       0x31, 0x2E, 0x2488}, {\r
+       0x32, 0x2E, 0x2489}, {\r
+       0x33, 0x2E, 0x248A}, {\r
+       0x34, 0x2E, 0x248B}, {\r
+       0x35, 0x2E, 0x248C}, {\r
+       0x36, 0x2E, 0x248D}, {\r
+       0x37, 0x2E, 0x248E}, {\r
+       0x38, 0x2E, 0x248F}, {\r
+       0x39, 0x2E, 0x2490}, {\r
+       0x64, 0x61, 0x3372}, {\r
+       0x41, 0x55, 0x3373}, {\r
+       0x6F, 0x56, 0x3375}, {\r
+       0x70, 0x63, 0x3376}, {\r
+       0x70, 0x41, 0x3380}, {\r
+       0x6E, 0x41, 0x3381}, {\r
+       0x6D, 0x41, 0x3383}, {\r
+       0x6B, 0x41, 0x3384}, {\r
+       0x4B, 0x42, 0x3385}, {\r
+       0x4D, 0x42, 0x3386}, {\r
+       0x47, 0x42, 0x3387}, {\r
+       0x70, 0x46, 0x338A}, {\r
+       0x6E, 0x46, 0x338B}, {\r
+       0x6D, 0x67, 0x338E}, {\r
+       0x6B, 0x67, 0x338F}, {\r
+       0x48, 0x7A, 0x3390}, {\r
+       0x66, 0x6D, 0x3399}, {\r
+       0x6E, 0x6D, 0x339A}, {\r
+       0x6D, 0x6D, 0x339C}, {\r
+       0x63, 0x6D, 0x339D}, {\r
+       0x6B, 0x6D, 0x339E}, {\r
+       0x50, 0x61, 0x33A9}, {\r
+       0x70, 0x73, 0x33B0}, {\r
+       0x6E, 0x73, 0x33B1}, {\r
+       0x6D, 0x73, 0x33B3}, {\r
+       0x70, 0x56, 0x33B4}, {\r
+       0x6E, 0x56, 0x33B5}, {\r
+       0x6D, 0x56, 0x33B7}, {\r
+       0x6B, 0x56, 0x33B8}, {\r
+       0x4D, 0x56, 0x33B9}, {\r
+       0x70, 0x57, 0x33BA}, {\r
+       0x6E, 0x57, 0x33BB}, {\r
+       0x6D, 0x57, 0x33BD}, {\r
+       0x6B, 0x57, 0x33BE}, {\r
+       0x4D, 0x57, 0x33BF}, {\r
+       0x42, 0x71, 0x33C3}, {\r
+       0x63, 0x63, 0x33C4}, {\r
+       0x63, 0x64, 0x33C5}, {\r
+       0x64, 0x42, 0x33C8}, {\r
+       0x47, 0x79, 0x33C9}, {\r
+       0x68, 0x61, 0x33CA}, {\r
+       0x48, 0x50, 0x33CB}, {\r
+       0x69, 0x6E, 0x33CC}, {\r
+       0x4B, 0x4B, 0x33CD}, {\r
+       0x4B, 0x4D, 0x33CE}, {\r
+       0x6B, 0x74, 0x33CF}, {\r
+       0x6C, 0x6D, 0x33D0}, {\r
+       0x6C, 0x6E, 0x33D1}, {\r
+       0x6C, 0x78, 0x33D3}, {\r
+       0x6D, 0x62, 0x33D4}, {\r
+       0x50, 0x48, 0x33D7}, {\r
+       0x50, 0x52, 0x33DA}, {\r
+       0x73, 0x72, 0x33DB}, {\r
+       0x53, 0x76, 0x33DC}, {\r
+       0x57, 0x62, 0x33DD}, {\r
+       0x66, 0x66, 0xFB00}, {\r
+       0x66, 0x69, 0xFB01}, {\r
+       0x66, 0x6C, 0xFB02}, {\r
+       0x73, 0x74, 0xFB06}, {\r
+       0, 0, 0}\r
+    }, *c;\r
+\r
+    int nc = -1;\r
+\r
+    for (c = composetbl; c->first; c++) {\r
+       if (c->first == first && c->second == second)\r
+           return c->composed;\r
+    }\r
+\r
+    if (recurse == 0) {\r
+       nc = check_compose_internal(second, first, 1);\r
+       if (nc == -1)\r
+           nc = check_compose_internal(toupper(first), toupper(second), 1);\r
+       if (nc == -1)\r
+           nc = check_compose_internal(toupper(second), toupper(first), 1);\r
+    }\r
+    return nc;\r
+}\r
+\r
+int check_compose(int first, int second)\r
+{\r
+    return check_compose_internal(first, second, 0);\r
+}\r
+\r
+int decode_codepage(char *cp_name)\r
+{\r
+    char *s, *d;\r
+    const struct cp_list_item *cpi;\r
+    int codepage = -1;\r
+    CPINFO cpinfo;\r
+\r
+    if (!*cp_name) {\r
+       /*\r
+        * Here we select a plausible default code page based on\r
+        * the locale the user is in. We wish to select an ISO code\r
+        * page or appropriate local default _rather_ than go with\r
+        * the Win125* series, because it's more important to have\r
+        * CSI and friends enabled by default than the ghastly\r
+        * Windows extra quote characters, and because it's more\r
+        * likely the user is connecting to a remote server that\r
+        * does something Unixy or VMSy and hence standards-\r
+        * compliant than that they're connecting back to a Windows\r
+        * box using horrible nonstandard charsets.\r
+        * \r
+        * Accordingly, Robert de Bath suggests a method for\r
+        * picking a default character set that runs as follows:\r
+        * first call GetACP to get the system's ANSI code page\r
+        * identifier, and translate as follows:\r
+        * \r
+        * 1250 -> ISO 8859-2\r
+        * 1251 -> KOI8-U\r
+        * 1252 -> ISO 8859-1\r
+        * 1253 -> ISO 8859-7\r
+        * 1254 -> ISO 8859-9\r
+        * 1255 -> ISO 8859-8\r
+        * 1256 -> ISO 8859-6\r
+        * 1257 -> ISO 8859-13 (changed from 8859-4 on advice of a Lithuanian)\r
+        * \r
+        * and for anything else, choose direct-to-font.\r
+        */\r
+       int cp = GetACP();\r
+       switch (cp) {\r
+         case 1250: cp_name = "ISO-8859-2"; break;\r
+         case 1251: cp_name = "KOI8-U"; break;\r
+         case 1252: cp_name = "ISO-8859-1"; break;\r
+         case 1253: cp_name = "ISO-8859-7"; break;\r
+         case 1254: cp_name = "ISO-8859-9"; break;\r
+         case 1255: cp_name = "ISO-8859-8"; break;\r
+         case 1256: cp_name = "ISO-8859-6"; break;\r
+         case 1257: cp_name = "ISO-8859-13"; break;\r
+           /* default: leave it blank, which will select -1, direct->font */\r
+       }\r
+    }\r
+\r
+    if (cp_name && *cp_name)\r
+       for (cpi = cp_list; cpi->name; cpi++) {\r
+           s = cp_name;\r
+           d = cpi->name;\r
+           for (;;) {\r
+               while (*s && !isalnum(*s) && *s != ':')\r
+                   s++;\r
+               while (*d && !isalnum(*d) && *d != ':')\r
+                   d++;\r
+               if (*s == 0) {\r
+                   codepage = cpi->codepage;\r
+                   if (codepage == CP_UTF8)\r
+                       goto break_break;\r
+                   if (codepage == -1)\r
+                       return codepage;\r
+                   if (codepage == 0) {\r
+                       codepage = 65536 + (cpi - cp_list);\r
+                       goto break_break;\r
+                   }\r
+\r
+                   if (GetCPInfo(codepage, &cpinfo) != 0)\r
+                       goto break_break;\r
+               }\r
+               if (tolower(*s++) != tolower(*d++))\r
+                   break;\r
+           }\r
+       }\r
+\r
+    if (cp_name && *cp_name) {\r
+       d = cp_name;\r
+       if (tolower(d[0]) == 'c' && tolower(d[1]) == 'p')\r
+           d += 2;\r
+       if (tolower(d[0]) == 'i' && tolower(d[1]) == 'b'\r
+           && tolower(d[2]) == 'm')\r
+           d += 3;\r
+       for (s = d; *s >= '0' && *s <= '9'; s++);\r
+       if (*s == 0 && s != d)\r
+           codepage = atoi(d);        /* CP999 or IBM999 */\r
+\r
+       if (codepage == CP_ACP)\r
+           codepage = GetACP();\r
+       if (codepage == CP_OEMCP)\r
+           codepage = GetOEMCP();\r
+       if (codepage > 65535)\r
+           codepage = -2;\r
+    }\r
+\r
+  break_break:;\r
+    if (codepage != -1) {\r
+       if (codepage != CP_UTF8 && codepage < 65536) {\r
+           if (GetCPInfo(codepage, &cpinfo) == 0) {\r
+               codepage = -2;\r
+           } else if (cpinfo.MaxCharSize > 1)\r
+               codepage = -3;\r
+       }\r
+    }\r
+    if (codepage == -1 && *cp_name)\r
+       codepage = -2;\r
+    return codepage;\r
+}\r
+\r
+const char *cp_name(int codepage)\r
+{\r
+    const struct cp_list_item *cpi, *cpno;\r
+    static char buf[32];\r
+\r
+    if (codepage == -1) {\r
+       sprintf(buf, "Use font encoding");\r
+       return buf;\r
+    }\r
+\r
+    if (codepage > 0 && codepage < 65536)\r
+       sprintf(buf, "CP%03d", codepage);\r
+    else\r
+       *buf = 0;\r
+\r
+    if (codepage >= 65536) {\r
+       cpno = 0;\r
+       for (cpi = cp_list; cpi->name; cpi++)\r
+           if (cpi == cp_list + (codepage - 65536)) {\r
+               cpno = cpi;\r
+               break;\r
+           }\r
+       if (cpno)\r
+           for (cpi = cp_list; cpi->name; cpi++) {\r
+               if (cpno->cp_table == cpi->cp_table)\r
+                   return cpi->name;\r
+           }\r
+    } else {\r
+       for (cpi = cp_list; cpi->name; cpi++) {\r
+           if (codepage == cpi->codepage)\r
+               return cpi->name;\r
+       }\r
+    }\r
+    return buf;\r
+}\r
+\r
+/*\r
+ * Return the nth code page in the list, for use in the GUI\r
+ * configurer.\r
+ */\r
+const char *cp_enumerate(int index)\r
+{\r
+    if (index < 0 || index >= lenof(cp_list))\r
+       return NULL;\r
+    return cp_list[index].name;\r
+}\r
+\r
+void get_unitab(int codepage, wchar_t * unitab, int ftype)\r
+{\r
+    char tbuf[4];\r
+    int i, max = 256, flg = MB_ERR_INVALID_CHARS;\r
+\r
+    if (ftype)\r
+       flg |= MB_USEGLYPHCHARS;\r
+    if (ftype == 2)\r
+       max = 128;\r
+\r
+    if (codepage == CP_UTF8) {\r
+       for (i = 0; i < max; i++)\r
+           unitab[i] = i;\r
+       return;\r
+    }\r
+\r
+    if (codepage == CP_ACP)\r
+       codepage = GetACP();\r
+    else if (codepage == CP_OEMCP)\r
+       codepage = GetOEMCP();\r
+\r
+    if (codepage > 0 && codepage < 65536) {\r
+       for (i = 0; i < max; i++) {\r
+           tbuf[0] = i;\r
+\r
+           if (mb_to_wc(codepage, flg, tbuf, 1, unitab + i, 1)\r
+               != 1)\r
+               unitab[i] = 0xFFFD;\r
+       }\r
+    } else {\r
+       int j = 256 - cp_list[codepage & 0xFFFF].cp_size;\r
+       for (i = 0; i < max; i++)\r
+           unitab[i] = i;\r
+       for (i = j; i < max; i++)\r
+           unitab[i] = cp_list[codepage & 0xFFFF].cp_table[i - j];\r
+    }\r
+}\r
+\r
+int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen,\r
+            char *mbstr, int mblen, char *defchr, int *defused,\r
+            struct unicode_data *ucsdata)\r
+{\r
+    char *p;\r
+    int i;\r
+    if (ucsdata && codepage == ucsdata->line_codepage && ucsdata->uni_tbl) {\r
+       /* Do this by array lookup if we can. */\r
+       if (wclen < 0) {\r
+           for (wclen = 0; wcstr[wclen++] ;);   /* will include the NUL */\r
+       }\r
+       for (p = mbstr, i = 0; i < wclen; i++) {\r
+           wchar_t ch = wcstr[i];\r
+           int by;\r
+           char *p1;\r
+           if (ucsdata->uni_tbl && (p1 = ucsdata->uni_tbl[(ch >> 8) & 0xFF])\r
+               && (by = p1[ch & 0xFF]))\r
+               *p++ = by;\r
+           else if (ch < 0x80)\r
+               *p++ = (char) ch;\r
+           else if (defchr) {\r
+               int j;\r
+               for (j = 0; defchr[j]; j++)\r
+                   *p++ = defchr[j];\r
+               if (defused) *defused = 1;\r
+           }\r
+#if 1\r
+           else\r
+               *p++ = '.';\r
+#endif\r
+           assert(p - mbstr < mblen);\r
+       }\r
+       return p - mbstr;\r
+    } else\r
+       return WideCharToMultiByte(codepage, flags, wcstr, wclen,\r
+                                  mbstr, mblen, defchr, defused);\r
+}\r
+\r
+int mb_to_wc(int codepage, int flags, char *mbstr, int mblen,\r
+            wchar_t *wcstr, int wclen)\r
+{\r
+    return MultiByteToWideChar(codepage, flags, mbstr, mblen, wcstr, wclen);\r
+}\r
+\r
+int is_dbcs_leadbyte(int codepage, char byte)\r
+{\r
+    return IsDBCSLeadByteEx(codepage, byte);\r
+}\r
diff --git a/putty/WINDOWS/WINUTILS.C b/putty/WINDOWS/WINUTILS.C
new file mode 100644 (file)
index 0000000..2daf1eb
--- /dev/null
@@ -0,0 +1,598 @@
+/*\r
+ * winutils.c: miscellaneous Windows utilities for GUI apps\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+#include "misc.h"\r
+\r
+#ifdef TESTMODE\r
+/* Definitions to allow this module to be compiled standalone for testing\r
+ * split_into_argv(). */\r
+#define smalloc malloc\r
+#define srealloc realloc\r
+#define sfree free\r
+#endif\r
+\r
+/*\r
+ * GetOpenFileName/GetSaveFileName tend to muck around with the process'\r
+ * working directory on at least some versions of Windows.\r
+ * Here's a wrapper that gives more control over this, and hides a little\r
+ * bit of other grottiness.\r
+ */\r
+\r
+struct filereq_tag {\r
+    TCHAR cwd[MAX_PATH];\r
+};\r
+\r
+/*\r
+ * `of' is expected to be initialised with most interesting fields, but\r
+ * this function does some administrivia. (assume `of' was memset to 0)\r
+ * save==1 -> GetSaveFileName; save==0 -> GetOpenFileName\r
+ * `state' is optional.\r
+ */\r
+BOOL request_file(filereq *state, OPENFILENAME *of, int preserve, int save)\r
+{\r
+    TCHAR cwd[MAX_PATH]; /* process CWD */\r
+    BOOL ret;\r
+\r
+    /* Get process CWD */\r
+    if (preserve) {\r
+       DWORD r = GetCurrentDirectory(lenof(cwd), cwd);\r
+       if (r == 0 || r >= lenof(cwd))\r
+           /* Didn't work, oh well. Stop trying to be clever. */\r
+           preserve = 0;\r
+    }\r
+\r
+    /* Open the file requester, maybe setting lpstrInitialDir */\r
+    {\r
+#ifdef OPENFILENAME_SIZE_VERSION_400\r
+       of->lStructSize = OPENFILENAME_SIZE_VERSION_400;\r
+#else\r
+       of->lStructSize = sizeof(*of);\r
+#endif\r
+       of->lpstrInitialDir = (state && state->cwd[0]) ? state->cwd : NULL;\r
+       /* Actually put up the requester. */\r
+       ret = save ? GetSaveFileName(of) : GetOpenFileName(of);\r
+    }\r
+\r
+    /* Get CWD left by requester */\r
+    if (state) {\r
+       DWORD r = GetCurrentDirectory(lenof(state->cwd), state->cwd);\r
+       if (r == 0 || r >= lenof(state->cwd))\r
+           /* Didn't work, oh well. */\r
+           state->cwd[0] = '\0';\r
+    }\r
+    \r
+    /* Restore process CWD */\r
+    if (preserve)\r
+       /* If it fails, there's not much we can do. */\r
+       (void) SetCurrentDirectory(cwd);\r
+\r
+    return ret;\r
+}\r
+\r
+filereq *filereq_new(void)\r
+{\r
+    filereq *ret = snew(filereq);\r
+    ret->cwd[0] = '\0';\r
+    return ret;\r
+}\r
+\r
+void filereq_free(filereq *state)\r
+{\r
+    sfree(state);\r
+}\r
+\r
+/*\r
+ * Message box with optional context help.\r
+ */\r
+\r
+/* Callback function to launch context help. */\r
+static VOID CALLBACK message_box_help_callback(LPHELPINFO lpHelpInfo)\r
+{\r
+    char *context = NULL;\r
+#define CHECK_CTX(name) \\r
+    do { \\r
+       if (lpHelpInfo->dwContextId == WINHELP_CTXID_ ## name) \\r
+           context = WINHELP_CTX_ ## name; \\r
+    } while (0)\r
+    CHECK_CTX(errors_hostkey_absent);\r
+    CHECK_CTX(errors_hostkey_changed);\r
+    CHECK_CTX(errors_cantloadkey);\r
+    CHECK_CTX(option_cleanup);\r
+    CHECK_CTX(pgp_fingerprints);\r
+#undef CHECK_CTX\r
+    if (context)\r
+       launch_help(hwnd, context);\r
+}\r
+\r
+int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid)\r
+{\r
+    MSGBOXPARAMS mbox;\r
+    \r
+    /*\r
+     * We use MessageBoxIndirect() because it allows us to specify a\r
+     * callback function for the Help button.\r
+     */\r
+    mbox.cbSize = sizeof(mbox);\r
+    /* Assumes the globals `hinst' and `hwnd' have sensible values. */\r
+    mbox.hInstance = hinst;\r
+    mbox.hwndOwner = hwnd;\r
+    mbox.lpfnMsgBoxCallback = &message_box_help_callback;\r
+    mbox.dwLanguageId = LANG_NEUTRAL;\r
+    mbox.lpszText = text;\r
+    mbox.lpszCaption = caption;\r
+    mbox.dwContextHelpId = helpctxid;\r
+    mbox.dwStyle = style;\r
+    if (helpctxid != 0 && has_help()) mbox.dwStyle |= MB_HELP;\r
+    return MessageBoxIndirect(&mbox);\r
+}\r
+\r
+/*\r
+ * Display the fingerprints of the PGP Master Keys to the user.\r
+ */\r
+void pgp_fingerprints(void)\r
+{\r
+    message_box("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"\r
+               "be used to establish a trust path from this executable to another\n"\r
+               "one. See the manual for more information.\n"\r
+               "(Note: these fingerprints have nothing to do with SSH!)\n"\r
+               "\n"\r
+               "PuTTY Master Key (RSA), 1024-bit:\n"\r
+               "  " PGP_RSA_MASTER_KEY_FP "\n"\r
+               "PuTTY Master Key (DSA), 1024-bit:\n"\r
+               "  " PGP_DSA_MASTER_KEY_FP,\r
+               "PGP fingerprints", MB_ICONINFORMATION | MB_OK,\r
+               HELPCTXID(pgp_fingerprints));\r
+}\r
+\r
+/*\r
+ * Split a complete command line into argc/argv, attempting to do\r
+ * it exactly the same way Windows itself would do it (so that\r
+ * console utilities, which receive argc and argv from Windows,\r
+ * will have their command lines processed in the same way as GUI\r
+ * utilities which get a whole command line and must break it\r
+ * themselves).\r
+ * \r
+ * Does not modify the input command line.\r
+ * \r
+ * The final parameter (argstart) is used to return a second array\r
+ * of char * pointers, the same length as argv, each one pointing\r
+ * at the start of the corresponding element of argv in the\r
+ * original command line. So if you get half way through processing\r
+ * your command line in argc/argv form and then decide you want to\r
+ * treat the rest as a raw string, you can. If you don't want to,\r
+ * `argstart' can be safely left NULL.\r
+ */\r
+void split_into_argv(char *cmdline, int *argc, char ***argv,\r
+                    char ***argstart)\r
+{\r
+    char *p;\r
+    char *outputline, *q;\r
+    char **outputargv, **outputargstart;\r
+    int outputargc;\r
+\r
+    /*\r
+     * At first glance the rules appeared to be:\r
+     *\r
+     *  - Single quotes are not special characters.\r
+     *\r
+     *  - Double quotes are removed, but within them spaces cease\r
+     *    to be special.\r
+     *\r
+     *  - Backslashes are _only_ special when a sequence of them\r
+     *    appear just before a double quote. In this situation,\r
+     *    they are treated like C backslashes: so \" just gives a\r
+     *    literal quote, \\" gives a literal backslash and then\r
+     *    opens or closes a double-quoted segment, \\\" gives a\r
+     *    literal backslash and then a literal quote, \\\\" gives\r
+     *    two literal backslashes and then opens/closes a\r
+     *    double-quoted segment, and so forth. Note that this\r
+     *    behaviour is identical inside and outside double quotes.\r
+     *\r
+     *  - Two successive double quotes become one literal double\r
+     *    quote, but only _inside_ a double-quoted segment.\r
+     *    Outside, they just form an empty double-quoted segment\r
+     *    (which may cause an empty argument word).\r
+     *\r
+     *  - That only leaves the interesting question of what happens\r
+     *    when one or more backslashes precedes two or more double\r
+     *    quotes, starting inside a double-quoted string. And the\r
+     *    answer to that appears somewhat bizarre. Here I tabulate\r
+     *    number of backslashes (across the top) against number of\r
+     *    quotes (down the left), and indicate how many backslashes\r
+     *    are output, how many quotes are output, and whether a\r
+     *    quoted segment is open at the end of the sequence:\r
+     * \r
+     *                      backslashes\r
+     * \r
+     *               0         1      2      3      4\r
+     * \r
+     *         0   0,0,y  |  1,0,y  2,0,y  3,0,y  4,0,y\r
+     *            --------+-----------------------------\r
+     *         1   0,0,n  |  0,1,y  1,0,n  1,1,y  2,0,n\r
+     *    q    2   0,1,n  |  0,1,n  1,1,n  1,1,n  2,1,n\r
+     *    u    3   0,1,y  |  0,2,n  1,1,y  1,2,n  2,1,y\r
+     *    o    4   0,1,n  |  0,2,y  1,1,n  1,2,y  2,1,n\r
+     *    t    5   0,2,n  |  0,2,n  1,2,n  1,2,n  2,2,n\r
+     *    e    6   0,2,y  |  0,3,n  1,2,y  1,3,n  2,2,y\r
+     *    s    7   0,2,n  |  0,3,y  1,2,n  1,3,y  2,2,n\r
+     *         8   0,3,n  |  0,3,n  1,3,n  1,3,n  2,3,n\r
+     *         9   0,3,y  |  0,4,n  1,3,y  1,4,n  2,3,y\r
+     *        10   0,3,n  |  0,4,y  1,3,n  1,4,y  2,3,n\r
+     *        11   0,4,n  |  0,4,n  1,4,n  1,4,n  2,4,n\r
+     * \r
+     * \r
+     *      [Test fragment was of the form "a\\\"""b c" d.]\r
+     * \r
+     * There is very weird mod-3 behaviour going on here in the\r
+     * number of quotes, and it even applies when there aren't any\r
+     * backslashes! How ghastly.\r
+     * \r
+     * With a bit of thought, this extremely odd diagram suddenly\r
+     * coalesced itself into a coherent, if still ghastly, model of\r
+     * how things work:\r
+     * \r
+     *  - As before, backslashes are only special when one or more\r
+     *    of them appear contiguously before at least one double\r
+     *    quote. In this situation the backslashes do exactly what\r
+     *    you'd expect: each one quotes the next thing in front of\r
+     *    it, so you end up with n/2 literal backslashes (if n is\r
+     *    even) or (n-1)/2 literal backslashes and a literal quote\r
+     *    (if n is odd). In the latter case the double quote\r
+     *    character right after the backslashes is used up.\r
+     * \r
+     *  - After that, any remaining double quotes are processed. A\r
+     *    string of contiguous unescaped double quotes has a mod-3\r
+     *    behaviour:\r
+     * \r
+     *     * inside a quoted segment, a quote ends the segment.\r
+     *     * _immediately_ after ending a quoted segment, a quote\r
+     *       simply produces a literal quote.\r
+     *     * otherwise, outside a quoted segment, a quote begins a\r
+     *       quoted segment.\r
+     * \r
+     *    So, for example, if we started inside a quoted segment\r
+     *    then two contiguous quotes would close the segment and\r
+     *    produce a literal quote; three would close the segment,\r
+     *    produce a literal quote, and open a new segment. If we\r
+     *    started outside a quoted segment, then two contiguous\r
+     *    quotes would open and then close a segment, producing no\r
+     *    output (but potentially creating a zero-length argument);\r
+     *    but three quotes would open and close a segment and then\r
+     *    produce a literal quote.\r
+     */\r
+\r
+    /*\r
+     * First deal with the simplest of all special cases: if there\r
+     * aren't any arguments, return 0,NULL,NULL.\r
+     */\r
+    while (*cmdline && isspace(*cmdline)) cmdline++;\r
+    if (!*cmdline) {\r
+       if (argc) *argc = 0;\r
+       if (argv) *argv = NULL;\r
+       if (argstart) *argstart = NULL;\r
+       return;\r
+    }\r
+\r
+    /*\r
+     * This will guaranteeably be big enough; we can realloc it\r
+     * down later.\r
+     */\r
+    outputline = snewn(1+strlen(cmdline), char);\r
+    outputargv = snewn(strlen(cmdline)+1 / 2, char *);\r
+    outputargstart = snewn(strlen(cmdline)+1 / 2, char *);\r
+\r
+    p = cmdline; q = outputline; outputargc = 0;\r
+\r
+    while (*p) {\r
+       int quote;\r
+\r
+       /* Skip whitespace searching for start of argument. */\r
+       while (*p && isspace(*p)) p++;\r
+       if (!*p) break;\r
+\r
+       /* We have an argument; start it. */\r
+       outputargv[outputargc] = q;\r
+       outputargstart[outputargc] = p;\r
+       outputargc++;\r
+       quote = 0;\r
+\r
+       /* Copy data into the argument until it's finished. */\r
+       while (*p) {\r
+           if (!quote && isspace(*p))\r
+               break;                 /* argument is finished */\r
+\r
+           if (*p == '"' || *p == '\\') {\r
+               /*\r
+                * We have a sequence of zero or more backslashes\r
+                * followed by a sequence of zero or more quotes.\r
+                * Count up how many of each, and then deal with\r
+                * them as appropriate.\r
+                */\r
+               int i, slashes = 0, quotes = 0;\r
+               while (*p == '\\') slashes++, p++;\r
+               while (*p == '"') quotes++, p++;\r
+\r
+               if (!quotes) {\r
+                   /*\r
+                    * Special case: if there are no quotes,\r
+                    * slashes are not special at all, so just copy\r
+                    * n slashes to the output string.\r
+                    */\r
+                   while (slashes--) *q++ = '\\';\r
+               } else {\r
+                   /* Slashes annihilate in pairs. */\r
+                   while (slashes >= 2) slashes -= 2, *q++ = '\\';\r
+\r
+                   /* One remaining slash takes out the first quote. */\r
+                   if (slashes) quotes--, *q++ = '"';\r
+\r
+                   if (quotes > 0) {\r
+                       /* Outside a quote segment, a quote starts one. */\r
+                       if (!quote) quotes--, quote = 1;\r
+\r
+                       /* Now we produce (n+1)/3 literal quotes... */\r
+                       for (i = 3; i <= quotes+1; i += 3) *q++ = '"';\r
+\r
+                       /* ... and end in a quote segment iff 3 divides n. */\r
+                       quote = (quotes % 3 == 0);\r
+                   }\r
+               }\r
+           } else {\r
+               *q++ = *p++;\r
+           }\r
+       }\r
+\r
+       /* At the end of an argument, just append a trailing NUL. */\r
+       *q++ = '\0';\r
+    }\r
+\r
+    outputargv = sresize(outputargv, outputargc, char *);\r
+    outputargstart = sresize(outputargstart, outputargc, char *);\r
+\r
+    if (argc) *argc = outputargc;\r
+    if (argv) *argv = outputargv; else sfree(outputargv);\r
+    if (argstart) *argstart = outputargstart; else sfree(outputargstart);\r
+}\r
+\r
+#ifdef TESTMODE\r
+\r
+const struct argv_test {\r
+    const char *cmdline;\r
+    const char *argv[10];\r
+} argv_tests[] = {\r
+    /*\r
+     * We generate this set of tests by invoking ourself with\r
+     * `-generate'.\r
+     */\r
+    {"ab c\" d", {"ab", "c d", NULL}},\r
+    {"a\"b c\" d", {"ab c", "d", NULL}},\r
+    {"a\"\"b c\" d", {"ab", "c d", NULL}},\r
+    {"a\"\"\"b c\" d", {"a\"b", "c d", NULL}},\r
+    {"a\"\"\"\"b c\" d", {"a\"b c", "d", NULL}},\r
+    {"a\"\"\"\"\"b c\" d", {"a\"b", "c d", NULL}},\r
+    {"a\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},\r
+    {"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"a\\b c\" d", {"a\\b", "c d", NULL}},\r
+    {"a\\\"b c\" d", {"a\"b", "c d", NULL}},\r
+    {"a\\\"\"b c\" d", {"a\"b c", "d", NULL}},\r
+    {"a\\\"\"\"b c\" d", {"a\"b", "c d", NULL}},\r
+    {"a\\\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"a\\\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},\r
+    {"a\\\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},\r
+    {"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}},\r
+    {"a\\\\b c\" d", {"a\\\\b", "c d", NULL}},\r
+    {"a\\\\\"b c\" d", {"a\\b c", "d", NULL}},\r
+    {"a\\\\\"\"b c\" d", {"a\\b", "c d", NULL}},\r
+    {"a\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},\r
+    {"a\\\\\"\"\"\"b c\" d", {"a\\\"b c", "d", NULL}},\r
+    {"a\\\\\"\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},\r
+    {"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},\r
+    {"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"a\\\\\\b c\" d", {"a\\\\\\b", "c d", NULL}},\r
+    {"a\\\\\\\"b c\" d", {"a\\\"b", "c d", NULL}},\r
+    {"a\\\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}},\r
+    {"a\\\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},\r
+    {"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},\r
+    {"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},\r
+    {"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}},\r
+    {"a\\\\\\\\b c\" d", {"a\\\\\\\\b", "c d", NULL}},\r
+    {"a\\\\\\\\\"b c\" d", {"a\\\\b c", "d", NULL}},\r
+    {"a\\\\\\\\\"\"b c\" d", {"a\\\\b", "c d", NULL}},\r
+    {"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},\r
+    {"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}},\r
+    {"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},\r
+    {"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},\r
+    {"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}},\r
+    {"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},\r
+    {"\"ab c\" d", {"ab c", "d", NULL}},\r
+    {"\"a\"b c\" d", {"ab", "c d", NULL}},\r
+    {"\"a\"\"b c\" d", {"a\"b", "c d", NULL}},\r
+    {"\"a\"\"\"b c\" d", {"a\"b c", "d", NULL}},\r
+    {"\"a\"\"\"\"b c\" d", {"a\"b", "c d", NULL}},\r
+    {"\"a\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"\"a\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},\r
+    {"\"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"\"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},\r
+    {"\"a\\b c\" d", {"a\\b c", "d", NULL}},\r
+    {"\"a\\\"b c\" d", {"a\"b c", "d", NULL}},\r
+    {"\"a\\\"\"b c\" d", {"a\"b", "c d", NULL}},\r
+    {"\"a\\\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"\"a\\\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},\r
+    {"\"a\\\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},\r
+    {"\"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},\r
+    {"\"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}},\r
+    {"\"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},\r
+    {"\"a\\\\b c\" d", {"a\\\\b c", "d", NULL}},\r
+    {"\"a\\\\\"b c\" d", {"a\\b", "c d", NULL}},\r
+    {"\"a\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}},\r
+    {"\"a\\\\\"\"\"b c\" d", {"a\\\"b c", "d", NULL}},\r
+    {"\"a\\\\\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},\r
+    {"\"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},\r
+    {"\"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\\b c\" d", {"a\\\\\\b c", "d", NULL}},\r
+    {"\"a\\\\\\\"b c\" d", {"a\\\"b c", "d", NULL}},\r
+    {"\"a\\\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},\r
+    {"\"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}},\r
+    {"\"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\\b c\" d", {"a\\\\\\\\b c", "d", NULL}},\r
+    {"\"a\\\\\\\\\"b c\" d", {"a\\\\b", "c d", NULL}},\r
+    {"\"a\\\\\\\\\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}},\r
+    {"\"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}},\r
+    {"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},\r
+    {"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}},\r
+};\r
+\r
+int main(int argc, char **argv)\r
+{\r
+    int i, j;\r
+\r
+    if (argc > 1) {\r
+       /*\r
+        * Generation of tests.\r
+        * \r
+        * Given `-splat <args>', we print out a C-style\r
+        * representation of each argument (in the form "a", "b",\r
+        * NULL), backslash-escaping each backslash and double\r
+        * quote.\r
+        * \r
+        * Given `-split <string>', we first doctor `string' by\r
+        * turning forward slashes into backslashes, single quotes\r
+        * into double quotes and underscores into spaces; and then\r
+        * we feed the resulting string to ourself with `-splat'.\r
+        * \r
+        * Given `-generate', we concoct a variety of fun test\r
+        * cases, encode them in quote-safe form (mapping \, " and\r
+        * space to /, ' and _ respectively) and feed each one to\r
+        * `-split'.\r
+        */\r
+       if (!strcmp(argv[1], "-splat")) {\r
+           int i;\r
+           char *p;\r
+           for (i = 2; i < argc; i++) {\r
+               putchar('"');\r
+               for (p = argv[i]; *p; p++) {\r
+                   if (*p == '\\' || *p == '"')\r
+                       putchar('\\');\r
+                   putchar(*p);\r
+               }\r
+               printf("\", ");\r
+           }\r
+           printf("NULL");\r
+           return 0;\r
+       }\r
+\r
+       if (!strcmp(argv[1], "-split") && argc > 2) {\r
+           char *str = malloc(20 + strlen(argv[0]) + strlen(argv[2]));\r
+           char *p, *q;\r
+\r
+           q = str + sprintf(str, "%s -splat ", argv[0]);\r
+           printf("    {\"");\r
+           for (p = argv[2]; *p; p++, q++) {\r
+               switch (*p) {\r
+                 case '/':  printf("\\\\"); *q = '\\'; break;\r
+                 case '\'': printf("\\\""); *q = '"';  break;\r
+                 case '_':  printf(" ");    *q = ' ';  break;\r
+                 default:   putchar(*p);    *q = *p;   break;\r
+               }\r
+           }\r
+           *p = '\0';\r
+           printf("\", {");\r
+           fflush(stdout);\r
+\r
+           system(str);\r
+\r
+           printf("}},\n");\r
+\r
+           return 0;\r
+       }\r
+\r
+       if (!strcmp(argv[1], "-generate")) {\r
+           char *teststr, *p;\r
+           int i, initialquote, backslashes, quotes;\r
+\r
+           teststr = malloc(200 + strlen(argv[0]));\r
+\r
+           for (initialquote = 0; initialquote <= 1; initialquote++) {\r
+               for (backslashes = 0; backslashes < 5; backslashes++) {\r
+                   for (quotes = 0; quotes < 9; quotes++) {\r
+                       p = teststr + sprintf(teststr, "%s -split ", argv[0]);\r
+                       if (initialquote) *p++ = '\'';\r
+                       *p++ = 'a';\r
+                       for (i = 0; i < backslashes; i++) *p++ = '/';\r
+                       for (i = 0; i < quotes; i++) *p++ = '\'';\r
+                       *p++ = 'b';\r
+                       *p++ = '_';\r
+                       *p++ = 'c';\r
+                       *p++ = '\'';\r
+                       *p++ = '_';\r
+                       *p++ = 'd';\r
+                       *p = '\0';\r
+\r
+                       system(teststr);\r
+                   }\r
+               }\r
+           }\r
+           return 0;\r
+       }\r
+\r
+       fprintf(stderr, "unrecognised option: \"%s\"\n", argv[1]);\r
+       return 1;\r
+    }\r
+\r
+    /*\r
+     * If we get here, we were invoked with no arguments, so just\r
+     * run the tests.\r
+     */\r
+\r
+    for (i = 0; i < lenof(argv_tests); i++) {\r
+       int ac;\r
+       char **av;\r
+\r
+       split_into_argv(argv_tests[i].cmdline, &ac, &av);\r
+\r
+       for (j = 0; j < ac && argv_tests[i].argv[j]; j++) {\r
+           if (strcmp(av[j], argv_tests[i].argv[j])) {\r
+               printf("failed test %d (|%s|) arg %d: |%s| should be |%s|\n",\r
+                      i, argv_tests[i].cmdline,\r
+                      j, av[j], argv_tests[i].argv[j]);\r
+           }\r
+#ifdef VERBOSE\r
+           else {\r
+               printf("test %d (|%s|) arg %d: |%s| == |%s|\n",\r
+                      i, argv_tests[i].cmdline,\r
+                      j, av[j], argv_tests[i].argv[j]);\r
+           }\r
+#endif\r
+       }\r
+       if (j < ac)\r
+           printf("failed test %d (|%s|): %d args returned, should be %d\n",\r
+                  i, argv_tests[i].cmdline, ac, j);\r
+       if (argv_tests[i].argv[j])\r
+           printf("failed test %d (|%s|): %d args returned, should be more\n",\r
+                  i, argv_tests[i].cmdline, ac);\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+#endif\r
diff --git a/putty/WINDOWS/WINX11.C b/putty/WINDOWS/WINX11.C
new file mode 100644 (file)
index 0000000..c8951b0
--- /dev/null
@@ -0,0 +1,18 @@
+/*\r
+ * winx11.c: fetch local auth data for X forwarding.\r
+ */\r
+\r
+#include <ctype.h>\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+\r
+void platform_get_x11_auth(struct X11Display *disp, const Config *cfg)\r
+{\r
+    if (cfg->xauthfile.path[0])\r
+       x11_get_auth_from_authfile(disp, cfg->xauthfile.path);\r
+}\r
+\r
+const int platform_uses_x11_unix_by_default = FALSE;\r
diff --git a/putty/WINDOWS/WIN_RES.H b/putty/WINDOWS/WIN_RES.H
new file mode 100644 (file)
index 0000000..3c5ec74
--- /dev/null
@@ -0,0 +1,34 @@
+/*\r
+ * win_res.h - constants shared between win_res.rc2 and the C code.\r
+ */\r
+\r
+#ifndef PUTTY_WIN_RES_H\r
+#define PUTTY_WIN_RES_H\r
+\r
+#define IDI_MAINICON     200\r
+#define IDI_CFGICON      201\r
+\r
+#define IDD_MAINBOX      102\r
+#define IDD_LOGBOX       110\r
+#define IDD_ABOUTBOX     111\r
+#define IDD_RECONF       112\r
+#define IDD_LICENCEBOX   113\r
+\r
+#define IDN_LIST        1001\r
+#define IDN_COPY        1002\r
+\r
+#define IDA_ICON        1001\r
+#define IDA_TEXT1       1002\r
+#define IDA_VERSION     1003\r
+#define IDA_TEXT2       1004\r
+#define IDA_LICENCE     1005\r
+#define IDA_WEB         1006\r
+\r
+#define IDC_TAB         1001\r
+#define IDC_TABSTATIC1  1002\r
+#define IDC_TABSTATIC2  1003\r
+#define IDC_TABLIST     1004\r
+#define IDC_HELPBTN     1005\r
+#define IDC_ABOUT       1006\r
+\r
+#endif\r
diff --git a/putty/WINDOWS/WIN_RES.RC2 b/putty/WINDOWS/WIN_RES.RC2
new file mode 100644 (file)
index 0000000..e159c56
--- /dev/null
@@ -0,0 +1,92 @@
+/*\r
+ * Windows resources shared between PuTTY and PuTTYtel, to be #include'd\r
+ * after defining appropriate macros.\r
+ * Note that many of these strings mention PuTTY. Due to restrictions in\r
+ * VC's handling of string concatenation, this can't easily be fixed.\r
+ * It's fixed up at runtime.\r
+ * FIXME: This file is called '.rc2' rather than '.rc' to avoid MSVC trying\r
+ * to compile it on its own when using the project files. Nicer solutions\r
+ * welcome.\r
+ */\r
+\r
+#include "win_res.h"\r
+\r
+IDI_MAINICON ICON "putty.ico"\r
+\r
+IDI_CFGICON ICON "puttycfg.ico"\r
+\r
+/* Accelerators used: clw */\r
+IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 214, 70\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "About PuTTY"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    DEFPUSHBUTTON "&Close", IDOK, 160, 52, 48, 14\r
+    PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 52, 70, 14\r
+    PUSHBUTTON "Visit &Web Site", IDA_WEB, 84, 52, 70, 14\r
+    CTEXT "PuTTY", IDA_TEXT1, 10, 6, 194, 8\r
+    CTEXT "", IDA_VERSION, 10, 16, 194, 16\r
+    CTEXT "\251 1997-2011 Simon Tatham. All rights reserved.",\r
+          IDA_TEXT2, 10, 34, 194, 16\r
+END\r
+\r
+/* Accelerators used: aco */\r
+IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 300, 252\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "PuTTY Configuration"\r
+FONT 8, "MS Shell Dlg"\r
+CLASS "PuTTYConfigBox"\r
+BEGIN\r
+END\r
+\r
+/* Accelerators used: co */\r
+IDD_LOGBOX DIALOG DISCARDABLE 100, 20, 300, 119\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "PuTTY Event Log"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    DEFPUSHBUTTON "&Close", IDOK, 135, 102, 44, 14\r
+    PUSHBUTTON "C&opy", IDN_COPY, 81, 102, 44, 14\r
+    LISTBOX IDN_LIST, 3, 3, 294, 95, LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | LBS_EXTENDEDSEL\r
+END\r
+\r
+/* No accelerators used */\r
+IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 263\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
+CAPTION "PuTTY Licence"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14\r
+\r
+    LTEXT "Copyright \251 1997-2011 Simon Tatham", 1000, 10, 10, 206, 8\r
+\r
+    LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8\r
+    LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8\r
+    LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8\r
+    LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8\r
+\r
+    LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8\r
+    LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8\r
+    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8\r
+    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8\r
+    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8\r
+    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8\r
+    LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8\r
+\r
+    LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8\r
+    LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8\r
+\r
+    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8\r
+    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8\r
+    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8\r
+    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8\r
+    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8\r
+    LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8\r
+    LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8\r
+    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8\r
+    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8\r
+    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8\r
+\r
+END\r
+\r
+#include "version.rc2"\r
diff --git a/putty/X11FWD.C b/putty/X11FWD.C
new file mode 100644 (file)
index 0000000..9f22a23
--- /dev/null
@@ -0,0 +1,791 @@
+/*\r
+ * Platform-independent bits of X11 forwarding.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+#include <time.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+#include "tree234.h"\r
+\r
+#define GET_16BIT(endian, cp) \\r
+  (endian=='B' ? GET_16BIT_MSB_FIRST(cp) : GET_16BIT_LSB_FIRST(cp))\r
+\r
+#define PUT_16BIT(endian, cp, val) \\r
+  (endian=='B' ? PUT_16BIT_MSB_FIRST(cp, val) : PUT_16BIT_LSB_FIRST(cp, val))\r
+\r
+const char *const x11_authnames[] = {\r
+    "", "MIT-MAGIC-COOKIE-1", "XDM-AUTHORIZATION-1"\r
+};\r
+\r
+struct XDMSeen {\r
+    unsigned int time;\r
+    unsigned char clientid[6];\r
+};\r
+\r
+struct X11Private {\r
+    const struct plug_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+    unsigned char firstpkt[12];               /* first X data packet */\r
+    struct X11Display *disp;\r
+    char *auth_protocol;\r
+    unsigned char *auth_data;\r
+    int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;\r
+    int verified;\r
+    int throttled, throttle_override;\r
+    unsigned long peer_ip;\r
+    int peer_port;\r
+    void *c;                          /* data used by ssh.c */\r
+    Socket s;\r
+};\r
+\r
+static int xdmseen_cmp(void *a, void *b)\r
+{\r
+    struct XDMSeen *sa = a, *sb = b;\r
+    return sa->time > sb->time ? 1 :\r
+          sa->time < sb->time ? -1 :\r
+           memcmp(sa->clientid, sb->clientid, sizeof(sa->clientid));\r
+}\r
+\r
+/* Do-nothing "plug" implementation, used by x11_setup_display() when it\r
+ * creates a trial connection (and then immediately closes it).\r
+ * XXX: bit out of place here, could in principle live in a platform-\r
+ *      independent network.c or something */\r
+static void dummy_plug_log(Plug p, int type, SockAddr addr, int port,\r
+                          const char *error_msg, int error_code) { }\r
+static int dummy_plug_closing\r
+     (Plug p, const char *error_msg, int error_code, int calling_back)\r
+{ return 1; }\r
+static int dummy_plug_receive(Plug p, int urgent, char *data, int len)\r
+{ return 1; }\r
+static void dummy_plug_sent(Plug p, int bufsize) { }\r
+static int dummy_plug_accepting(Plug p, OSSocket sock) { return 1; }\r
+static const struct plug_function_table dummy_plug = {\r
+    dummy_plug_log, dummy_plug_closing, dummy_plug_receive,\r
+    dummy_plug_sent, dummy_plug_accepting\r
+};\r
+\r
+struct X11Display *x11_setup_display(char *display, int authtype,\r
+                                    const Config *cfg)\r
+{\r
+    struct X11Display *disp = snew(struct X11Display);\r
+    char *localcopy;\r
+    int i;\r
+\r
+    if (!display || !*display) {\r
+       localcopy = platform_get_x_display();\r
+       if (!localcopy || !*localcopy) {\r
+           sfree(localcopy);\r
+           localcopy = dupstr(":0");  /* plausible default for any platform */\r
+       }\r
+    } else\r
+       localcopy = dupstr(display);\r
+\r
+    /*\r
+     * Parse the display name.\r
+     *\r
+     * We expect this to have one of the following forms:\r
+     * \r
+     *  - the standard X format which looks like\r
+     *    [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ]\r
+     *    (X11 also permits a double colon to indicate DECnet, but\r
+     *    that's not our problem, thankfully!)\r
+     *\r
+     *         - only seen in the wild on MacOS (so far): a pathname to a\r
+     *           Unix-domain socket, which will typically and confusingly\r
+     *           end in ":0", and which I'm currently distinguishing from\r
+     *           the standard scheme by noting that it starts with '/'.\r
+     */\r
+    if (localcopy[0] == '/') {\r
+       disp->unixsocketpath = localcopy;\r
+       disp->unixdomain = TRUE;\r
+       disp->hostname = NULL;\r
+       disp->displaynum = -1;\r
+       disp->screennum = 0;\r
+       disp->addr = NULL;\r
+    } else {\r
+       char *colon, *dot, *slash;\r
+       char *protocol, *hostname;\r
+\r
+       colon = strrchr(localcopy, ':');\r
+       if (!colon) {\r
+           sfree(disp);\r
+           sfree(localcopy);\r
+           return NULL;               /* FIXME: report a specific error? */\r
+       }\r
+\r
+       *colon++ = '\0';\r
+       dot = strchr(colon, '.');\r
+       if (dot)\r
+           *dot++ = '\0';\r
+\r
+       disp->displaynum = atoi(colon);\r
+       if (dot)\r
+           disp->screennum = atoi(dot);\r
+       else\r
+           disp->screennum = 0;\r
+\r
+       protocol = NULL;\r
+       hostname = localcopy;\r
+       if (colon > localcopy) {\r
+           slash = strchr(localcopy, '/');\r
+           if (slash) {\r
+               *slash++ = '\0';\r
+               protocol = localcopy;\r
+               hostname = slash;\r
+           }\r
+       }\r
+\r
+       disp->hostname = *hostname ? dupstr(hostname) : NULL;\r
+\r
+       if (protocol)\r
+           disp->unixdomain = (!strcmp(protocol, "local") ||\r
+                               !strcmp(protocol, "unix"));\r
+       else if (!*hostname || !strcmp(hostname, "unix"))\r
+           disp->unixdomain = platform_uses_x11_unix_by_default;\r
+       else\r
+           disp->unixdomain = FALSE;\r
+\r
+       if (!disp->hostname && !disp->unixdomain)\r
+           disp->hostname = dupstr("localhost");\r
+\r
+       disp->unixsocketpath = NULL;\r
+       disp->addr = NULL;\r
+\r
+       sfree(localcopy);\r
+    }\r
+\r
+    /*\r
+     * Look up the display hostname, if we need to.\r
+     */\r
+    if (!disp->unixdomain) {\r
+       const char *err;\r
+\r
+       disp->port = 6000 + disp->displaynum;\r
+       disp->addr = name_lookup(disp->hostname, disp->port,\r
+                                &disp->realhost, cfg, ADDRTYPE_UNSPEC);\r
+    \r
+       if ((err = sk_addr_error(disp->addr)) != NULL) {\r
+           sk_addr_free(disp->addr);\r
+           sfree(disp->hostname);\r
+           sfree(disp->unixsocketpath);\r
+           return NULL;               /* FIXME: report an error */\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Try upgrading an IP-style localhost display to a Unix-socket\r
+     * display (as the standard X connection libraries do).\r
+     */\r
+    if (!disp->unixdomain && sk_address_is_local(disp->addr)) {\r
+       SockAddr ux = platform_get_x11_unix_address(NULL, disp->displaynum);\r
+       const char *err = sk_addr_error(ux);\r
+       if (!err) {\r
+           /* Create trial connection to see if there is a useful Unix-domain\r
+            * socket */\r
+           const struct plug_function_table *dummy = &dummy_plug;\r
+           Socket s = sk_new(sk_addr_dup(ux), 0, 0, 0, 0, 0, (Plug)&dummy);\r
+           err = sk_socket_error(s);\r
+           sk_close(s);\r
+       }\r
+       if (err) {\r
+           sk_addr_free(ux);\r
+       } else {\r
+           sk_addr_free(disp->addr);\r
+           disp->unixdomain = TRUE;\r
+           disp->addr = ux;\r
+           /* Fill in the rest in a moment */\r
+       }\r
+    }\r
+\r
+    if (disp->unixdomain) {\r
+       if (!disp->addr)\r
+           disp->addr = platform_get_x11_unix_address(disp->unixsocketpath,\r
+                                                      disp->displaynum);\r
+       if (disp->unixsocketpath)\r
+           disp->realhost = dupstr(disp->unixsocketpath);\r
+       else\r
+           disp->realhost = dupprintf("unix:%d", disp->displaynum);\r
+       disp->port = 0;\r
+    }\r
+\r
+    /*\r
+     * Invent the remote authorisation details.\r
+     */\r
+    if (authtype == X11_MIT) {\r
+       disp->remoteauthproto = X11_MIT;\r
+\r
+       /* MIT-MAGIC-COOKIE-1. Cookie size is 128 bits (16 bytes). */\r
+       disp->remoteauthdata = snewn(16, unsigned char);\r
+       for (i = 0; i < 16; i++)\r
+           disp->remoteauthdata[i] = random_byte();\r
+       disp->remoteauthdatalen = 16;\r
+\r
+       disp->xdmseen = NULL;\r
+    } else {\r
+       assert(authtype == X11_XDM);\r
+       disp->remoteauthproto = X11_XDM;\r
+\r
+       /* XDM-AUTHORIZATION-1. Cookie size is 16 bytes; byte 8 is zero. */\r
+       disp->remoteauthdata = snewn(16, unsigned char);\r
+       for (i = 0; i < 16; i++)\r
+           disp->remoteauthdata[i] = (i == 8 ? 0 : random_byte());\r
+       disp->remoteauthdatalen = 16;\r
+\r
+       disp->xdmseen = newtree234(xdmseen_cmp);\r
+    }\r
+    disp->remoteauthprotoname = dupstr(x11_authnames[disp->remoteauthproto]);\r
+    disp->remoteauthdatastring = snewn(disp->remoteauthdatalen * 2 + 1, char);\r
+    for (i = 0; i < disp->remoteauthdatalen; i++)\r
+       sprintf(disp->remoteauthdatastring + i*2, "%02x",\r
+               disp->remoteauthdata[i]);\r
+\r
+    /*\r
+     * Fetch the local authorisation details.\r
+     */\r
+    disp->localauthproto = X11_NO_AUTH;\r
+    disp->localauthdata = NULL;\r
+    disp->localauthdatalen = 0;\r
+    platform_get_x11_auth(disp, cfg);\r
+\r
+    return disp;\r
+}\r
+\r
+void x11_free_display(struct X11Display *disp)\r
+{\r
+    if (disp->xdmseen != NULL) {\r
+       struct XDMSeen *seen;\r
+       while ((seen = delpos234(disp->xdmseen, 0)) != NULL)\r
+           sfree(seen);\r
+       freetree234(disp->xdmseen);\r
+    }\r
+    sfree(disp->hostname);\r
+    sfree(disp->unixsocketpath);\r
+    if (disp->localauthdata)\r
+       memset(disp->localauthdata, 0, disp->localauthdatalen);\r
+    sfree(disp->localauthdata);\r
+    if (disp->remoteauthdata)\r
+       memset(disp->remoteauthdata, 0, disp->remoteauthdatalen);\r
+    sfree(disp->remoteauthdata);\r
+    sfree(disp->remoteauthprotoname);\r
+    sfree(disp->remoteauthdatastring);\r
+    sk_addr_free(disp->addr);\r
+    sfree(disp);\r
+}\r
+\r
+#define XDM_MAXSKEW 20*60      /* 20 minute clock skew should be OK */\r
+\r
+static char *x11_verify(unsigned long peer_ip, int peer_port,\r
+                       struct X11Display *disp, char *proto,\r
+                       unsigned char *data, int dlen)\r
+{\r
+    if (strcmp(proto, x11_authnames[disp->remoteauthproto]) != 0)\r
+       return "wrong authorisation protocol attempted";\r
+    if (disp->remoteauthproto == X11_MIT) {\r
+        if (dlen != disp->remoteauthdatalen)\r
+            return "MIT-MAGIC-COOKIE-1 data was wrong length";\r
+        if (memcmp(disp->remoteauthdata, data, dlen) != 0)\r
+            return "MIT-MAGIC-COOKIE-1 data did not match";\r
+    }\r
+    if (disp->remoteauthproto == X11_XDM) {\r
+       unsigned long t;\r
+       time_t tim;\r
+       int i;\r
+       struct XDMSeen *seen, *ret;\r
+\r
+        if (dlen != 24)\r
+            return "XDM-AUTHORIZATION-1 data was wrong length";\r
+       if (peer_port == -1)\r
+            return "cannot do XDM-AUTHORIZATION-1 without remote address data";\r
+       des_decrypt_xdmauth(disp->remoteauthdata+9, data, 24);\r
+        if (memcmp(disp->remoteauthdata, data, 8) != 0)\r
+            return "XDM-AUTHORIZATION-1 data failed check"; /* cookie wrong */\r
+       if (GET_32BIT_MSB_FIRST(data+8) != peer_ip)\r
+            return "XDM-AUTHORIZATION-1 data failed check";   /* IP wrong */\r
+       if ((int)GET_16BIT_MSB_FIRST(data+12) != peer_port)\r
+            return "XDM-AUTHORIZATION-1 data failed check";   /* port wrong */\r
+       t = GET_32BIT_MSB_FIRST(data+14);\r
+       for (i = 18; i < 24; i++)\r
+           if (data[i] != 0)          /* zero padding wrong */\r
+               return "XDM-AUTHORIZATION-1 data failed check";\r
+       tim = time(NULL);\r
+       if (abs(t - tim) > XDM_MAXSKEW)\r
+           return "XDM-AUTHORIZATION-1 time stamp was too far out";\r
+       seen = snew(struct XDMSeen);\r
+       seen->time = t;\r
+       memcpy(seen->clientid, data+8, 6);\r
+       assert(disp->xdmseen != NULL);\r
+       ret = add234(disp->xdmseen, seen);\r
+       if (ret != seen) {\r
+           sfree(seen);\r
+           return "XDM-AUTHORIZATION-1 data replayed";\r
+       }\r
+       /* While we're here, purge entries too old to be replayed. */\r
+       for (;;) {\r
+           seen = index234(disp->xdmseen, 0);\r
+           assert(seen != NULL);\r
+           if (t - seen->time <= XDM_MAXSKEW)\r
+               break;\r
+           sfree(delpos234(disp->xdmseen, 0));\r
+       }\r
+    }\r
+    /* implement other protocols here if ever required */\r
+    return NULL;\r
+}\r
+\r
+void x11_get_auth_from_authfile(struct X11Display *disp,\r
+                               const char *authfilename)\r
+{\r
+    FILE *authfp;\r
+    char *buf, *ptr, *str[4];\r
+    int len[4];\r
+    int family, protocol;\r
+    int ideal_match = FALSE;\r
+    char *ourhostname = get_hostname();\r
+\r
+    /*\r
+     * Normally we should look for precisely the details specified in\r
+     * `disp'. However, there's an oddity when the display is local:\r
+     * displays like "localhost:0" usually have their details stored\r
+     * in a Unix-domain-socket record (even if there isn't actually a\r
+     * real Unix-domain socket available, as with OpenSSH's proxy X11\r
+     * server).\r
+     *\r
+     * This is apparently a fudge to get round the meaninglessness of\r
+     * "localhost" in a shared-home-directory context -- xauth entries\r
+     * for Unix-domain sockets already disambiguate this by storing\r
+     * the *local* hostname in the conveniently-blank hostname field,\r
+     * but IP "localhost" records couldn't do this. So, typically, an\r
+     * IP "localhost" entry in the auth database isn't present and if\r
+     * it were it would be ignored.\r
+     *\r
+     * However, we don't entirely trust that (say) Windows X servers\r
+     * won't rely on a straight "localhost" entry, bad idea though\r
+     * that is; so if we can't find a Unix-domain-socket entry we'll\r
+     * fall back to an IP-based entry if we can find one.\r
+     */\r
+    int localhost = !disp->unixdomain && sk_address_is_local(disp->addr);\r
+\r
+    authfp = fopen(authfilename, "rb");\r
+    if (!authfp)\r
+       return;\r
+\r
+    /* Records in .Xauthority contain four strings of up to 64K each */\r
+    buf = snewn(65537 * 4, char);\r
+\r
+    while (!ideal_match) {\r
+       int c, i, j, match = FALSE;\r
+       \r
+#define GET do { c = fgetc(authfp); if (c == EOF) goto done; c = (unsigned char)c; } while (0)\r
+       /* Expect a big-endian 2-byte number giving address family */\r
+       GET; family = c;\r
+       GET; family = (family << 8) | c;\r
+       /* Then expect four strings, each composed of a big-endian 2-byte\r
+        * length field followed by that many bytes of data */\r
+       ptr = buf;\r
+       for (i = 0; i < 4; i++) {\r
+           GET; len[i] = c;\r
+           GET; len[i] = (len[i] << 8) | c;\r
+           str[i] = ptr;\r
+           for (j = 0; j < len[i]; j++) {\r
+               GET; *ptr++ = c;\r
+           }\r
+           *ptr++ = '\0';\r
+       }\r
+#undef GET\r
+\r
+       /*\r
+        * Now we have a full X authority record in memory. See\r
+        * whether it matches the display we're trying to\r
+        * authenticate to.\r
+        *\r
+        * The details we've just read should be interpreted as\r
+        * follows:\r
+        * \r
+        *  - 'family' is the network address family used to\r
+        *    connect to the display. 0 means IPv4; 6 means IPv6;\r
+        *    256 means Unix-domain sockets.\r
+        * \r
+        *  - str[0] is the network address itself. For IPv4 and\r
+        *    IPv6, this is a string of binary data of the\r
+        *    appropriate length (respectively 4 and 16 bytes)\r
+        *    representing the address in big-endian format, e.g.\r
+        *    7F 00 00 01 means IPv4 localhost. For Unix-domain\r
+        *    sockets, this is the host name of the machine on\r
+        *    which the Unix-domain display resides (so that an\r
+        *    .Xauthority file on a shared file system can contain\r
+        *    authority entries for Unix-domain displays on\r
+        *    several machines without them clashing).\r
+        * \r
+        *  - str[1] is the display number. I've no idea why\r
+        *    .Xauthority stores this as a string when it has a\r
+        *    perfectly good integer format, but there we go.\r
+        * \r
+        *  - str[2] is the authorisation method, encoded as its\r
+        *    canonical string name (i.e. "MIT-MAGIC-COOKIE-1",\r
+        *    "XDM-AUTHORIZATION-1" or something we don't\r
+        *    recognise).\r
+        * \r
+        *  - str[3] is the actual authorisation data, stored in\r
+        *    binary form.\r
+        */\r
+\r
+       if (disp->displaynum < 0 || disp->displaynum != atoi(str[1]))\r
+           continue;                  /* not the one */\r
+\r
+       for (protocol = 1; protocol < lenof(x11_authnames); protocol++)\r
+           if (!strcmp(str[2], x11_authnames[protocol]))\r
+               break;\r
+       if (protocol == lenof(x11_authnames))\r
+           continue;  /* don't recognise this protocol, look for another */\r
+\r
+       switch (family) {\r
+         case 0:   /* IPv4 */\r
+           if (!disp->unixdomain &&\r
+               sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {\r
+               char buf[4];\r
+               sk_addrcopy(disp->addr, buf);\r
+               if (len[0] == 4 && !memcmp(str[0], buf, 4)) {\r
+                   match = TRUE;\r
+                   /* If this is a "localhost" entry, note it down\r
+                    * but carry on looking for a Unix-domain entry. */\r
+                   ideal_match = !localhost;\r
+               }\r
+           }\r
+           break;\r
+         case 6:   /* IPv6 */\r
+           if (!disp->unixdomain &&\r
+               sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {\r
+               char buf[16];\r
+               sk_addrcopy(disp->addr, buf);\r
+               if (len[0] == 16 && !memcmp(str[0], buf, 16)) {\r
+                   match = TRUE;\r
+                   ideal_match = !localhost;\r
+               }\r
+           }\r
+           break;\r
+         case 256: /* Unix-domain / localhost */\r
+           if ((disp->unixdomain || localhost)\r
+               && ourhostname && !strcmp(ourhostname, str[0]))\r
+               /* A matching Unix-domain socket is always the best\r
+                * match. */\r
+               match = ideal_match = TRUE;\r
+           break;\r
+       }\r
+\r
+       if (match) {\r
+           /* Current best guess -- may be overridden if !ideal_match */\r
+           disp->localauthproto = protocol;\r
+           sfree(disp->localauthdata); /* free previous guess, if any */\r
+           disp->localauthdata = snewn(len[3], unsigned char);\r
+           memcpy(disp->localauthdata, str[3], len[3]);\r
+           disp->localauthdatalen = len[3];\r
+       }\r
+    }\r
+\r
+    done:\r
+    fclose(authfp);\r
+    memset(buf, 0, 65537 * 4);\r
+    sfree(buf);\r
+    sfree(ourhostname);\r
+}\r
+\r
+static void x11_log(Plug p, int type, SockAddr addr, int port,\r
+                   const char *error_msg, int error_code)\r
+{\r
+    /* We have no interface to the logging module here, so we drop these. */\r
+}\r
+\r
+static int x11_closing(Plug plug, const char *error_msg, int error_code,\r
+                      int calling_back)\r
+{\r
+    struct X11Private *pr = (struct X11Private *) plug;\r
+\r
+    /*\r
+     * We have no way to communicate down the forwarded connection,\r
+     * so if an error occurred on the socket, we just ignore it\r
+     * and treat it like a proper close.\r
+     */\r
+    sshfwd_close(pr->c);\r
+    x11_close(pr->s);\r
+    return 1;\r
+}\r
+\r
+static int x11_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    struct X11Private *pr = (struct X11Private *) plug;\r
+\r
+    if (sshfwd_write(pr->c, data, len) > 0) {\r
+       pr->throttled = 1;\r
+       sk_set_frozen(pr->s, 1);\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+static void x11_sent(Plug plug, int bufsize)\r
+{\r
+    struct X11Private *pr = (struct X11Private *) plug;\r
+\r
+    sshfwd_unthrottle(pr->c, bufsize);\r
+}\r
+\r
+/*\r
+ * When setting up X forwarding, we should send the screen number\r
+ * from the specified local display. This function extracts it from\r
+ * the display string.\r
+ */\r
+int x11_get_screen_number(char *display)\r
+{\r
+    int n;\r
+\r
+    n = strcspn(display, ":");\r
+    if (!display[n])\r
+       return 0;\r
+    n = strcspn(display, ".");\r
+    if (!display[n])\r
+       return 0;\r
+    return atoi(display + n + 1);\r
+}\r
+\r
+/*\r
+ * Called to set up the raw connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ * also, fills the SocketsStructure\r
+ */\r
+extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,\r
+                           const char *peeraddr, int peerport,\r
+                           const Config *cfg)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       x11_log,\r
+       x11_closing,\r
+       x11_receive,\r
+       x11_sent,\r
+       NULL\r
+    };\r
+\r
+    const char *err;\r
+    struct X11Private *pr;\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    pr = snew(struct X11Private);\r
+    pr->fn = &fn_table;\r
+    pr->auth_protocol = NULL;\r
+    pr->disp = disp;\r
+    pr->verified = 0;\r
+    pr->data_read = 0;\r
+    pr->throttled = pr->throttle_override = 0;\r
+    pr->c = c;\r
+\r
+    pr->s = *s = new_connection(sk_addr_dup(disp->addr),\r
+                               disp->realhost, disp->port,\r
+                               0, 1, 0, 0, (Plug) pr, cfg);\r
+    if ((err = sk_socket_error(*s)) != NULL) {\r
+       sfree(pr);\r
+       return err;\r
+    }\r
+\r
+    /*\r
+     * See if we can make sense of the peer address we were given.\r
+     */\r
+    {\r
+       int i[4];\r
+       if (peeraddr &&\r
+           4 == sscanf(peeraddr, "%d.%d.%d.%d", i+0, i+1, i+2, i+3)) {\r
+           pr->peer_ip = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3];\r
+           pr->peer_port = peerport;\r
+       } else {\r
+           pr->peer_ip = 0;\r
+           pr->peer_port = -1;\r
+       }\r
+    }\r
+\r
+    sk_set_private_ptr(*s, pr);\r
+    return NULL;\r
+}\r
+\r
+void x11_close(Socket s)\r
+{\r
+    struct X11Private *pr;\r
+    if (!s)\r
+       return;\r
+    pr = (struct X11Private *) sk_get_private_ptr(s);\r
+    if (pr->auth_protocol) {\r
+       sfree(pr->auth_protocol);\r
+       sfree(pr->auth_data);\r
+    }\r
+\r
+    sfree(pr);\r
+\r
+    sk_close(s);\r
+}\r
+\r
+void x11_unthrottle(Socket s)\r
+{\r
+    struct X11Private *pr;\r
+    if (!s)\r
+       return;\r
+    pr = (struct X11Private *) sk_get_private_ptr(s);\r
+\r
+    pr->throttled = 0;\r
+    sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
+}\r
+\r
+void x11_override_throttle(Socket s, int enable)\r
+{\r
+    struct X11Private *pr;\r
+    if (!s)\r
+       return;\r
+    pr = (struct X11Private *) sk_get_private_ptr(s);\r
+\r
+    pr->throttle_override = enable;\r
+    sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
+}\r
+\r
+/*\r
+ * Called to send data down the raw connection.\r
+ */\r
+int x11_send(Socket s, char *data, int len)\r
+{\r
+    struct X11Private *pr;\r
+    if (!s)\r
+       return 0;\r
+    pr = (struct X11Private *) sk_get_private_ptr(s);\r
+\r
+    /*\r
+     * Read the first packet.\r
+     */\r
+    while (len > 0 && pr->data_read < 12)\r
+       pr->firstpkt[pr->data_read++] = (unsigned char) (len--, *data++);\r
+    if (pr->data_read < 12)\r
+       return 0;\r
+\r
+    /*\r
+     * If we have not allocated the auth_protocol and auth_data\r
+     * strings, do so now.\r
+     */\r
+    if (!pr->auth_protocol) {\r
+       pr->auth_plen = GET_16BIT(pr->firstpkt[0], pr->firstpkt + 6);\r
+       pr->auth_dlen = GET_16BIT(pr->firstpkt[0], pr->firstpkt + 8);\r
+       pr->auth_psize = (pr->auth_plen + 3) & ~3;\r
+       pr->auth_dsize = (pr->auth_dlen + 3) & ~3;\r
+       /* Leave room for a terminating zero, to make our lives easier. */\r
+       pr->auth_protocol = snewn(pr->auth_psize + 1, char);\r
+       pr->auth_data = snewn(pr->auth_dsize, unsigned char);\r
+    }\r
+\r
+    /*\r
+     * Read the auth_protocol and auth_data strings.\r
+     */\r
+    while (len > 0 && pr->data_read < 12 + pr->auth_psize)\r
+       pr->auth_protocol[pr->data_read++ - 12] = (len--, *data++);\r
+    while (len > 0 && pr->data_read < 12 + pr->auth_psize + pr->auth_dsize)\r
+       pr->auth_data[pr->data_read++ - 12 -\r
+                     pr->auth_psize] = (unsigned char) (len--, *data++);\r
+    if (pr->data_read < 12 + pr->auth_psize + pr->auth_dsize)\r
+       return 0;\r
+\r
+    /*\r
+     * If we haven't verified the authorisation, do so now.\r
+     */\r
+    if (!pr->verified) {\r
+       char *err;\r
+\r
+       pr->auth_protocol[pr->auth_plen] = '\0';        /* ASCIZ */\r
+       err = x11_verify(pr->peer_ip, pr->peer_port,\r
+                        pr->disp, pr->auth_protocol,\r
+                        pr->auth_data, pr->auth_dlen);\r
+\r
+       /*\r
+        * If authorisation failed, construct and send an error\r
+        * packet, then terminate the connection.\r
+        */\r
+       if (err) {\r
+           char *message;\r
+           int msglen, msgsize;\r
+           unsigned char *reply;\r
+\r
+           message = dupprintf("%s X11 proxy: %s", appname, err);\r
+           msglen = strlen(message);\r
+           reply = snewn(8 + msglen+1 + 4, unsigned char); /* include zero */\r
+           msgsize = (msglen + 3) & ~3;\r
+           reply[0] = 0;              /* failure */\r
+           reply[1] = msglen;         /* length of reason string */\r
+           memcpy(reply + 2, pr->firstpkt + 2, 4);     /* major/minor proto vsn */\r
+           PUT_16BIT(pr->firstpkt[0], reply + 6, msgsize >> 2);/* data len */\r
+           memset(reply + 8, 0, msgsize);\r
+           memcpy(reply + 8, message, msglen);\r
+           sshfwd_write(pr->c, (char *)reply, 8 + msgsize);\r
+           sshfwd_close(pr->c);\r
+           x11_close(s);\r
+           sfree(reply);\r
+           sfree(message);\r
+           return 0;\r
+       }\r
+\r
+       /*\r
+        * Now we know we're going to accept the connection. Strip\r
+        * the fake auth data, and optionally put real auth data in\r
+        * instead.\r
+        */\r
+        {\r
+            char realauthdata[64];\r
+            int realauthlen = 0;\r
+            int authstrlen = strlen(x11_authnames[pr->disp->localauthproto]);\r
+           int buflen = 0;            /* initialise to placate optimiser */\r
+            static const char zeroes[4] = { 0,0,0,0 };\r
+           void *buf;\r
+\r
+            if (pr->disp->localauthproto == X11_MIT) {\r
+                assert(pr->disp->localauthdatalen <= lenof(realauthdata));\r
+                realauthlen = pr->disp->localauthdatalen;\r
+                memcpy(realauthdata, pr->disp->localauthdata, realauthlen);\r
+            } else if (pr->disp->localauthproto == X11_XDM &&\r
+                      pr->disp->localauthdatalen == 16 &&\r
+                      ((buf = sk_getxdmdata(s, &buflen))!=0)) {\r
+               time_t t;\r
+                realauthlen = (buflen+12+7) & ~7;\r
+               assert(realauthlen <= lenof(realauthdata));\r
+               memset(realauthdata, 0, realauthlen);\r
+               memcpy(realauthdata, pr->disp->localauthdata, 8);\r
+               memcpy(realauthdata+8, buf, buflen);\r
+               t = time(NULL);\r
+               PUT_32BIT_MSB_FIRST(realauthdata+8+buflen, t);\r
+               des_encrypt_xdmauth(pr->disp->localauthdata+9,\r
+                                   (unsigned char *)realauthdata,\r
+                                   realauthlen);\r
+               sfree(buf);\r
+           }\r
+            /* implement other auth methods here if required */\r
+\r
+            PUT_16BIT(pr->firstpkt[0], pr->firstpkt + 6, authstrlen);\r
+            PUT_16BIT(pr->firstpkt[0], pr->firstpkt + 8, realauthlen);\r
+        \r
+            sk_write(s, (char *)pr->firstpkt, 12);\r
+\r
+            if (authstrlen) {\r
+                sk_write(s, x11_authnames[pr->disp->localauthproto],\r
+                        authstrlen);\r
+                sk_write(s, zeroes, 3 & (-authstrlen));\r
+            }\r
+            if (realauthlen) {\r
+                sk_write(s, realauthdata, realauthlen);\r
+                sk_write(s, zeroes, 3 & (-realauthlen));\r
+            }\r
+        }\r
+       pr->verified = 1;\r
+    }\r
+\r
+    /*\r
+     * After initialisation, just copy data simply.\r
+     */\r
+\r
+    return sk_write(s, data, len);\r
+}\r
diff --git a/putty/dllmain.c b/putty/dllmain.c
new file mode 100644 (file)
index 0000000..e46a72a
--- /dev/null
@@ -0,0 +1,19 @@
+// dllmain.cpp : DLL \83A\83v\83\8a\83P\81[\83V\83\87\83\93\82Ì\83G\83\93\83g\83\8a \83|\83C\83\93\83g\82ð\92è\8b`\82µ\82Ü\82·\81B\r
+#include "stdafx.h"\r
+\r
+BOOL APIENTRY DllMain( HMODULE hModule,\r
+                       DWORD  ul_reason_for_call,\r
+                       LPVOID lpReserved\r
+                                        )\r
+{\r
+       switch (ul_reason_for_call)\r
+       {\r
+       case DLL_PROCESS_ATTACH:\r
+       case DLL_THREAD_ATTACH:\r
+       case DLL_THREAD_DETACH:\r
+       case DLL_PROCESS_DETACH:\r
+               break;\r
+       }\r
+       return TRUE;\r
+}\r
+\r
diff --git a/putty/stdafx.h b/putty/stdafx.h
new file mode 100644 (file)
index 0000000..d5cdd1f
--- /dev/null
@@ -0,0 +1,16 @@
+// stdafx.h : \95W\8f\80\82Ì\83V\83X\83e\83\80 \83C\83\93\83N\83\8b\81[\83\83t\83@\83C\83\8b\82Ì\83C\83\93\83N\83\8b\81[\83\83t\83@\83C\83\8b\81A\82Ü\82½\82Í\r
+// \8eQ\8fÆ\89ñ\90\94\82ª\91½\82­\81A\82©\82Â\82 \82Ü\82è\95Ï\8dX\82³\82ê\82È\82¢\81A\83v\83\8d\83W\83F\83N\83g\90ê\97p\82Ì\83C\83\93\83N\83\8b\81[\83\83t\83@\83C\83\8b\r
+// \82ð\8bL\8fq\82µ\82Ü\82·\81B\r
+//\r
+\r
+#pragma once\r
+\r
+#include "targetver.h"\r
+\r
+#define WIN32_LEAN_AND_MEAN             // Windows \83w\83b\83_\81[\82©\82ç\8eg\97p\82³\82ê\82Ä\82¢\82È\82¢\95\94\95ª\82ð\8f\9c\8aO\82µ\82Ü\82·\81B\r
+// Windows \83w\83b\83_\81\83t\83@\83C\83\8b:\r
+#include <windows.h>\r
+\r
+\r
+\r
+// TODO: \83v\83\8d\83O\83\89\83\80\82É\95K\97v\82È\92Ç\89Á\83w\83b\83_\81[\82ð\82±\82±\82Å\8eQ\8fÆ\82µ\82Ä\82­\82¾\82³\82¢\81B\r
diff --git a/putty/targetver.h b/putty/targetver.h
new file mode 100644 (file)
index 0000000..2481509
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma once\r
+\r
+// \88È\89º\82Ì\83}\83N\83\8d\82Í\81A\8dÅ\92á\8cÀ\95K\97v\82È\83v\83\89\83b\83g\83t\83H\81[\83\80\82ð\92è\8b`\82µ\82Ü\82·\81B\8dÅ\92á\8cÀ\95K\97v\82È\83v\83\89\83b\83g\83t\83H\81[\83\80\82Æ\82Í\81A\r
+// \83A\83v\83\8a\83P\81[\83V\83\87\83\93\82ð\8eÀ\8ds\82·\82é\82½\82ß\82É\95K\97v\82È\8b@\94\\82ð\94õ\82¦\82½\8dÅ\82à\8cÃ\82¢\83o\81[\83W\83\87\83\93\82Ì Windows \82â Internet Explorer \82È\82Ç\r
+// \82ð\82¢\82¢\82Ü\82·\81B\82±\82ê\82ç\82Ì\83}\83N\83\8d\82Í\81A\8ew\92è\82µ\82½\83o\81[\83W\83\87\83\93\82Æ\81A\82»\82ê\88È\91O\82Ì\83o\81[\83W\83\87\83\93\82Ì\83v\83\89\83b\83g\83t\83H\81[\83\80\8fã\82Å\97\98\97p\82Å\82«\82é\82·\82×\82Ä\82Ì\8b@\94\\82ð\97L\8cø\82É\82·\82é\82±\82Æ\82É\82æ\82Á\82Ä \r
+// \93®\8dì\82µ\82Ü\82·\81B\r
+\r
+// \89º\82Å\8ew\92è\82³\82ê\82½\92è\8b`\82Ì\91O\82É\91Î\8fÛ\83v\83\89\83b\83g\83t\83H\81[\83\80\82ð\8ew\92è\82µ\82È\82¯\82ê\82Î\82È\82ç\82È\82¢\8fê\8d\87\81A\88È\89º\82Ì\92è\8b`\82ð\95Ï\8dX\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+// \88Ù\82È\82é\83v\83\89\83b\83g\83t\83H\81[\83\80\82É\91Î\89\9e\82·\82é\92l\82É\8aÖ\82·\82é\8dÅ\90V\8fî\95ñ\82É\82Â\82¢\82Ä\82Í\81AMSDN \82ð\8eQ\8fÆ\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+#ifndef WINVER                          // \8dÅ\92á\8cÀ\95K\97v\82È\83v\83\89\83b\83g\83t\83H\81[\83\80\82ª Windows Vista \82Å\82 \82é\82±\82Æ\82ð\8ew\92è\82µ\82Ü\82·\81B\r
+#define WINVER 0x0600           // \82±\82ê\82ð Windows \82Ì\91¼\82Ì\83o\81[\83W\83\87\83\93\8cü\82¯\82É\93K\90Ø\82È\92l\82É\95Ï\8dX\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+#endif\r
+\r
+#ifndef _WIN32_WINNT            // \8dÅ\92á\8cÀ\95K\97v\82È\83v\83\89\83b\83g\83t\83H\81[\83\80\82ª Windows Vista \82Å\82 \82é\82±\82Æ\82ð\8ew\92è\82µ\82Ü\82·\81B\r
+#define _WIN32_WINNT 0x0600     // \82±\82ê\82ð Windows \82Ì\91¼\82Ì\83o\81[\83W\83\87\83\93\8cü\82¯\82É\93K\90Ø\82È\92l\82É\95Ï\8dX\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+#endif\r
+\r
+#ifndef _WIN32_WINDOWS          // \8dÅ\92á\8cÀ\95K\97v\82È\83v\83\89\83b\83g\83t\83H\81[\83\80\82ª Windows 98 \82Å\82 \82é\82±\82Æ\82ð\8ew\92è\82µ\82Ü\82·\81B\r
+#define _WIN32_WINDOWS 0x0410 // \82±\82ê\82ð Windows Me \82Ü\82½\82Í\82»\82ê\88È\8d~\82Ì\83o\81[\83W\83\87\83\93\8cü\82¯\82É\93K\90Ø\82È\92l\82É\95Ï\8dX\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+#endif\r
+\r
+#ifndef _WIN32_IE                       // \8dÅ\92á\8cÀ\95K\97v\82È\83v\83\89\83b\83g\83t\83H\81[\83\80\82ª Internet Explorer 7.0 \82Å\82 \82é\82±\82Æ\82ð\8ew\92è\82µ\82Ü\82·\81B\r
+#define _WIN32_IE 0x0700        // \82±\82ê\82ð IE \82Ì\91¼\82Ì\83o\81[\83W\83\87\83\93\8cü\82¯\82É\93K\90Ø\82È\92l\82É\95Ï\8dX\82µ\82Ä\82­\82¾\82³\82¢\81B\r
+#endif\r