Despite Windows being its main area of fame, PuTTY is no longer a Windows-only application suite. It has a working Unix port; a Mac port is in progress; more ports may or may not happen at a later date.
Therefore, embedding Windows-specific code in core modules such as ssh.c
is not acceptable. We went to great lengths to remove all the Windows-specific stuff from our core modules, and to shift it out into Windows-specific modules. Adding large amounts of Windows-specific stuff in parts of the code that should be portable is almost guaranteed to make us reject a contribution.
The PuTTY source base is divided into platform-specific modules and platform-generic modules. The Unix-specific modules are all in the unix
subdirectory; the Windows-specific modules are in the windows
subdirectory.
All the modules in the main source directory and other subdirectories - notably all of the code for the various back ends - are platform-generic. We want to keep them that way.
This also means you should stick to the C semantics guaranteed by the C standard: try not to make assumptions about the precise size of basic types such as int
and long int
; don't use pointer casts to do endianness-dependent operations, and so on.
(Even within a platform front end you should still be careful of some of these portability issues. The Windows front end compiles on both 32- and 64-bit x86 and also Arm.)
Our current choice of C standards version is mostly C99. With a couple of exceptions, you can assume that C99 features are available (in particular <stdint.h>
, <stdbool.h>
and inline
), but you shouldn't use things that are new in C11 (such as <uchar.h>
or _Generic
).
The exceptions to that rule are due to the need for Visual Studio compatibility:
-Wvla
when building with gcc and clang, to make it easier to avoid accidentally breaking that rule.
<inttypes.h>
. So that file is included centrally in defs.h
, and has a set of workaround definitions for the PRIx64
-type macros we use. If you need to use another one of those macros, you need to add a workaround definition in defs.h
, and don't casually re-include <inttypes.h>
anywhere else in the source file.
Here are a few portability assumptions that we do currently allow (because we'd already have to thoroughly vet the existing code if they ever needed to change, and it doesn't seem worth doing that unless we really have to):
int
is at least 32 bits wide. (We've never tried to port PuTTY to a platform with 16-bit int
, and it doesn't look likely to be necessary in future.)
char
is exactly 8 bits. (Exceptions to that are even less likely to be relevant to us than short int
.)
memset
to write zero bytes over a whole structure will have the effect of setting all its pointer fields to NULL
. (The standard itself guarantees this for integer fields, but not for pointers.)
time_t
has POSIX semantics, i.e. that it represents an integer number of non-leap seconds since 1970-01-01 00:00:00 UTC. (Times in this format are used in X authorisation, but we could work around that by carefully distinguishing local time_t
from time values used in the wire protocol; but these semantics of time_t
are also baked into the shared library API used by the GSSAPI authentication code, which would be much harder to change.)
char
value representing a Latin alphabetic character, without bothering to allow for EBCDIC or other non-consecutive encodings of the alphabet.)
On the other hand, here are some particular things not to assume:
char
. In particular, you must cast char
values to unsigned char
before passing them to any <ctype.h>
function (because those expect a non-negative character value, or EOF
). If you need a particular signedness, explicitly specify signed char
or unsigned char
, or use C99 int8_t
or uint8_t
.
'\n'
and '\r'
potentially having unusual meanings on a given platform. So it's fine to say \n
in a string you're passing to printf
, but in any context where those characters appear in a standardised wire protocol or a binary file format, they should be spelled '\012'
and '\015'
respectively.