📄 udp.but
字号:
Depending on \e{external} DLLs is something we'd like to avoid if at
all possible (though for some purposes, such as complex SSH
authentication mechanisms, it may be unavoidable). If it can't be
avoided, the important thing is to follow the same principle of
graceful degradation: if a DLL can't be found, then PuTTY should run
happily and just not supply the feature that depended on it.
\H{udp-single-threaded} Single-threaded code
PuTTY and its supporting tools, or at least the vast majority of
them, run in only one OS thread.
This means that if you're devising some piece of internal mechanism,
there's no need to use locks to make sure it doesn't get called by
two threads at once. The only way code can be called re-entrantly is
by recursion.
That said, most of Windows PuTTY's network handling is triggered off
Windows messages requested by \cw{WSAAsyncSelect()}, so if you call
\cw{MessageBox()} deep within some network event handling code you
should be aware that you might be re-entered if a network event
comes in and is passed on to our window procedure by the
\cw{MessageBox()} message loop.
Also, the front ends (in particular Windows Plink) can use multiple
threads if they like. However, Windows Plink keeps \e{very} tight
control of its auxiliary threads, and uses them pretty much
exclusively as a form of \cw{select()}. Pretty much all the code
outside \cw{windows/winplink.c} is \e{only} ever called from the one
primary thread; the others just loop round blocking on file handles
and send messages to the main thread when some real work needs
doing. This is not considered a portability hazard because that bit
of \cw{windows/winplink.c} will need rewriting on other platforms in
any case.
One important consequence of this: PuTTY has only one thread in
which to do everything. That \q{everything} may include managing
more than one login session (\k{udp-globals}), managing multiple
data channels within an SSH session, responding to GUI events even
when nothing is happening on the network, and responding to network
requests from the server (such as repeat key exchange) even when the
program is dealing with complex user interaction such as the
re-configuration dialog box. This means that \e{almost none} of the
PuTTY code can safely block.
\H{udp-keystrokes} Keystrokes sent to the server wherever possible
In almost all cases, PuTTY sends keystrokes to the server. Even
weird keystrokes that you think should be hot keys controlling
PuTTY. Even Alt-F4 or Alt-Space, for example. If a keystroke has a
well-defined escape sequence that it could usefully be sending to
the server, then it should do so, or at the very least it should be
configurably able to do so.
To unconditionally turn a key combination into a hot key to control
PuTTY is almost always a design error. If a hot key is really truly
required, then try to find a key combination for it which \e{isn't}
already used in existing PuTTYs (either it sends nothing to the
server, or it sends the same thing as some other combination). Even
then, be prepared for the possibility that one day that key
combination might end up being needed to send something to the
server - so make sure that there's an alternative way to invoke
whatever PuTTY feature it controls.
\H{udp-640x480} 640\u00D7{x}480 friendliness in configuration panels
There's a reason we have lots of tiny configuration panels instead
of a few huge ones, and that reason is that not everyone has a
1600\u00D7{x}1200 desktop. 640\u00D7{x}480 is still a viable
resolution for running Windows (and indeed it's still the default if
you start up in safe mode), so it's still a resolution we care
about.
Accordingly, the PuTTY configuration box, and the PuTTYgen control
window, are deliberately kept just small enough to fit comfortably
on a 640\u00D7{x}480 display. If you're adding controls to either of
these boxes and you find yourself wanting to increase the size of
the whole box, \e{don't}. Split it into more panels instead.
\H{udp-makefiles-auto} Automatically generated \cw{Makefile}s
PuTTY is intended to compile on multiple platforms, and with
multiple compilers. It would be horrifying to try to maintain a
single \cw{Makefile} which handled all possible situations, and just
as painful to try to directly maintain a set of matching
\cw{Makefile}s for each different compilation environment.
Therefore, we have moved the problem up by one level. In the PuTTY
source archive is a file called \c{Recipe}, which lists which source
files combine to produce which binaries; and there is also a script
called \cw{mkfiles.pl}, which reads \c{Recipe} and writes out the
real \cw{Makefile}s. (The script also reads all the source files and
analyses their dependencies on header files, so we get an extra
benefit from doing it this way, which is that we can supply correct
dependency information even in environments where it's difficult to
set up an automated \c{make depend} phase.)
You should \e{never} edit any of the PuTTY \cw{Makefile}s directly.
They are not stored in our source repository at all. They are
automatically generated by \cw{mkfiles.pl} from the file \c{Recipe}.
If you need to add a new object file to a particular binary, the
right thing to do is to edit \c{Recipe} and re-run \cw{mkfiles.pl}.
This will cause the new object file to be added in every tool that
requires it, on every platform where it matters, in every
\cw{Makefile} to which it is relevant, \e{and} to get all the
dependency data right.
If you send us a patch that modifies one of the \cw{Makefile}s, you
just waste our time, because we will have to convert it into a
change to \c{Recipe}. If you send us a patch that modifies \e{all}
of the \cw{Makefile}s, you will have wasted a lot of \e{your} time
as well!
(There is a comment at the top of every \cw{Makefile} in the PuTTY
source archive saying this, but many people don't seem to read it,
so it's worth repeating here.)
\H{udp-ssh-coroutines} Coroutines in \cw{ssh.c}
Large parts of the code in \cw{ssh.c} are structured using a set of
macros that implement (something close to) Donald Knuth's
\q{coroutines} concept in C.
Essentially, the purpose of these macros are to arrange that a
function can call \cw{crReturn()} to return to its caller, and the
next time it is called control will resume from just after that
\cw{crReturn} statement.
This means that any local (automatic) variables declared in such a
function will be corrupted every time you call \cw{crReturn}. If you
need a variable to persist for longer than that, you \e{must} make
it a field in one of the persistent state structures: either the
local state structures \c{s} or \c{st} in each function, or the
backend-wide structure \c{ssh}.
See
\W{http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}\c{http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}
for a more in-depth discussion of what these macros are for and how
they work.
\H{udp-compile-once} Single compilation of each source file
The PuTTY build system for any given platform works on the following
very simple model:
\b Each source file is compiled precisely once, to produce a single
object file.
\b Each binary is created by linking together some combination of
those object files.
Therefore, if you need to introduce functionality to a particular
module which is only available in some of the tool binaries (for
example, a cryptographic proxy authentication mechanism which needs
to be left out of PuTTYtel to maintain its usability in
crypto-hostile jurisdictions), the \e{wrong} way to do it is by
adding \cw{#ifdef}s in (say) \cw{proxy.c}. This would require
separate compilation of \cw{proxy.c} for PuTTY and PuTTYtel, which
means that the entire \cw{Makefile}-generation architecture (see
\k{udp-makefiles-auto}) would have to be significantly redesigned.
Unless you are prepared to do that redesign yourself, \e{and}
guarantee that it will still port to any future platforms we might
decide to run on, you should not attempt this!
The \e{right} way to introduce a feature like this is to put the new
code in a separate source file, and (if necessary) introduce a
second new source file defining the same set of functions, but
defining them as stubs which don't provide the feature. Then the
module whose behaviour needs to vary (\cw{proxy.c} in this example)
can call the functions defined in these two modules, and it will
either provide the new feature or not provide it according to which
of your new modules it is linked with.
Of course, object files are never shared \e{between} platforms; so
it is allowable to use \cw{#ifdef} to select between platforms. This
happens in \cw{puttyps.h} (choosing which of the platform-specific
include files to use), and also in \cw{misc.c} (the Windows-specific
\q{Minefield} memory diagnostic system). It should be used
sparingly, though, if at all.
\H{udp-perfection} Do as we say, not as we do
The current PuTTY code probably does not conform strictly to \e{all}
of the principles listed above. There may be the occasional
SSH-specific piece of code in what should be a backend-independent
module, or the occasional dependence on a non-standard X library
function under Unix.
This should not be taken as a licence to go ahead and violate the
rules. Where we violate them ourselves, we're not happy about it,
and we would welcome patches that fix any existing problems. Please
try to help us make our code better, not worse!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -