mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-01 03:22:48 -05:00
New library-style 'utils' subdirectories.
Now that the new CMake build system is encouraging us to lay out the code like a set of libraries, it seems like a good idea to make them look more _like_ libraries, by putting things into separate modules as far as possible. This fixes several previous annoyances in which you had to link against some object in order to get a function you needed, but that object also contained other functions you didn't need which included link-time symbol references you didn't want to have to deal with. The usual offender was subsidiary supporting programs including misc.c for some innocuous function and then finding they had to deal with the requirements of buildinfo(). This big reorganisation introduces three new subdirectories called 'utils', one at the top level and one in each platform subdir. In each case, the directory contains basically the same files that were previously placed in the 'utils' build-time library, except that the ones that were extremely miscellaneous (misc.c, utils.c, uxmisc.c, winmisc.c, winmiscs.c, winutils.c) have been split up into much smaller pieces.
This commit is contained in:
54
utils/base64_decode_atom.c
Normal file
54
utils/base64_decode_atom.c
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Core routine to decode a single atomic base64 chunk.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
int base64_decode_atom(const char *atom, unsigned char *out)
|
||||
{
|
||||
int vals[4];
|
||||
int i, v, len;
|
||||
unsigned word;
|
||||
char c;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
c = atom[i];
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
v = c - 'A';
|
||||
else if (c >= 'a' && c <= 'z')
|
||||
v = c - 'a' + 26;
|
||||
else if (c >= '0' && c <= '9')
|
||||
v = c - '0' + 52;
|
||||
else if (c == '+')
|
||||
v = 62;
|
||||
else if (c == '/')
|
||||
v = 63;
|
||||
else if (c == '=')
|
||||
v = -1;
|
||||
else
|
||||
return 0; /* invalid atom */
|
||||
vals[i] = v;
|
||||
}
|
||||
|
||||
if (vals[0] == -1 || vals[1] == -1)
|
||||
return 0;
|
||||
if (vals[2] == -1 && vals[3] != -1)
|
||||
return 0;
|
||||
|
||||
if (vals[3] != -1)
|
||||
len = 3;
|
||||
else if (vals[2] != -1)
|
||||
len = 2;
|
||||
else
|
||||
len = 1;
|
||||
|
||||
word = ((vals[0] << 18) |
|
||||
(vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));
|
||||
out[0] = (word >> 16) & 0xFF;
|
||||
if (len > 1)
|
||||
out[1] = (word >> 8) & 0xFF;
|
||||
if (len > 2)
|
||||
out[2] = word & 0xFF;
|
||||
return len;
|
||||
}
|
30
utils/base64_encode_atom.c
Normal file
30
utils/base64_encode_atom.c
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Core routine to encode a single atomic base64 chunk.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
void base64_encode_atom(const unsigned char *data, int n, char *out)
|
||||
{
|
||||
static const char base64_chars[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
unsigned word;
|
||||
|
||||
word = data[0] << 16;
|
||||
if (n > 1)
|
||||
word |= data[1] << 8;
|
||||
if (n > 2)
|
||||
word |= data[2];
|
||||
out[0] = base64_chars[(word >> 18) & 0x3F];
|
||||
out[1] = base64_chars[(word >> 12) & 0x3F];
|
||||
if (n > 1)
|
||||
out[2] = base64_chars[(word >> 6) & 0x3F];
|
||||
else
|
||||
out[2] = '=';
|
||||
if (n > 2)
|
||||
out[3] = base64_chars[word & 0x3F];
|
||||
else
|
||||
out[3] = '=';
|
||||
}
|
173
utils/bufchain.c
Normal file
173
utils/bufchain.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Generic routines to deal with send buffers: a linked list of
|
||||
* smallish blocks, with the operations
|
||||
*
|
||||
* - add an arbitrary amount of data to the end of the list
|
||||
* - remove the first N bytes from the list
|
||||
* - return a (pointer,length) pair giving some initial data in
|
||||
* the list, suitable for passing to a send or write system
|
||||
* call
|
||||
* - retrieve a larger amount of initial data from the list
|
||||
* - return the current size of the buffer chain in bytes
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define BUFFER_MIN_GRANULE 512
|
||||
|
||||
struct bufchain_granule {
|
||||
struct bufchain_granule *next;
|
||||
char *bufpos, *bufend, *bufmax;
|
||||
};
|
||||
|
||||
static void uninitialised_queue_idempotent_callback(IdempotentCallback *ic)
|
||||
{
|
||||
unreachable("bufchain callback used while uninitialised");
|
||||
}
|
||||
|
||||
void bufchain_init(bufchain *ch)
|
||||
{
|
||||
ch->head = ch->tail = NULL;
|
||||
ch->buffersize = 0;
|
||||
ch->ic = NULL;
|
||||
ch->queue_idempotent_callback = uninitialised_queue_idempotent_callback;
|
||||
}
|
||||
|
||||
void bufchain_clear(bufchain *ch)
|
||||
{
|
||||
struct bufchain_granule *b;
|
||||
while (ch->head) {
|
||||
b = ch->head;
|
||||
ch->head = ch->head->next;
|
||||
smemclr(b, sizeof(*b));
|
||||
sfree(b);
|
||||
}
|
||||
ch->tail = NULL;
|
||||
ch->buffersize = 0;
|
||||
}
|
||||
|
||||
size_t bufchain_size(bufchain *ch)
|
||||
{
|
||||
return ch->buffersize;
|
||||
}
|
||||
|
||||
void bufchain_set_callback_inner(
|
||||
bufchain *ch, IdempotentCallback *ic,
|
||||
void (*queue_idempotent_callback)(IdempotentCallback *ic))
|
||||
{
|
||||
ch->queue_idempotent_callback = queue_idempotent_callback;
|
||||
ch->ic = ic;
|
||||
}
|
||||
|
||||
void bufchain_add(bufchain *ch, const void *data, size_t len)
|
||||
{
|
||||
const char *buf = (const char *)data;
|
||||
|
||||
if (len == 0) return;
|
||||
|
||||
ch->buffersize += len;
|
||||
|
||||
while (len > 0) {
|
||||
if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {
|
||||
size_t copylen = min(len, ch->tail->bufmax - ch->tail->bufend);
|
||||
memcpy(ch->tail->bufend, buf, copylen);
|
||||
buf += copylen;
|
||||
len -= copylen;
|
||||
ch->tail->bufend += copylen;
|
||||
}
|
||||
if (len > 0) {
|
||||
size_t grainlen =
|
||||
max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);
|
||||
struct bufchain_granule *newbuf;
|
||||
newbuf = smalloc(grainlen);
|
||||
newbuf->bufpos = newbuf->bufend =
|
||||
(char *)newbuf + sizeof(struct bufchain_granule);
|
||||
newbuf->bufmax = (char *)newbuf + grainlen;
|
||||
newbuf->next = NULL;
|
||||
if (ch->tail)
|
||||
ch->tail->next = newbuf;
|
||||
else
|
||||
ch->head = newbuf;
|
||||
ch->tail = newbuf;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch->ic)
|
||||
ch->queue_idempotent_callback(ch->ic);
|
||||
}
|
||||
|
||||
void bufchain_consume(bufchain *ch, size_t len)
|
||||
{
|
||||
struct bufchain_granule *tmp;
|
||||
|
||||
assert(ch->buffersize >= len);
|
||||
while (len > 0) {
|
||||
int remlen = len;
|
||||
assert(ch->head != NULL);
|
||||
if (remlen >= ch->head->bufend - ch->head->bufpos) {
|
||||
remlen = ch->head->bufend - ch->head->bufpos;
|
||||
tmp = ch->head;
|
||||
ch->head = tmp->next;
|
||||
if (!ch->head)
|
||||
ch->tail = NULL;
|
||||
smemclr(tmp, sizeof(*tmp));
|
||||
sfree(tmp);
|
||||
} else
|
||||
ch->head->bufpos += remlen;
|
||||
ch->buffersize -= remlen;
|
||||
len -= remlen;
|
||||
}
|
||||
}
|
||||
|
||||
ptrlen bufchain_prefix(bufchain *ch)
|
||||
{
|
||||
return make_ptrlen(ch->head->bufpos, ch->head->bufend - ch->head->bufpos);
|
||||
}
|
||||
|
||||
void bufchain_fetch(bufchain *ch, void *data, size_t len)
|
||||
{
|
||||
struct bufchain_granule *tmp;
|
||||
char *data_c = (char *)data;
|
||||
|
||||
tmp = ch->head;
|
||||
|
||||
assert(ch->buffersize >= len);
|
||||
while (len > 0) {
|
||||
int remlen = len;
|
||||
|
||||
assert(tmp != NULL);
|
||||
if (remlen >= tmp->bufend - tmp->bufpos)
|
||||
remlen = tmp->bufend - tmp->bufpos;
|
||||
memcpy(data_c, tmp->bufpos, remlen);
|
||||
|
||||
tmp = tmp->next;
|
||||
len -= remlen;
|
||||
data_c += remlen;
|
||||
}
|
||||
}
|
||||
|
||||
void bufchain_fetch_consume(bufchain *ch, void *data, size_t len)
|
||||
{
|
||||
bufchain_fetch(ch, data, len);
|
||||
bufchain_consume(ch, len);
|
||||
}
|
||||
|
||||
bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len)
|
||||
{
|
||||
if (ch->buffersize >= len) {
|
||||
bufchain_fetch_consume(ch, data, len);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len)
|
||||
{
|
||||
if (len > ch->buffersize)
|
||||
len = ch->buffersize;
|
||||
if (len)
|
||||
bufchain_fetch_consume(ch, data, len);
|
||||
return len;
|
||||
}
|
158
utils/buildinfo.c
Normal file
158
utils/buildinfo.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Return a string describing everything we know about how this
|
||||
* particular binary was built: from what source, for what target
|
||||
* platform, using what tools, with what settings, etc.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
char *buildinfo(const char *newline)
|
||||
{
|
||||
strbuf *buf = strbuf_new();
|
||||
|
||||
strbuf_catf(buf, "Build platform: %d-bit %s",
|
||||
(int)(CHAR_BIT * sizeof(void *)),
|
||||
BUILDINFO_PLATFORM);
|
||||
|
||||
#ifdef __clang_version__
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
|
||||
#elif defined __GNUC__ && defined __VERSION__
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
|
||||
#endif
|
||||
|
||||
#if defined _MSC_VER
|
||||
#ifndef FOUND_COMPILER
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: ", newline);
|
||||
#else
|
||||
strbuf_catf(buf, ", emulating ");
|
||||
#endif
|
||||
strbuf_catf(buf, "Visual Studio");
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* List of _MSC_VER values and their translations taken from
|
||||
* https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||
*
|
||||
* The pointless #if 0 branch containing this comment is there so
|
||||
* that every real clause can start with #elif and there's no
|
||||
* anomalous first clause. That way the patch looks nicer when you
|
||||
* add extra ones.
|
||||
*/
|
||||
#elif _MSC_VER == 1928 && _MSC_FULL_VER >= 192829500
|
||||
/*
|
||||
* 16.9 and 16.8 have the same _MSC_VER value, and have to be
|
||||
* distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not
|
||||
* mentioned on the above page, but see e.g.
|
||||
* https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120
|
||||
* which says that 16.9 builds will have versions starting at
|
||||
* 19.28.29500.* and going up. Hence, 19 28 29500 is what we
|
||||
* compare _MSC_FULL_VER against above.
|
||||
*/
|
||||
strbuf_catf(buf, " 2019 (16.9)");
|
||||
#elif _MSC_VER == 1928
|
||||
strbuf_catf(buf, " 2019 (16.8)");
|
||||
#elif _MSC_VER == 1927
|
||||
strbuf_catf(buf, " 2019 (16.7)");
|
||||
#elif _MSC_VER == 1926
|
||||
strbuf_catf(buf, " 2019 (16.6)");
|
||||
#elif _MSC_VER == 1925
|
||||
strbuf_catf(buf, " 2019 (16.5)");
|
||||
#elif _MSC_VER == 1924
|
||||
strbuf_catf(buf, " 2019 (16.4)");
|
||||
#elif _MSC_VER == 1923
|
||||
strbuf_catf(buf, " 2019 (16.3)");
|
||||
#elif _MSC_VER == 1922
|
||||
strbuf_catf(buf, " 2019 (16.2)");
|
||||
#elif _MSC_VER == 1921
|
||||
strbuf_catf(buf, " 2019 (16.1)");
|
||||
#elif _MSC_VER == 1920
|
||||
strbuf_catf(buf, " 2019 (16.0)");
|
||||
#elif _MSC_VER == 1916
|
||||
strbuf_catf(buf, " 2017 version 15.9");
|
||||
#elif _MSC_VER == 1915
|
||||
strbuf_catf(buf, " 2017 version 15.8");
|
||||
#elif _MSC_VER == 1914
|
||||
strbuf_catf(buf, " 2017 version 15.7");
|
||||
#elif _MSC_VER == 1913
|
||||
strbuf_catf(buf, " 2017 version 15.6");
|
||||
#elif _MSC_VER == 1912
|
||||
strbuf_catf(buf, " 2017 version 15.5");
|
||||
#elif _MSC_VER == 1911
|
||||
strbuf_catf(buf, " 2017 version 15.3");
|
||||
#elif _MSC_VER == 1910
|
||||
strbuf_catf(buf, " 2017 RTW (15.0)");
|
||||
#elif _MSC_VER == 1900
|
||||
strbuf_catf(buf, " 2015 (14.0)");
|
||||
#elif _MSC_VER == 1800
|
||||
strbuf_catf(buf, " 2013 (12.0)");
|
||||
#elif _MSC_VER == 1700
|
||||
strbuf_catf(buf, " 2012 (11.0)");
|
||||
#elif _MSC_VER == 1600
|
||||
strbuf_catf(buf, " 2010 (10.0)");
|
||||
#elif _MSC_VER == 1500
|
||||
strbuf_catf(buf, " 2008 (9.0)");
|
||||
#elif _MSC_VER == 1400
|
||||
strbuf_catf(buf, " 2005 (8.0)");
|
||||
#elif _MSC_VER == 1310
|
||||
strbuf_catf(buf, " .NET 2003 (7.1)");
|
||||
#elif _MSC_VER == 1300
|
||||
strbuf_catf(buf, " .NET 2002 (7.0)");
|
||||
#elif _MSC_VER == 1200
|
||||
strbuf_catf(buf, " 6.0");
|
||||
#else
|
||||
strbuf_catf(buf, ", unrecognised version");
|
||||
#endif
|
||||
strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
|
||||
#endif
|
||||
|
||||
#ifdef BUILDINFO_GTK
|
||||
{
|
||||
char *gtk_buildinfo = buildinfo_gtk_version();
|
||||
if (gtk_buildinfo) {
|
||||
strbuf_catf(buf, "%sCompiled against GTK version %s",
|
||||
newline, gtk_buildinfo);
|
||||
sfree(gtk_buildinfo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined _WINDOWS
|
||||
{
|
||||
int echm = has_embedded_chm();
|
||||
if (echm >= 0)
|
||||
strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline,
|
||||
echm ? "yes" : "no");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined _WINDOWS && defined MINEFIELD
|
||||
strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
|
||||
#endif
|
||||
#ifdef NO_SECUREZEROMEMORY
|
||||
strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
|
||||
#endif
|
||||
#ifdef NO_IPV6
|
||||
strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
|
||||
#endif
|
||||
#ifdef NO_GSSAPI
|
||||
strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
|
||||
#endif
|
||||
#ifdef STATIC_GSSAPI
|
||||
strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
|
||||
#endif
|
||||
#ifdef UNPROTECT
|
||||
strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
|
||||
#endif
|
||||
#ifdef FUZZING
|
||||
strbuf_catf(buf, "%sBuild option: FUZZING", newline);
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
strbuf_catf(buf, "%sBuild option: DEBUG", newline);
|
||||
#endif
|
||||
|
||||
strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
|
||||
|
||||
return strbuf_to_str(buf);
|
||||
}
|
15
utils/burnstr.c
Normal file
15
utils/burnstr.c
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 'Burn' a dynamically allocated string, in the sense of destroying
|
||||
* it beyond recovery: overwrite it with zeroes and then free it.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
void burnstr(char *string)
|
||||
{
|
||||
if (string) {
|
||||
smemclr(string, strlen(string));
|
||||
sfree(string);
|
||||
}
|
||||
}
|
26
utils/chomp.c
Normal file
26
utils/chomp.c
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Perl-style 'chomp', for a line we just read with fgetline.
|
||||
*
|
||||
* Unlike Perl chomp, however, we're deliberately forgiving of strange
|
||||
* line-ending conventions.
|
||||
*
|
||||
* Also we forgive NULL on input, so you can just write 'line =
|
||||
* chomp(fgetline(fp));' and not bother checking for NULL until
|
||||
* afterwards.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
char *chomp(char *str)
|
||||
{
|
||||
if (str) {
|
||||
int len = strlen(str);
|
||||
while (len > 0 && (str[len-1] == '\r' || str[len-1] == '\n'))
|
||||
len--;
|
||||
str[len] = '\0';
|
||||
}
|
||||
return str;
|
||||
}
|
593
utils/conf.c
Normal file
593
utils/conf.c
Normal file
@ -0,0 +1,593 @@
|
||||
/*
|
||||
* conf.c: implementation of the internal storage format used for
|
||||
* the configuration of a PuTTY session.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tree234.h"
|
||||
#include "putty.h"
|
||||
|
||||
/*
|
||||
* Enumeration of types used in keys and values.
|
||||
*/
|
||||
typedef enum {
|
||||
TYPE_NONE, TYPE_BOOL, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT
|
||||
} Type;
|
||||
|
||||
/*
|
||||
* Arrays which allow us to look up the subkey and value types for a
|
||||
* given primary key id.
|
||||
*/
|
||||
#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
|
||||
static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
|
||||
#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
|
||||
static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
|
||||
|
||||
/*
|
||||
* Configuration keys are primarily integers (big enum of all the
|
||||
* different configurable options); some keys have string-designated
|
||||
* subkeys, such as the list of environment variables (subkeys
|
||||
* defined by the variable names); some have integer-designated
|
||||
* subkeys (wordness, colours, preference lists).
|
||||
*/
|
||||
struct key {
|
||||
int primary;
|
||||
union {
|
||||
int i;
|
||||
char *s;
|
||||
} secondary;
|
||||
};
|
||||
|
||||
/* Variant form of struct key which doesn't contain dynamic data, used
|
||||
* for lookups. */
|
||||
struct constkey {
|
||||
int primary;
|
||||
union {
|
||||
int i;
|
||||
const char *s;
|
||||
} secondary;
|
||||
};
|
||||
|
||||
struct value {
|
||||
union {
|
||||
bool boolval;
|
||||
int intval;
|
||||
char *stringval;
|
||||
Filename *fileval;
|
||||
FontSpec *fontval;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct conf_entry {
|
||||
struct key key;
|
||||
struct value value;
|
||||
};
|
||||
|
||||
struct conf_tag {
|
||||
tree234 *tree;
|
||||
};
|
||||
|
||||
/*
|
||||
* Because 'struct key' is the first element in 'struct conf_entry',
|
||||
* it's safe (guaranteed by the C standard) to cast arbitrarily back
|
||||
* and forth between the two types. Therefore, we only need one
|
||||
* comparison function, which can double as a main sort function for
|
||||
* the tree (comparing two conf_entry structures with each other)
|
||||
* and a search function (looking up an externally supplied key).
|
||||
*/
|
||||
static int conf_cmp(void *av, void *bv)
|
||||
{
|
||||
struct key *a = (struct key *)av;
|
||||
struct key *b = (struct key *)bv;
|
||||
|
||||
if (a->primary < b->primary)
|
||||
return -1;
|
||||
else if (a->primary > b->primary)
|
||||
return +1;
|
||||
switch (subkeytypes[a->primary]) {
|
||||
case TYPE_INT:
|
||||
if (a->secondary.i < b->secondary.i)
|
||||
return -1;
|
||||
else if (a->secondary.i > b->secondary.i)
|
||||
return +1;
|
||||
return 0;
|
||||
case TYPE_STR:
|
||||
return strcmp(a->secondary.s, b->secondary.s);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int conf_cmp_constkey(void *av, void *bv)
|
||||
{
|
||||
struct key *a = (struct key *)av;
|
||||
struct constkey *b = (struct constkey *)bv;
|
||||
|
||||
if (a->primary < b->primary)
|
||||
return -1;
|
||||
else if (a->primary > b->primary)
|
||||
return +1;
|
||||
switch (subkeytypes[a->primary]) {
|
||||
case TYPE_INT:
|
||||
if (a->secondary.i < b->secondary.i)
|
||||
return -1;
|
||||
else if (a->secondary.i > b->secondary.i)
|
||||
return +1;
|
||||
return 0;
|
||||
case TYPE_STR:
|
||||
return strcmp(a->secondary.s, b->secondary.s);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free any dynamic data items pointed to by a 'struct key'. We
|
||||
* don't free the structure itself, since it's probably part of a
|
||||
* larger allocated block.
|
||||
*/
|
||||
static void free_key(struct key *key)
|
||||
{
|
||||
if (subkeytypes[key->primary] == TYPE_STR)
|
||||
sfree(key->secondary.s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a 'struct key' into another one, copying its dynamic data
|
||||
* if necessary.
|
||||
*/
|
||||
static void copy_key(struct key *to, struct key *from)
|
||||
{
|
||||
to->primary = from->primary;
|
||||
switch (subkeytypes[to->primary]) {
|
||||
case TYPE_INT:
|
||||
to->secondary.i = from->secondary.i;
|
||||
break;
|
||||
case TYPE_STR:
|
||||
to->secondary.s = dupstr(from->secondary.s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free any dynamic data items pointed to by a 'struct value'. We
|
||||
* don't free the value itself, since it's probably part of a larger
|
||||
* allocated block.
|
||||
*/
|
||||
static void free_value(struct value *val, int type)
|
||||
{
|
||||
if (type == TYPE_STR)
|
||||
sfree(val->u.stringval);
|
||||
else if (type == TYPE_FILENAME)
|
||||
filename_free(val->u.fileval);
|
||||
else if (type == TYPE_FONT)
|
||||
fontspec_free(val->u.fontval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a 'struct value' into another one, copying its dynamic data
|
||||
* if necessary.
|
||||
*/
|
||||
static void copy_value(struct value *to, struct value *from, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case TYPE_BOOL:
|
||||
to->u.boolval = from->u.boolval;
|
||||
break;
|
||||
case TYPE_INT:
|
||||
to->u.intval = from->u.intval;
|
||||
break;
|
||||
case TYPE_STR:
|
||||
to->u.stringval = dupstr(from->u.stringval);
|
||||
break;
|
||||
case TYPE_FILENAME:
|
||||
to->u.fileval = filename_copy(from->u.fileval);
|
||||
break;
|
||||
case TYPE_FONT:
|
||||
to->u.fontval = fontspec_copy(from->u.fontval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an entire 'struct conf_entry' and its dynamic data.
|
||||
*/
|
||||
static void free_entry(struct conf_entry *entry)
|
||||
{
|
||||
free_key(&entry->key);
|
||||
free_value(&entry->value, valuetypes[entry->key.primary]);
|
||||
sfree(entry);
|
||||
}
|
||||
|
||||
Conf *conf_new(void)
|
||||
{
|
||||
Conf *conf = snew(struct conf_tag);
|
||||
|
||||
conf->tree = newtree234(conf_cmp);
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void conf_clear(Conf *conf)
|
||||
{
|
||||
struct conf_entry *entry;
|
||||
|
||||
while ((entry = delpos234(conf->tree, 0)) != NULL)
|
||||
free_entry(entry);
|
||||
}
|
||||
|
||||
void conf_free(Conf *conf)
|
||||
{
|
||||
conf_clear(conf);
|
||||
freetree234(conf->tree);
|
||||
sfree(conf);
|
||||
}
|
||||
|
||||
static void conf_insert(Conf *conf, struct conf_entry *entry)
|
||||
{
|
||||
struct conf_entry *oldentry = add234(conf->tree, entry);
|
||||
if (oldentry && oldentry != entry) {
|
||||
del234(conf->tree, oldentry);
|
||||
free_entry(oldentry);
|
||||
oldentry = add234(conf->tree, entry);
|
||||
assert(oldentry == entry);
|
||||
}
|
||||
}
|
||||
|
||||
void conf_copy_into(Conf *newconf, Conf *oldconf)
|
||||
{
|
||||
struct conf_entry *entry, *entry2;
|
||||
int i;
|
||||
|
||||
conf_clear(newconf);
|
||||
|
||||
for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
|
||||
entry2 = snew(struct conf_entry);
|
||||
copy_key(&entry2->key, &entry->key);
|
||||
copy_value(&entry2->value, &entry->value,
|
||||
valuetypes[entry->key.primary]);
|
||||
add234(newconf->tree, entry2);
|
||||
}
|
||||
}
|
||||
|
||||
Conf *conf_copy(Conf *oldconf)
|
||||
{
|
||||
Conf *newconf = conf_new();
|
||||
|
||||
conf_copy_into(newconf, oldconf);
|
||||
|
||||
return newconf;
|
||||
}
|
||||
|
||||
bool conf_get_bool(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_BOOL);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.boolval;
|
||||
}
|
||||
|
||||
int conf_get_int(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.intval;
|
||||
}
|
||||
|
||||
int conf_get_int_int(Conf *conf, int primary, int secondary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_INT);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
key.primary = primary;
|
||||
key.secondary.i = secondary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.intval;
|
||||
}
|
||||
|
||||
char *conf_get_str(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.stringval;
|
||||
}
|
||||
|
||||
char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
key.secondary.s = (char *)secondary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
return entry ? entry->value.u.stringval : NULL;
|
||||
}
|
||||
|
||||
char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
|
||||
{
|
||||
char *ret = conf_get_str_str_opt(conf, primary, secondary);
|
||||
assert(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *conf_get_str_strs(Conf *conf, int primary,
|
||||
char *subkeyin, char **subkeyout)
|
||||
{
|
||||
struct constkey key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
if (subkeyin) {
|
||||
key.secondary.s = subkeyin;
|
||||
entry = findrel234(conf->tree, &key, NULL, REL234_GT);
|
||||
} else {
|
||||
key.secondary.s = "";
|
||||
entry = findrel234(conf->tree, &key, conf_cmp_constkey, REL234_GE);
|
||||
}
|
||||
if (!entry || entry->key.primary != primary)
|
||||
return NULL;
|
||||
*subkeyout = entry->key.secondary.s;
|
||||
return entry->value.u.stringval;
|
||||
}
|
||||
|
||||
char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
|
||||
{
|
||||
struct constkey key;
|
||||
struct conf_entry *entry;
|
||||
int index;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
key.secondary.s = "";
|
||||
entry = findrelpos234(conf->tree, &key, conf_cmp_constkey,
|
||||
REL234_GE, &index);
|
||||
if (!entry || entry->key.primary != primary)
|
||||
return NULL;
|
||||
entry = index234(conf->tree, index + n);
|
||||
if (!entry || entry->key.primary != primary)
|
||||
return NULL;
|
||||
return entry->key.secondary.s;
|
||||
}
|
||||
|
||||
Filename *conf_get_filename(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FILENAME);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.fileval;
|
||||
}
|
||||
|
||||
FontSpec *conf_get_fontspec(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FONT);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.fontval;
|
||||
}
|
||||
|
||||
void conf_set_bool(Conf *conf, int primary, bool value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_BOOL);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.boolval = value;
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_int(Conf *conf, int primary, int value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.intval = value;
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_int_int(Conf *conf, int primary,
|
||||
int secondary, int value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_INT);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
entry->key.primary = primary;
|
||||
entry->key.secondary.i = secondary;
|
||||
entry->value.u.intval = value;
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_str(Conf *conf, int primary, const char *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.stringval = dupstr(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_str_str(Conf *conf, int primary, const char *secondary,
|
||||
const char *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
entry->key.primary = primary;
|
||||
entry->key.secondary.s = dupstr(secondary);
|
||||
entry->value.u.stringval = dupstr(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_del_str_str(Conf *conf, int primary, const char *secondary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
key.secondary.s = (char *)secondary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
if (entry) {
|
||||
del234(conf->tree, entry);
|
||||
free_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void conf_set_filename(Conf *conf, int primary, const Filename *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FILENAME);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.fileval = filename_copy(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FONT);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.fontval = fontspec_copy(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_serialise(BinarySink *bs, Conf *conf)
|
||||
{
|
||||
int i;
|
||||
struct conf_entry *entry;
|
||||
|
||||
for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
|
||||
put_uint32(bs, entry->key.primary);
|
||||
|
||||
switch (subkeytypes[entry->key.primary]) {
|
||||
case TYPE_INT:
|
||||
put_uint32(bs, entry->key.secondary.i);
|
||||
break;
|
||||
case TYPE_STR:
|
||||
put_asciz(bs, entry->key.secondary.s);
|
||||
break;
|
||||
}
|
||||
switch (valuetypes[entry->key.primary]) {
|
||||
case TYPE_BOOL:
|
||||
put_bool(bs, entry->value.u.boolval);
|
||||
break;
|
||||
case TYPE_INT:
|
||||
put_uint32(bs, entry->value.u.intval);
|
||||
break;
|
||||
case TYPE_STR:
|
||||
put_asciz(bs, entry->value.u.stringval);
|
||||
break;
|
||||
case TYPE_FILENAME:
|
||||
filename_serialise(bs, entry->value.u.fileval);
|
||||
break;
|
||||
case TYPE_FONT:
|
||||
fontspec_serialise(bs, entry->value.u.fontval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
put_uint32(bs, 0xFFFFFFFFU);
|
||||
}
|
||||
|
||||
bool conf_deserialise(Conf *conf, BinarySource *src)
|
||||
{
|
||||
struct conf_entry *entry;
|
||||
unsigned primary;
|
||||
|
||||
while (1) {
|
||||
primary = get_uint32(src);
|
||||
|
||||
if (get_err(src))
|
||||
return false;
|
||||
if (primary == 0xFFFFFFFFU)
|
||||
return true;
|
||||
if (primary >= N_CONFIG_OPTIONS)
|
||||
return false;
|
||||
|
||||
entry = snew(struct conf_entry);
|
||||
entry->key.primary = primary;
|
||||
|
||||
switch (subkeytypes[entry->key.primary]) {
|
||||
case TYPE_INT:
|
||||
entry->key.secondary.i = toint(get_uint32(src));
|
||||
break;
|
||||
case TYPE_STR:
|
||||
entry->key.secondary.s = dupstr(get_asciz(src));
|
||||
break;
|
||||
}
|
||||
|
||||
switch (valuetypes[entry->key.primary]) {
|
||||
case TYPE_BOOL:
|
||||
entry->value.u.boolval = get_bool(src);
|
||||
break;
|
||||
case TYPE_INT:
|
||||
entry->value.u.intval = toint(get_uint32(src));
|
||||
break;
|
||||
case TYPE_STR:
|
||||
entry->value.u.stringval = dupstr(get_asciz(src));
|
||||
break;
|
||||
case TYPE_FILENAME:
|
||||
entry->value.u.fileval = filename_deserialise(src);
|
||||
break;
|
||||
case TYPE_FONT:
|
||||
entry->value.u.fontval = fontspec_deserialise(src);
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_err(src)) {
|
||||
free_entry(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
}
|
15
utils/conf_dest.c
Normal file
15
utils/conf_dest.c
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Decide whether the 'host name' or 'serial line' field of a Conf is
|
||||
* important, based on which protocol it has selected.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
char const *conf_dest(Conf *conf)
|
||||
{
|
||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||
return conf_get_str(conf, CONF_serline);
|
||||
else
|
||||
return conf_get_str(conf, CONF_host);
|
||||
}
|
||||
|
14
utils/conf_launchable.c
Normal file
14
utils/conf_launchable.c
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Determine whether or not a Conf represents a session which can
|
||||
* sensibly be launched right now.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
bool conf_launchable(Conf *conf)
|
||||
{
|
||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||
return conf_get_str(conf, CONF_serline)[0] != 0;
|
||||
else
|
||||
return conf_get_str(conf, CONF_host)[0] != 0;
|
||||
}
|
49
utils/ctrlparse.c
Normal file
49
utils/ctrlparse.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Parse a ^C style character specification.
|
||||
* Returns NULL in `next' if we didn't recognise it as a control character,
|
||||
* in which case `c' should be ignored.
|
||||
* The precise current parsing is an oddity inherited from the terminal
|
||||
* answerback-string parsing code. All sequences start with ^; all except
|
||||
* ^<123> are two characters. The ones that are worth keeping are probably:
|
||||
* ^? 127
|
||||
* ^@A-Z[\]^_ 0-31
|
||||
* a-z 1-26
|
||||
* <num> specified by number (decimal, 0octal, 0xHEX)
|
||||
* ~ ^ escape
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
char ctrlparse(char *s, char **next)
|
||||
{
|
||||
char c = 0;
|
||||
if (*s != '^') {
|
||||
*next = NULL;
|
||||
} else {
|
||||
s++;
|
||||
if (*s == '\0') {
|
||||
*next = NULL;
|
||||
} else if (*s == '<') {
|
||||
s++;
|
||||
c = (char)strtol(s, next, 0);
|
||||
if ((*next == s) || (**next != '>')) {
|
||||
c = 0;
|
||||
*next = NULL;
|
||||
} else
|
||||
(*next)++;
|
||||
} else if (*s >= 'a' && *s <= 'z') {
|
||||
c = (*s - ('a' - 1));
|
||||
*next = s+1;
|
||||
} else if ((*s >= '@' && *s <= '_') || *s == '?' || (*s & 0x80)) {
|
||||
c = ('@' ^ *s);
|
||||
*next = s+1;
|
||||
} else if (*s == '~') {
|
||||
c = '^';
|
||||
*next = s+1;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
56
utils/debug.c
Normal file
56
utils/debug.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Debugging routines used by the debug() macros, at least if you
|
||||
* compiled with -DDEBUG (aka the PUTTY_DEBUG cmake option) so that
|
||||
* those macros don't optimise down to nothing.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
void debug_printf(const char *fmt, ...)
|
||||
{
|
||||
char *buf;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
buf = dupvprintf(fmt, ap);
|
||||
dputs(buf);
|
||||
sfree(buf);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void debug_memdump(const void *buf, int len, bool L)
|
||||
{
|
||||
int i;
|
||||
const unsigned char *p = buf;
|
||||
char foo[17];
|
||||
if (L) {
|
||||
int delta;
|
||||
debug_printf("\t%d (0x%x) bytes:\n", len, len);
|
||||
delta = 15 & (uintptr_t)p;
|
||||
p -= delta;
|
||||
len += delta;
|
||||
}
|
||||
for (; 0 < len; p += 16, len -= 16) {
|
||||
dputs(" ");
|
||||
if (L)
|
||||
debug_printf("%p: ", p);
|
||||
strcpy(foo, "................"); /* sixteen dots */
|
||||
for (i = 0; i < 16 && i < len; ++i) {
|
||||
if (&p[i] < (unsigned char *) buf) {
|
||||
dputs(" "); /* 3 spaces */
|
||||
foo[i] = ' ';
|
||||
} else {
|
||||
debug_printf("%c%2.2x",
|
||||
&p[i] != (unsigned char *) buf
|
||||
&& i % 4 ? '.' : ' ', p[i]
|
||||
);
|
||||
if (p[i] >= ' ' && p[i] <= '~')
|
||||
foo[i] = (char) p[i];
|
||||
}
|
||||
}
|
||||
foo[i] = '\0';
|
||||
debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);
|
||||
}
|
||||
}
|
48
utils/dupcat.c
Normal file
48
utils/dupcat.c
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Implementation function behind dupcat() in misc.h.
|
||||
*
|
||||
* This function is called with an arbitrary number of 'const char *'
|
||||
* parameters, of which the last one is a null pointer. The wrapper
|
||||
* macro puts on the null pointer itself, so normally callers don't
|
||||
* have to.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
char *dupcat_fn(const char *s1, ...)
|
||||
{
|
||||
int len;
|
||||
char *p, *q, *sn;
|
||||
va_list ap;
|
||||
|
||||
len = strlen(s1);
|
||||
va_start(ap, s1);
|
||||
while (1) {
|
||||
sn = va_arg(ap, char *);
|
||||
if (!sn)
|
||||
break;
|
||||
len += strlen(sn);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
p = snewn(len + 1, char);
|
||||
strcpy(p, s1);
|
||||
q = p + strlen(p);
|
||||
|
||||
va_start(ap, s1);
|
||||
while (1) {
|
||||
sn = va_arg(ap, char *);
|
||||
if (!sn)
|
||||
break;
|
||||
strcpy(q, sn);
|
||||
q += strlen(q);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
100
utils/dupprintf.c
Normal file
100
utils/dupprintf.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Do an sprintf(), but into a custom-allocated buffer.
|
||||
*
|
||||
* Currently I'm doing this via vsnprintf. This has worked so far,
|
||||
* but it's not good, because vsnprintf is not available on all
|
||||
* platforms. There's an ifdef to use `_vsnprintf', which seems
|
||||
* to be the local name for it on Windows. Other platforms may
|
||||
* lack it completely, in which case it'll be time to rewrite
|
||||
* this function in a totally different way.
|
||||
*
|
||||
* The only `properly' portable solution I can think of is to
|
||||
* implement my own format string scanner, which figures out an
|
||||
* upper bound for the length of each formatting directive,
|
||||
* allocates the buffer as it goes along, and calls sprintf() to
|
||||
* actually process each directive. If I ever need to actually do
|
||||
* this, some caveats:
|
||||
*
|
||||
* - It's very hard to find a reliable upper bound for
|
||||
* floating-point values. %f, in particular, when supplied with
|
||||
* a number near to the upper or lower limit of representable
|
||||
* numbers, could easily take several hundred characters. It's
|
||||
* probably feasible to predict this statically using the
|
||||
* constants in <float.h>, or even to predict it dynamically by
|
||||
* looking at the exponent of the specific float provided, but
|
||||
* it won't be fun.
|
||||
*
|
||||
* - Don't forget to _check_, after calling sprintf, that it's
|
||||
* used at most the amount of space we had available.
|
||||
*
|
||||
* - Fault any formatting directive we don't fully understand. The
|
||||
* aim here is to _guarantee_ that we never overflow the buffer,
|
||||
* because this is a security-critical function. If we see a
|
||||
* directive we don't know about, we should panic and die rather
|
||||
* than run any risk.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
/* Work around lack of va_copy in old MSC */
|
||||
#if defined _MSC_VER && !defined va_copy
|
||||
#define va_copy(a, b) TYPECHECK( \
|
||||
(va_list *)0 == &(a) && (va_list *)0 == &(b), \
|
||||
memcpy(&a, &b, sizeof(va_list)))
|
||||
#endif
|
||||
|
||||
/* Also lack of vsnprintf before VS2015 */
|
||||
#if defined _WINDOWS && \
|
||||
!defined __MINGW32__ && \
|
||||
!defined __WINE__ && \
|
||||
_MSC_VER < 1900
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
|
||||
char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
size_t size = *sizeptr;
|
||||
sgrowarrayn_nm(buf, size, oldlen, 512);
|
||||
|
||||
while (1) {
|
||||
va_list aq;
|
||||
va_copy(aq, ap);
|
||||
int len = vsnprintf(buf + oldlen, size - oldlen, fmt, aq);
|
||||
va_end(aq);
|
||||
|
||||
if (len >= 0 && len < size) {
|
||||
/* This is the C99-specified criterion for snprintf to have
|
||||
* been completely successful. */
|
||||
*sizeptr = size;
|
||||
return buf;
|
||||
} else if (len > 0) {
|
||||
/* This is the C99 error condition: the returned length is
|
||||
* the required buffer size not counting the NUL. */
|
||||
sgrowarrayn_nm(buf, size, oldlen + 1, len);
|
||||
} else {
|
||||
/* This is the pre-C99 glibc error condition: <0 means the
|
||||
* buffer wasn't big enough, so we enlarge it a bit and hope. */
|
||||
sgrowarray_nm(buf, size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *dupvprintf(const char *fmt, va_list ap)
|
||||
{
|
||||
size_t size = 0;
|
||||
return dupvprintf_inner(NULL, 0, &size, fmt, ap);
|
||||
}
|
||||
char *dupprintf(const char *fmt, ...)
|
||||
{
|
||||
char *ret;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
ret = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
19
utils/dupstr.c
Normal file
19
utils/dupstr.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Allocate a duplicate of an ordinary C NUL-terminated string.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
char *dupstr(const char *s)
|
||||
{
|
||||
char *p = NULL;
|
||||
if (s) {
|
||||
int len = strlen(s);
|
||||
p = snewn(len + 1, char);
|
||||
strcpy(p, s);
|
||||
}
|
||||
return p;
|
||||
}
|
29
utils/encode_utf8.c
Normal file
29
utils/encode_utf8.c
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Encode a single code point as UTF-8.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
size_t encode_utf8(void *output, unsigned long ch)
|
||||
{
|
||||
unsigned char *start = (unsigned char *)output, *p = start;
|
||||
|
||||
if (ch < 0x80) {
|
||||
*p++ = ch;
|
||||
} else if (ch < 0x800) {
|
||||
*p++ = 0xC0 | (ch >> 6);
|
||||
*p++ = 0x80 | (ch & 0x3F);
|
||||
} else if (ch < 0x10000) {
|
||||
*p++ = 0xE0 | (ch >> 12);
|
||||
*p++ = 0x80 | ((ch >> 6) & 0x3F);
|
||||
*p++ = 0x80 | (ch & 0x3F);
|
||||
} else {
|
||||
assert(ch <= 0x10FFFF);
|
||||
*p++ = 0xF0 | (ch >> 18);
|
||||
*p++ = 0x80 | ((ch >> 12) & 0x3F);
|
||||
*p++ = 0x80 | ((ch >> 6) & 0x3F);
|
||||
*p++ = 0x80 | (ch & 0x3F);
|
||||
}
|
||||
return p - start;
|
||||
}
|
25
utils/fgetline.c
Normal file
25
utils/fgetline.c
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Read an entire line of text from a file. Return a buffer
|
||||
* malloced to be as big as necessary (caller must free).
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
char *fgetline(FILE *fp)
|
||||
{
|
||||
char *ret = snewn(512, char);
|
||||
size_t size = 512, len = 0;
|
||||
while (fgets(ret + len, size - len, fp)) {
|
||||
len += strlen(ret + len);
|
||||
if (len > 0 && ret[len-1] == '\n')
|
||||
break; /* got a newline, we're done */
|
||||
sgrowarrayn_nm(ret, size, len, 512);
|
||||
}
|
||||
if (len == 0) { /* first fgets returned NULL */
|
||||
sfree(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret[len] = '\0';
|
||||
return ret;
|
||||
}
|
18
utils/host_strchr.c
Normal file
18
utils/host_strchr.c
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* strchr-like wrapper around host_strchr_internal.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
char *host_strchr(const char *s, int c)
|
||||
{
|
||||
char set[2];
|
||||
set[0] = c;
|
||||
set[1] = '\0';
|
||||
return (char *) host_strchr_internal(s, set, true);
|
||||
}
|
80
utils/host_strchr_internal.c
Normal file
80
utils/host_strchr_internal.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Find a character in a string, unless it's a colon contained within
|
||||
* square brackets. Used for untangling strings of the form
|
||||
* 'host:port', where host can be an IPv6 literal.
|
||||
*
|
||||
* This internal function provides an API that's a bit like strchr (in
|
||||
* that it returns a pointer to the character it found, or NULL), and
|
||||
* a bit like strcspn (in that you can give it a set of characters to
|
||||
* look for, not just one). Also it has an option to return the first
|
||||
* or last character it finds. Other functions in the utils directory
|
||||
* provide wrappers on it with APIs more like familiar <string.h>
|
||||
* functions.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
const char *host_strchr_internal(const char *s, const char *set, bool first)
|
||||
{
|
||||
int brackets = 0;
|
||||
const char *ret = NULL;
|
||||
|
||||
while (1) {
|
||||
if (!*s)
|
||||
return ret;
|
||||
|
||||
if (*s == '[')
|
||||
brackets++;
|
||||
else if (*s == ']' && brackets > 0)
|
||||
brackets--;
|
||||
else if (brackets && *s == ':')
|
||||
/* never match */ ;
|
||||
else if (strchr(set, *s)) {
|
||||
ret = s;
|
||||
if (first)
|
||||
return ret;
|
||||
}
|
||||
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int passes = 0, fails = 0;
|
||||
|
||||
#define TEST1(func, string, arg2, suffix, result) do \
|
||||
{ \
|
||||
const char *str = string; \
|
||||
unsigned ret = func(string, arg2) suffix; \
|
||||
if (ret == result) { \
|
||||
passes++; \
|
||||
} else { \
|
||||
printf("fail: %s(%s,%s)%s = %u, expected %u\n", \
|
||||
#func, #string, #arg2, #suffix, ret, \
|
||||
(unsigned)result); \
|
||||
fails++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
TEST1(host_strchr, "[1:2:3]:4:5", ':', -str, 7);
|
||||
TEST1(host_strrchr, "[1:2:3]:4:5", ':', -str, 9);
|
||||
TEST1(host_strcspn, "[1:2:3]:4:5", "/:",, 7);
|
||||
TEST1(host_strchr, "[1:2:3]", ':', == NULL, 1);
|
||||
TEST1(host_strrchr, "[1:2:3]", ':', == NULL, 1);
|
||||
TEST1(host_strcspn, "[1:2:3]", "/:",, 7);
|
||||
TEST1(host_strcspn, "[1:2/3]", "/:",, 4);
|
||||
TEST1(host_strcspn, "[1:2:3]/", "/:",, 7);
|
||||
|
||||
printf("passed %d failed %d total %d\n", passes, fails, passes+fails);
|
||||
return fails != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
#endif /* TEST */
|
19
utils/host_strcspn.c
Normal file
19
utils/host_strcspn.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* strcspn-like wrapper around host_strchr_internal.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
size_t host_strcspn(const char *s, const char *set)
|
||||
{
|
||||
const char *answer = host_strchr_internal(s, set, true);
|
||||
if (answer)
|
||||
return answer - s;
|
||||
else
|
||||
return strlen(s);
|
||||
}
|
51
utils/host_strduptrim.c
Normal file
51
utils/host_strduptrim.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Trim square brackets off the outside of an IPv6 address literal.
|
||||
* Leave all other strings unchanged. Returns a fresh dynamically
|
||||
* allocated string.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
char *host_strduptrim(const char *s)
|
||||
{
|
||||
if (s[0] == '[') {
|
||||
const char *p = s+1;
|
||||
int colons = 0;
|
||||
while (*p && *p != ']') {
|
||||
if (isxdigit((unsigned char)*p))
|
||||
/* OK */;
|
||||
else if (*p == ':')
|
||||
colons++;
|
||||
else
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
if (*p == '%') {
|
||||
/*
|
||||
* This delimiter character introduces an RFC 4007 scope
|
||||
* id suffix (e.g. suffixing the address literal with
|
||||
* %eth1 or %2 or some such). There's no syntax
|
||||
* specification for the scope id, so just accept anything
|
||||
* except the closing ].
|
||||
*/
|
||||
p += strcspn(p, "]");
|
||||
}
|
||||
if (*p == ']' && !p[1] && colons > 1) {
|
||||
/*
|
||||
* This looks like an IPv6 address literal (hex digits and
|
||||
* at least two colons, plus optional scope id, contained
|
||||
* in square brackets). Trim off the brackets.
|
||||
*/
|
||||
return dupprintf("%.*s", (int)(p - (s+1)), s+1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Any other shape of string is simply duplicated.
|
||||
*/
|
||||
return dupstr(s);
|
||||
}
|
18
utils/host_strrchr.c
Normal file
18
utils/host_strrchr.c
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* strchr-like wrapper around host_strchr_internal.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
char *host_strrchr(const char *s, int c)
|
||||
{
|
||||
char set[2];
|
||||
set[0] = c;
|
||||
set[1] = '\0';
|
||||
return (char *) host_strchr_internal(s, set, false);
|
||||
}
|
16
utils/ltime.c
Normal file
16
utils/ltime.c
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Portable implementation of ltime() for any ISO-C platform where
|
||||
* time_t behaves. (In practice, we've found that platforms such as
|
||||
* Windows and Mac have needed their own specialised implementations.)
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct tm ltime(void)
|
||||
{
|
||||
time_t t;
|
||||
time(&t);
|
||||
assert (t != ((time_t)-1));
|
||||
return *localtime(&t);
|
||||
}
|
318
utils/marshal.c
Normal file
318
utils/marshal.c
Normal file
@ -0,0 +1,318 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "marshal.h"
|
||||
#include "misc.h"
|
||||
|
||||
void BinarySink_put_data(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
bs->write(bs, data, len);
|
||||
}
|
||||
|
||||
void BinarySink_put_datapl(BinarySink *bs, ptrlen pl)
|
||||
{
|
||||
BinarySink_put_data(bs, pl.ptr, pl.len);
|
||||
}
|
||||
|
||||
void BinarySink_put_padding(BinarySink *bs, size_t len, unsigned char padbyte)
|
||||
{
|
||||
char buf[16];
|
||||
memset(buf, padbyte, sizeof(buf));
|
||||
while (len > 0) {
|
||||
size_t thislen = len < sizeof(buf) ? len : sizeof(buf);
|
||||
bs->write(bs, buf, thislen);
|
||||
len -= thislen;
|
||||
}
|
||||
}
|
||||
|
||||
void BinarySink_put_byte(BinarySink *bs, unsigned char val)
|
||||
{
|
||||
bs->write(bs, &val, 1);
|
||||
}
|
||||
|
||||
void BinarySink_put_bool(BinarySink *bs, bool val)
|
||||
{
|
||||
unsigned char cval = val ? 1 : 0;
|
||||
bs->write(bs, &cval, 1);
|
||||
}
|
||||
|
||||
void BinarySink_put_uint16(BinarySink *bs, unsigned long val)
|
||||
{
|
||||
unsigned char data[2];
|
||||
PUT_16BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_uint32(BinarySink *bs, unsigned long val)
|
||||
{
|
||||
unsigned char data[4];
|
||||
PUT_32BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_uint64(BinarySink *bs, uint64_t val)
|
||||
{
|
||||
unsigned char data[8];
|
||||
PUT_64BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_string(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
/* Check that the string length fits in a uint32, without doing a
|
||||
* potentially implementation-defined shift of more than 31 bits */
|
||||
assert((len >> 31) < 2);
|
||||
|
||||
BinarySink_put_uint32(bs, len);
|
||||
bs->write(bs, data, len);
|
||||
}
|
||||
|
||||
void BinarySink_put_stringpl(BinarySink *bs, ptrlen pl)
|
||||
{
|
||||
BinarySink_put_string(bs, pl.ptr, pl.len);
|
||||
}
|
||||
|
||||
void BinarySink_put_stringz(BinarySink *bs, const char *str)
|
||||
{
|
||||
BinarySink_put_string(bs, str, strlen(str));
|
||||
}
|
||||
|
||||
void BinarySink_put_stringsb(BinarySink *bs, struct strbuf *buf)
|
||||
{
|
||||
BinarySink_put_string(bs, buf->s, buf->len);
|
||||
strbuf_free(buf);
|
||||
}
|
||||
|
||||
void BinarySink_put_asciz(BinarySink *bs, const char *str)
|
||||
{
|
||||
bs->write(bs, str, strlen(str) + 1);
|
||||
}
|
||||
|
||||
bool BinarySink_put_pstring(BinarySink *bs, const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
if (len > 255)
|
||||
return false; /* can't write a Pascal-style string this long */
|
||||
BinarySink_put_byte(bs, len);
|
||||
bs->write(bs, str, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static bool BinarySource_data_avail(BinarySource *src, size_t wanted)
|
||||
{
|
||||
if (src->err)
|
||||
return false;
|
||||
|
||||
if (wanted <= src->len - src->pos)
|
||||
return true;
|
||||
|
||||
src->err = BSE_OUT_OF_DATA;
|
||||
return false;
|
||||
}
|
||||
|
||||
#define avail(wanted) BinarySource_data_avail(src, wanted)
|
||||
#define advance(dist) (src->pos += dist)
|
||||
#define here ((const void *)((const unsigned char *)src->data + src->pos))
|
||||
#define consume(dist) \
|
||||
((const void *)((const unsigned char *)src->data + \
|
||||
((src->pos += dist) - dist)))
|
||||
|
||||
ptrlen BinarySource_get_data(BinarySource *src, size_t wanted)
|
||||
{
|
||||
if (!avail(wanted))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(wanted), wanted);
|
||||
}
|
||||
|
||||
unsigned char BinarySource_get_byte(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(1))
|
||||
return 0;
|
||||
|
||||
ucp = consume(1);
|
||||
return *ucp;
|
||||
}
|
||||
|
||||
bool BinarySource_get_bool(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(1))
|
||||
return false;
|
||||
|
||||
ucp = consume(1);
|
||||
return *ucp != 0;
|
||||
}
|
||||
|
||||
unsigned BinarySource_get_uint16(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(2))
|
||||
return 0;
|
||||
|
||||
ucp = consume(2);
|
||||
return GET_16BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
unsigned long BinarySource_get_uint32(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(4))
|
||||
return 0;
|
||||
|
||||
ucp = consume(4);
|
||||
return GET_32BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
uint64_t BinarySource_get_uint64(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(8))
|
||||
return 0;
|
||||
|
||||
ucp = consume(8);
|
||||
return GET_64BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_string(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
size_t len;
|
||||
|
||||
if (!avail(4))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
ucp = consume(4);
|
||||
len = GET_32BIT_MSB_FIRST(ucp);
|
||||
|
||||
if (!avail(len))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(len), len);
|
||||
}
|
||||
|
||||
const char *BinarySource_get_asciz(BinarySource *src)
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
if (src->err)
|
||||
return "";
|
||||
|
||||
start = here;
|
||||
end = memchr(start, '\0', src->len - src->pos);
|
||||
if (!end) {
|
||||
src->err = BSE_OUT_OF_DATA;
|
||||
return "";
|
||||
}
|
||||
|
||||
advance(end + 1 - start);
|
||||
return start;
|
||||
}
|
||||
|
||||
static ptrlen BinarySource_get_chars_internal(
|
||||
BinarySource *src, const char *set, bool include)
|
||||
{
|
||||
const char *start = here;
|
||||
while (avail(1)) {
|
||||
bool present = NULL != strchr(set, *(const char *)consume(0));
|
||||
if (present != include)
|
||||
break;
|
||||
(void) consume(1);
|
||||
}
|
||||
const char *end = here;
|
||||
return make_ptrlen(start, end - start);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_chars(BinarySource *src, const char *include_set)
|
||||
{
|
||||
return BinarySource_get_chars_internal(src, include_set, true);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_nonchars(BinarySource *src, const char *exclude_set)
|
||||
{
|
||||
return BinarySource_get_chars_internal(src, exclude_set, false);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_chomped_line(BinarySource *src)
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
if (src->err)
|
||||
return make_ptrlen(here, 0);
|
||||
|
||||
start = here;
|
||||
end = memchr(start, '\n', src->len - src->pos);
|
||||
if (end)
|
||||
advance(end + 1 - start);
|
||||
else
|
||||
advance(src->len - src->pos);
|
||||
end = here;
|
||||
|
||||
if (end > start && end[-1] == '\n')
|
||||
end--;
|
||||
if (end > start && end[-1] == '\r')
|
||||
end--;
|
||||
|
||||
return make_ptrlen(start, end - start);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_pstring(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
size_t len;
|
||||
|
||||
if (!avail(1))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
ucp = consume(1);
|
||||
len = *ucp;
|
||||
|
||||
if (!avail(len))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(len), len);
|
||||
}
|
||||
|
||||
void BinarySource_REWIND_TO__(BinarySource *src, size_t pos)
|
||||
{
|
||||
if (pos <= src->len) {
|
||||
src->pos = pos;
|
||||
src->err = BSE_NO_ERROR; /* clear any existing error */
|
||||
} else {
|
||||
src->pos = src->len;
|
||||
src->err = BSE_OUT_OF_DATA; /* new error if we rewind out of range */
|
||||
}
|
||||
}
|
||||
|
||||
static void stdio_sink_write(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink);
|
||||
fwrite(data, 1, len, sink->fp);
|
||||
}
|
||||
|
||||
void stdio_sink_init(stdio_sink *sink, FILE *fp)
|
||||
{
|
||||
sink->fp = fp;
|
||||
BinarySink_INIT(sink, stdio_sink_write);
|
||||
}
|
||||
|
||||
static void bufchain_sink_write(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
bufchain_sink *sink = BinarySink_DOWNCAST(bs, bufchain_sink);
|
||||
bufchain_add(sink->ch, data, len);
|
||||
}
|
||||
|
||||
void bufchain_sink_init(bufchain_sink *sink, bufchain *ch)
|
||||
{
|
||||
sink->ch = ch;
|
||||
BinarySink_INIT(sink, bufchain_sink_write);
|
||||
}
|
134
utils/memory.c
Normal file
134
utils/memory.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* PuTTY's memory allocation wrappers.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "puttymem.h"
|
||||
#include "misc.h"
|
||||
|
||||
void *safemalloc(size_t factor1, size_t factor2, size_t addend)
|
||||
{
|
||||
if (factor1 > SIZE_MAX / factor2)
|
||||
goto fail;
|
||||
size_t product = factor1 * factor2;
|
||||
|
||||
if (addend > SIZE_MAX)
|
||||
goto fail;
|
||||
if (product > SIZE_MAX - addend)
|
||||
goto fail;
|
||||
size_t size = product + addend;
|
||||
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
|
||||
void *p;
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_malloc(size);
|
||||
#else
|
||||
p = malloc(size);
|
||||
#endif
|
||||
|
||||
if (!p)
|
||||
goto fail;
|
||||
|
||||
return p;
|
||||
|
||||
fail:
|
||||
out_of_memory();
|
||||
}
|
||||
|
||||
void *saferealloc(void *ptr, size_t n, size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (n > INT_MAX / size) {
|
||||
p = NULL;
|
||||
} else {
|
||||
size *= n;
|
||||
if (!ptr) {
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_malloc(size);
|
||||
#else
|
||||
p = malloc(size);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_realloc(ptr, size);
|
||||
#else
|
||||
p = realloc(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!p)
|
||||
out_of_memory();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void safefree(void *ptr)
|
||||
{
|
||||
if (ptr) {
|
||||
#ifdef MINEFIELD
|
||||
minefield_c_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize,
|
||||
size_t oldlen, size_t extralen, bool secret)
|
||||
{
|
||||
/* The largest value we can safely multiply by eltsize */
|
||||
assert(eltsize > 0);
|
||||
size_t maxsize = (~(size_t)0) / eltsize;
|
||||
|
||||
size_t oldsize = *allocated;
|
||||
|
||||
/* Range-check the input values */
|
||||
assert(oldsize <= maxsize);
|
||||
assert(oldlen <= maxsize);
|
||||
assert(extralen <= maxsize - oldlen);
|
||||
|
||||
/* If the size is already enough, don't bother doing anything! */
|
||||
if (oldsize > oldlen + extralen)
|
||||
return ptr;
|
||||
|
||||
/* Find out how much we need to grow the array by. */
|
||||
size_t increment = (oldlen + extralen) - oldsize;
|
||||
|
||||
/* Invent a new size. We want to grow the array by at least
|
||||
* 'increment' elements; by at least a fixed number of bytes (to
|
||||
* get things started when sizes are small); and by some constant
|
||||
* factor of its old size (to avoid repeated calls to this
|
||||
* function taking quadratic time overall). */
|
||||
if (increment < 256 / eltsize)
|
||||
increment = 256 / eltsize;
|
||||
if (increment < oldsize / 16)
|
||||
increment = oldsize / 16;
|
||||
|
||||
/* But we also can't grow beyond maxsize. */
|
||||
size_t maxincr = maxsize - oldsize;
|
||||
if (increment > maxincr)
|
||||
increment = maxincr;
|
||||
|
||||
size_t newsize = oldsize + increment;
|
||||
void *toret;
|
||||
if (secret) {
|
||||
toret = safemalloc(newsize, eltsize, 0);
|
||||
if (oldsize) {
|
||||
memcpy(toret, ptr, oldsize * eltsize);
|
||||
smemclr(ptr, oldsize * eltsize);
|
||||
sfree(ptr);
|
||||
}
|
||||
} else {
|
||||
toret = saferealloc(ptr, newsize, eltsize);
|
||||
}
|
||||
*allocated = newsize;
|
||||
return toret;
|
||||
}
|
34
utils/memxor.c
Normal file
34
utils/memxor.c
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* XOR two pieces of memory, copying the result into a third, which
|
||||
* may precisely alias one of the input pair (but no guarantees if it
|
||||
* partially overlaps).
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size)
|
||||
{
|
||||
switch (size & 15) {
|
||||
case 0:
|
||||
while (size >= 16) {
|
||||
size -= 16;
|
||||
*out++ = *in1++ ^ *in2++;
|
||||
case 15: *out++ = *in1++ ^ *in2++;
|
||||
case 14: *out++ = *in1++ ^ *in2++;
|
||||
case 13: *out++ = *in1++ ^ *in2++;
|
||||
case 12: *out++ = *in1++ ^ *in2++;
|
||||
case 11: *out++ = *in1++ ^ *in2++;
|
||||
case 10: *out++ = *in1++ ^ *in2++;
|
||||
case 9: *out++ = *in1++ ^ *in2++;
|
||||
case 8: *out++ = *in1++ ^ *in2++;
|
||||
case 7: *out++ = *in1++ ^ *in2++;
|
||||
case 6: *out++ = *in1++ ^ *in2++;
|
||||
case 5: *out++ = *in1++ ^ *in2++;
|
||||
case 4: *out++ = *in1++ ^ *in2++;
|
||||
case 3: *out++ = *in1++ ^ *in2++;
|
||||
case 2: *out++ = *in1++ ^ *in2++;
|
||||
case 1: *out++ = *in1++ ^ *in2++;
|
||||
}
|
||||
}
|
||||
}
|
28
utils/miscucs.c
Normal file
28
utils/miscucs.c
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Centralised Unicode-related helper functions, separate from misc.c
|
||||
* so that they can be omitted from tools that aren't including
|
||||
* Unicode handling.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include "misc.h"
|
||||
|
||||
wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len)
|
||||
{
|
||||
int mult;
|
||||
for (mult = 1 ;; mult++) {
|
||||
wchar_t *ret = snewn(mult*len + 2, wchar_t);
|
||||
int outlen;
|
||||
outlen = mb_to_wc(codepage, flags, string, len, ret, mult*len + 1);
|
||||
if (outlen < mult*len+1) {
|
||||
ret[outlen] = L'\0';
|
||||
return ret;
|
||||
}
|
||||
sfree(ret);
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string)
|
||||
{
|
||||
return dup_mb_to_wc_c(codepage, flags, string, strlen(string));
|
||||
}
|
8
utils/null_lp.c
Normal file
8
utils/null_lp.c
Normal file
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Stub methods usable by LogPolicy implementations.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
bool null_lp_verbose_no(LogPolicy *lp) { return false; }
|
||||
bool null_lp_verbose_yes(LogPolicy *lp) { return true; }
|
42
utils/nullseat.c
Normal file
42
utils/nullseat.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Stub methods usable by Seat implementations.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
size_t nullseat_output(
|
||||
Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
|
||||
bool nullseat_eof(Seat *seat) { return true; }
|
||||
int nullseat_get_userpass_input(
|
||||
Seat *seat, prompts_t *p, bufchain *input) { return 0; }
|
||||
void nullseat_notify_remote_exit(Seat *seat) {}
|
||||
void nullseat_connection_fatal(Seat *seat, const char *message) {}
|
||||
void nullseat_update_specials_menu(Seat *seat) {}
|
||||
char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
|
||||
void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}
|
||||
int nullseat_verify_ssh_host_key(
|
||||
Seat *seat, const char *host, int port, const char *keytype,
|
||||
char *keystr, const char *keydisp, char **key_fingerprints,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
int nullseat_confirm_weak_crypto_primitive(
|
||||
Seat *seat, const char *algtype, const char *algname,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
int nullseat_confirm_weak_cached_hostkey(
|
||||
Seat *seat, const char *algname, const char *betteralgs,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
bool nullseat_is_never_utf8(Seat *seat) { return false; }
|
||||
bool nullseat_is_always_utf8(Seat *seat) { return true; }
|
||||
void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {}
|
||||
const char *nullseat_get_x_display(Seat *seat) { return NULL; }
|
||||
bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; }
|
||||
bool nullseat_get_window_pixel_size(
|
||||
Seat *seat, int *width, int *height) { return false; }
|
||||
StripCtrlChars *nullseat_stripctrl_new(
|
||||
Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;}
|
||||
bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; }
|
||||
bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; }
|
||||
bool nullseat_verbose_no(Seat *seat) { return false; }
|
||||
bool nullseat_verbose_yes(Seat *seat) { return true; }
|
||||
bool nullseat_interactive_no(Seat *seat) { return false; }
|
||||
bool nullseat_interactive_yes(Seat *seat) { return true; }
|
||||
bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; }
|
21
utils/nullstrcmp.c
Normal file
21
utils/nullstrcmp.c
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Compare two strings, just like strcmp, except that we tolerate null
|
||||
* pointers as a legal input, and define them to compare before any
|
||||
* non-null string (even the empty string).
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
int nullstrcmp(const char *a, const char *b)
|
||||
{
|
||||
if (a == NULL && b == NULL)
|
||||
return 0;
|
||||
if (a == NULL)
|
||||
return -1;
|
||||
if (b == NULL)
|
||||
return +1;
|
||||
return strcmp(a, b);
|
||||
}
|
11
utils/out_of_memory.c
Normal file
11
utils/out_of_memory.c
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Standard implementation of the out_of_memory function called by our
|
||||
* malloc wrappers.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
void out_of_memory(void)
|
||||
{
|
||||
modalfatalbox("Out of memory");
|
||||
}
|
40
utils/parse_blocksize.c
Normal file
40
utils/parse_blocksize.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Parse a string block size specification. This is approximately a
|
||||
* subset of the block size specs supported by GNU fileutils:
|
||||
* "nk" = n kilobytes
|
||||
* "nM" = n megabytes
|
||||
* "nG" = n gigabytes
|
||||
* All numbers are decimal, and suffixes refer to powers of two.
|
||||
* Case-insensitive.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
unsigned long parse_blocksize(const char *bs)
|
||||
{
|
||||
char *suf;
|
||||
unsigned long r = strtoul(bs, &suf, 10);
|
||||
if (*suf != '\0') {
|
||||
while (*suf && isspace((unsigned char)*suf)) suf++;
|
||||
switch (*suf) {
|
||||
case 'k': case 'K':
|
||||
r *= 1024ul;
|
||||
break;
|
||||
case 'm': case 'M':
|
||||
r *= 1024ul * 1024ul;
|
||||
break;
|
||||
case 'g': case 'G':
|
||||
r *= 1024ul * 1024ul * 1024ul;
|
||||
break;
|
||||
case '\0':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
59
utils/prompts.c
Normal file
59
utils/prompts.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Functions for making, destroying, and manipulating prompts_t
|
||||
* structures.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
prompts_t *new_prompts(void)
|
||||
{
|
||||
prompts_t *p = snew(prompts_t);
|
||||
p->prompts = NULL;
|
||||
p->n_prompts = p->prompts_size = 0;
|
||||
p->data = NULL;
|
||||
p->to_server = true; /* to be on the safe side */
|
||||
p->name = p->instruction = NULL;
|
||||
p->name_reqd = p->instr_reqd = false;
|
||||
return p;
|
||||
}
|
||||
|
||||
void add_prompt(prompts_t *p, char *promptstr, bool echo)
|
||||
{
|
||||
prompt_t *pr = snew(prompt_t);
|
||||
pr->prompt = promptstr;
|
||||
pr->echo = echo;
|
||||
pr->result = strbuf_new_nm();
|
||||
sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
|
||||
p->prompts[p->n_prompts++] = pr;
|
||||
}
|
||||
|
||||
void prompt_set_result(prompt_t *pr, const char *newstr)
|
||||
{
|
||||
strbuf_clear(pr->result);
|
||||
put_datapl(pr->result, ptrlen_from_asciz(newstr));
|
||||
}
|
||||
|
||||
const char *prompt_get_result_ref(prompt_t *pr)
|
||||
{
|
||||
return pr->result->s;
|
||||
}
|
||||
|
||||
char *prompt_get_result(prompt_t *pr)
|
||||
{
|
||||
return dupstr(pr->result->s);
|
||||
}
|
||||
|
||||
void free_prompts(prompts_t *p)
|
||||
{
|
||||
size_t i;
|
||||
for (i=0; i < p->n_prompts; i++) {
|
||||
prompt_t *pr = p->prompts[i];
|
||||
strbuf_free(pr->result);
|
||||
sfree(pr->prompt);
|
||||
sfree(pr);
|
||||
}
|
||||
sfree(p->prompts);
|
||||
sfree(p->name);
|
||||
sfree(p->instruction);
|
||||
sfree(p);
|
||||
}
|
95
utils/ptrlen.c
Normal file
95
utils/ptrlen.c
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Functions to deal with ptrlens.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "ssh.h"
|
||||
|
||||
bool ptrlen_eq_string(ptrlen pl, const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
return (pl.len == len && !memcmp(pl.ptr, str, len));
|
||||
}
|
||||
|
||||
bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2)
|
||||
{
|
||||
return (pl1.len == pl2.len && !memcmp(pl1.ptr, pl2.ptr, pl1.len));
|
||||
}
|
||||
|
||||
int ptrlen_strcmp(ptrlen pl1, ptrlen pl2)
|
||||
{
|
||||
size_t minlen = pl1.len < pl2.len ? pl1.len : pl2.len;
|
||||
if (minlen) { /* tolerate plX.ptr==NULL as long as plX.len==0 */
|
||||
int cmp = memcmp(pl1.ptr, pl2.ptr, minlen);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
return pl1.len < pl2.len ? -1 : pl1.len > pl2.len ? +1 : 0;
|
||||
}
|
||||
|
||||
bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail)
|
||||
{
|
||||
if (whole.len >= prefix.len &&
|
||||
!memcmp(whole.ptr, prefix.ptr, prefix.len)) {
|
||||
if (tail) {
|
||||
tail->ptr = (const char *)whole.ptr + prefix.len;
|
||||
tail->len = whole.len - prefix.len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail)
|
||||
{
|
||||
if (whole.len >= suffix.len &&
|
||||
!memcmp((char *)whole.ptr + (whole.len - suffix.len),
|
||||
suffix.ptr, suffix.len)) {
|
||||
if (tail) {
|
||||
tail->ptr = whole.ptr;
|
||||
tail->len = whole.len - suffix.len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ptrlen ptrlen_get_word(ptrlen *input, const char *separators)
|
||||
{
|
||||
const char *p = input->ptr, *end = p + input->len;
|
||||
ptrlen toret;
|
||||
|
||||
while (p < end && strchr(separators, *p))
|
||||
p++;
|
||||
toret.ptr = p;
|
||||
while (p < end && !strchr(separators, *p))
|
||||
p++;
|
||||
toret.len = p - (const char *)toret.ptr;
|
||||
|
||||
size_t to_consume = p - (const char *)input->ptr;
|
||||
assert(to_consume <= input->len);
|
||||
input->ptr = (const char *)input->ptr + to_consume;
|
||||
input->len -= to_consume;
|
||||
|
||||
return toret;
|
||||
}
|
||||
|
||||
char *mkstr(ptrlen pl)
|
||||
{
|
||||
char *p = snewn(pl.len + 1, char);
|
||||
memcpy(p, pl.ptr, pl.len);
|
||||
p[pl.len] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
bool strstartswith(const char *s, const char *t)
|
||||
{
|
||||
return !strncmp(s, t, strlen(t));
|
||||
}
|
||||
|
||||
bool strendswith(const char *s, const char *t)
|
||||
{
|
||||
size_t slen = strlen(s), tlen = strlen(t);
|
||||
return slen >= tlen && !strcmp(s + (slen - tlen), t);
|
||||
}
|
19
utils/read_file_into.c
Normal file
19
utils/read_file_into.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Read an entire file into a BinarySink.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
bool read_file_into(BinarySink *bs, FILE *fp)
|
||||
{
|
||||
char buf[4096];
|
||||
while (1) {
|
||||
size_t retd = fread(buf, 1, sizeof(buf), fp);
|
||||
if (retd == 0)
|
||||
return !ferror(fp);
|
||||
put_data(bs, buf, retd);
|
||||
}
|
||||
}
|
20
utils/seat_connection_fatal.c
Normal file
20
utils/seat_connection_fatal.c
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Wrapper function for the connection_fatal() method of a Seat,
|
||||
* providing printf-style formatting.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
void seat_connection_fatal(Seat *seat, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *msg;
|
||||
|
||||
va_start(ap, fmt);
|
||||
msg = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
seat->vt->connection_fatal(seat, msg);
|
||||
sfree(msg); /* if we return */
|
||||
}
|
||||
|
84
utils/sessprep.c
Normal file
84
utils/sessprep.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* sessprep.c: centralise some preprocessing done on Conf objects
|
||||
* before launching them.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
void prepare_session(Conf *conf)
|
||||
{
|
||||
char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
|
||||
char *host = hostbuf;
|
||||
char *p, *q;
|
||||
|
||||
/*
|
||||
* Trim leading whitespace from the hostname.
|
||||
*/
|
||||
host += strspn(host, " \t");
|
||||
|
||||
/*
|
||||
* See if host is of the form user@host, and separate out the
|
||||
* username if so.
|
||||
*/
|
||||
if (host[0] != '\0') {
|
||||
/*
|
||||
* Use strrchr, in case the _username_ in turn is of the form
|
||||
* user@host, which has been known.
|
||||
*/
|
||||
char *atsign = strrchr(host, '@');
|
||||
if (atsign) {
|
||||
*atsign = '\0';
|
||||
conf_set_str(conf, CONF_username, host);
|
||||
host = atsign + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Trim a colon suffix off the hostname if it's there, and discard
|
||||
* the text after it.
|
||||
*
|
||||
* The exact reason why we _ignore_ this text, rather than
|
||||
* treating it as a port number, is unfortunately lost in the
|
||||
* mists of history: the commit which originally introduced this
|
||||
* change on 2001-05-06 was clear on _what_ it was doing but
|
||||
* didn't bother to explain _why_. But I [SGT, 2017-12-03] suspect
|
||||
* it has to do with priority order: what should a saved session
|
||||
* do if its CONF_host contains 'server.example.com:123' and its
|
||||
* CONF_port contains 456? If CONF_port contained the _default_
|
||||
* port number then it might be a good guess that the colon suffix
|
||||
* on the host name was intended to override that, but you don't
|
||||
* really want to get into making heuristic judgments on that
|
||||
* basis.
|
||||
*
|
||||
* (Then again, you could just as easily make the same argument
|
||||
* about whether a 'user@' prefix on the host name should override
|
||||
* CONF_username, which this code _does_ do. I don't have a good
|
||||
* answer, sadly. Both these pieces of behaviour have been around
|
||||
* for years and it would probably cause subtle breakage in all
|
||||
* sorts of long-forgotten scripting to go changing things around
|
||||
* now.)
|
||||
*
|
||||
* In order to protect unbracketed IPv6 address literals against
|
||||
* this treatment, we do not make this change at all if there's
|
||||
* _more_ than one (un-IPv6-bracketed) colon.
|
||||
*/
|
||||
p = host_strchr(host, ':');
|
||||
if (p && p == host_strrchr(host, ':')) {
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any remaining whitespace.
|
||||
*/
|
||||
p = hostbuf;
|
||||
q = host;
|
||||
while (*q) {
|
||||
if (*q != ' ' && *q != '\t')
|
||||
*p++ = *q;
|
||||
q++;
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
conf_set_str(conf, CONF_host, hostbuf);
|
||||
sfree(hostbuf);
|
||||
}
|
14
utils/sk_free_peer_info.c
Normal file
14
utils/sk_free_peer_info.c
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Free a SocketPeerInfo, and everything that dangles off it.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
void sk_free_peer_info(SocketPeerInfo *pi)
|
||||
{
|
||||
if (pi) {
|
||||
sfree((char *)pi->addr_text);
|
||||
sfree((char *)pi->log_text);
|
||||
sfree(pi);
|
||||
}
|
||||
}
|
42
utils/smemclr.c
Normal file
42
utils/smemclr.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Securely wipe memory.
|
||||
*
|
||||
* The actual wiping is no different from what memset would do: the
|
||||
* point of 'securely' is to try to be sure over-clever compilers
|
||||
* won't optimise away memsets on variables that are about to be freed
|
||||
* or go out of scope. See
|
||||
* https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/771-BSI.html
|
||||
*
|
||||
* Some platforms (e.g. Windows) may provide their own version of this
|
||||
* function.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
void smemclr(void *b, size_t n)
|
||||
{
|
||||
volatile char *vp;
|
||||
|
||||
if (b && n > 0) {
|
||||
/*
|
||||
* Zero out the memory.
|
||||
*/
|
||||
memset(b, 0, n);
|
||||
|
||||
/*
|
||||
* Perform a volatile access to the object, forcing the
|
||||
* compiler to admit that the previous memset was important.
|
||||
*
|
||||
* This while loop should in practice run for zero iterations
|
||||
* (since we know we just zeroed the object out), but in
|
||||
* theory (as far as the compiler knows) it might range over
|
||||
* the whole object. (If we had just written, say, '*vp =
|
||||
* *vp;', a compiler could in principle have 'helpfully'
|
||||
* optimised the memset into only zeroing out the first byte.
|
||||
* This should be robust.)
|
||||
*/
|
||||
vp = b;
|
||||
while (*vp) vp++;
|
||||
}
|
||||
}
|
25
utils/smemeq.c
Normal file
25
utils/smemeq.c
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Compare two fixed-size regions of memory, in a crypto-safe way,
|
||||
* i.e. without timing or cache side channels indicating anything
|
||||
* about what the answer was or where the first difference (if any)
|
||||
* might have been.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
bool smemeq(const void *av, const void *bv, size_t len)
|
||||
{
|
||||
const unsigned char *a = (const unsigned char *)av;
|
||||
const unsigned char *b = (const unsigned char *)bv;
|
||||
unsigned val = 0;
|
||||
|
||||
while (len-- > 0) {
|
||||
val |= *a++ ^ *b++;
|
||||
}
|
||||
/* Now val is 0 iff we want to return 1, and in the range
|
||||
* 0x01..0xFF iff we want to return 0. So subtracting from 0x100
|
||||
* will clear bit 8 iff we want to return 0, and leave it set iff
|
||||
* we want to return 1, so then we can just shift down. */
|
||||
return (0x100 - val) >> 8;
|
||||
}
|
27
utils/ssh2_pick_fingerprint.c
Normal file
27
utils/ssh2_pick_fingerprint.c
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Choose an SSH-2 fingerprint type, out of an array of possible ones.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "ssh.h"
|
||||
|
||||
FingerprintType ssh2_pick_fingerprint(
|
||||
char **fingerprints, FingerprintType preferred_type)
|
||||
{
|
||||
/*
|
||||
* Keys are either SSH-2, in which case we have all fingerprint
|
||||
* types, or SSH-1, in which case we have only MD5. So we return
|
||||
* the default type if we can, or MD5 if that's all we have; no
|
||||
* need for a fully general preference-list system.
|
||||
*/
|
||||
FingerprintType fptype = fingerprints[preferred_type] ?
|
||||
preferred_type : SSH_FPTYPE_MD5;
|
||||
assert(fingerprints[fptype]);
|
||||
return fptype;
|
||||
}
|
||||
|
||||
FingerprintType ssh2_pick_default_fingerprint(char **fingerprints)
|
||||
{
|
||||
return ssh2_pick_fingerprint(fingerprints, SSH_FPTYPE_DEFAULT);
|
||||
}
|
128
utils/sshutils.c
Normal file
128
utils/sshutils.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Supporting routines used in common by all the various components of
|
||||
* the SSH system.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "sshchan.h"
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Centralised standard methods for other channel implementations to
|
||||
* borrow.
|
||||
*/
|
||||
|
||||
void chan_remotely_opened_confirmation(Channel *chan)
|
||||
{
|
||||
unreachable("this channel type should never receive OPEN_CONFIRMATION");
|
||||
}
|
||||
|
||||
void chan_remotely_opened_failure(Channel *chan, const char *errtext)
|
||||
{
|
||||
unreachable("this channel type should never receive OPEN_FAILURE");
|
||||
}
|
||||
|
||||
bool chan_default_want_close(
|
||||
Channel *chan, bool sent_local_eof, bool rcvd_remote_eof)
|
||||
{
|
||||
/*
|
||||
* Default close policy: we start initiating the CHANNEL_CLOSE
|
||||
* procedure as soon as both sides of the channel have seen EOF.
|
||||
*/
|
||||
return sent_local_eof && rcvd_remote_eof;
|
||||
}
|
||||
|
||||
bool chan_no_exit_status(Channel *chan, int status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_exit_signal(
|
||||
Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_exit_signal_numeric(
|
||||
Channel *chan, int signum, bool core_dumped, ptrlen msg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_run_shell(Channel *chan)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_run_command(Channel *chan, ptrlen command)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_run_subsystem(Channel *chan, ptrlen subsys)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_enable_x11_forwarding(
|
||||
Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
|
||||
unsigned screen_number)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_enable_agent_forwarding(Channel *chan)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_allocate_pty(
|
||||
Channel *chan, ptrlen termtype, unsigned width, unsigned height,
|
||||
unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_send_break(Channel *chan, unsigned length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_send_signal(Channel *chan, ptrlen signame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chan_no_change_window_size(
|
||||
Channel *chan, unsigned width, unsigned height,
|
||||
unsigned pixwidth, unsigned pixheight)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void chan_no_request_response(Channel *chan, bool success)
|
||||
{
|
||||
unreachable("this channel type should never send a want-reply request");
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Other miscellaneous utility functions.
|
||||
*/
|
||||
|
||||
void free_rportfwd(struct ssh_rportfwd *rpf)
|
||||
{
|
||||
if (rpf) {
|
||||
sfree(rpf->log_description);
|
||||
sfree(rpf->shost);
|
||||
sfree(rpf->dhost);
|
||||
sfree(rpf);
|
||||
}
|
||||
}
|
118
utils/strbuf.c
Normal file
118
utils/strbuf.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Functions to work with strbufs.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
struct strbuf_impl {
|
||||
size_t size;
|
||||
struct strbuf visible;
|
||||
bool nm; /* true if we insist on non-moving buffer resizes */
|
||||
};
|
||||
|
||||
#define STRBUF_SET_UPTR(buf) \
|
||||
((buf)->visible.u = (unsigned char *)(buf)->visible.s)
|
||||
#define STRBUF_SET_PTR(buf, ptr) \
|
||||
((buf)->visible.s = (ptr), STRBUF_SET_UPTR(buf))
|
||||
|
||||
void *strbuf_append(strbuf *buf_o, size_t len)
|
||||
{
|
||||
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||
char *toret;
|
||||
sgrowarray_general(
|
||||
buf->visible.s, buf->size, buf->visible.len + 1, len, buf->nm);
|
||||
STRBUF_SET_UPTR(buf);
|
||||
toret = buf->visible.s + buf->visible.len;
|
||||
buf->visible.len += len;
|
||||
buf->visible.s[buf->visible.len] = '\0';
|
||||
return toret;
|
||||
}
|
||||
|
||||
void strbuf_shrink_to(strbuf *buf, size_t new_len)
|
||||
{
|
||||
assert(new_len <= buf->len);
|
||||
buf->len = new_len;
|
||||
buf->s[buf->len] = '\0';
|
||||
}
|
||||
|
||||
void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove)
|
||||
{
|
||||
assert(amount_to_remove <= buf->len);
|
||||
buf->len -= amount_to_remove;
|
||||
buf->s[buf->len] = '\0';
|
||||
}
|
||||
|
||||
bool strbuf_chomp(strbuf *buf, char char_to_remove)
|
||||
{
|
||||
if (buf->len > 0 && buf->s[buf->len-1] == char_to_remove) {
|
||||
strbuf_shrink_by(buf, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void strbuf_BinarySink_write(
|
||||
BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
strbuf *buf_o = BinarySink_DOWNCAST(bs, strbuf);
|
||||
memcpy(strbuf_append(buf_o, len), data, len);
|
||||
}
|
||||
|
||||
static strbuf *strbuf_new_general(bool nm)
|
||||
{
|
||||
struct strbuf_impl *buf = snew(struct strbuf_impl);
|
||||
BinarySink_INIT(&buf->visible, strbuf_BinarySink_write);
|
||||
buf->visible.len = 0;
|
||||
buf->size = 512;
|
||||
buf->nm = nm;
|
||||
STRBUF_SET_PTR(buf, snewn(buf->size, char));
|
||||
*buf->visible.s = '\0';
|
||||
return &buf->visible;
|
||||
}
|
||||
strbuf *strbuf_new(void) { return strbuf_new_general(false); }
|
||||
strbuf *strbuf_new_nm(void) { return strbuf_new_general(true); }
|
||||
void strbuf_free(strbuf *buf_o)
|
||||
{
|
||||
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||
if (buf->visible.s) {
|
||||
smemclr(buf->visible.s, buf->size);
|
||||
sfree(buf->visible.s);
|
||||
}
|
||||
sfree(buf);
|
||||
}
|
||||
char *strbuf_to_str(strbuf *buf_o)
|
||||
{
|
||||
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||
char *ret = buf->visible.s;
|
||||
sfree(buf);
|
||||
return ret;
|
||||
}
|
||||
void strbuf_catfv(strbuf *buf_o, const char *fmt, va_list ap)
|
||||
{
|
||||
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||
STRBUF_SET_PTR(buf, dupvprintf_inner(buf->visible.s, buf->visible.len,
|
||||
&buf->size, fmt, ap));
|
||||
buf->visible.len += strlen(buf->visible.s + buf->visible.len);
|
||||
}
|
||||
void strbuf_catf(strbuf *buf_o, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
strbuf_catfv(buf_o, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
strbuf *strbuf_new_for_agent_query(void)
|
||||
{
|
||||
strbuf *buf = strbuf_new();
|
||||
strbuf_append(buf, 4);
|
||||
return buf;
|
||||
}
|
||||
void strbuf_finalise_agent_query(strbuf *buf_o)
|
||||
{
|
||||
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||
assert(buf->visible.len >= 5);
|
||||
PUT_32BIT_MSB_FIRST(buf->visible.u, buf->visible.len - 4);
|
||||
}
|
21
utils/string_length_for_printf.c
Normal file
21
utils/string_length_for_printf.c
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Convert a size_t value to int, by saturating it at INT_MAX. Useful
|
||||
* if you want to use the printf idiom "%.*s", where the '*' precision
|
||||
* specifier expects an int in the variadic argument list, but what
|
||||
* you have is not an int but a size_t. This method of converting to
|
||||
* int will at least do something _safe_ with overlong values, even if
|
||||
* (due to the limitation of printf itself) the whole string still
|
||||
* won't be printed.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
int string_length_for_printf(size_t s)
|
||||
{
|
||||
if (s > INT_MAX)
|
||||
return INT_MAX;
|
||||
return s;
|
||||
}
|
476
utils/stripctrl.c
Normal file
476
utils/stripctrl.c
Normal file
@ -0,0 +1,476 @@
|
||||
/*
|
||||
* stripctrl.c: a facility for stripping control characters out of a
|
||||
* data stream (defined as any multibyte character in the system
|
||||
* locale which is neither printable nor \n), using the standard C
|
||||
* library multibyte character facilities.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "terminal.h"
|
||||
#include "misc.h"
|
||||
#include "marshal.h"
|
||||
|
||||
#define SCC_BUFSIZE 64
|
||||
#define LINE_LIMIT 77
|
||||
|
||||
typedef struct StripCtrlCharsImpl StripCtrlCharsImpl;
|
||||
struct StripCtrlCharsImpl {
|
||||
mbstate_t mbs_in, mbs_out;
|
||||
|
||||
bool permit_cr;
|
||||
wchar_t substitution;
|
||||
|
||||
char buf[SCC_BUFSIZE];
|
||||
size_t buflen;
|
||||
|
||||
Terminal *term;
|
||||
bool last_term_utf;
|
||||
struct term_utf8_decode utf8;
|
||||
unsigned long (*translate)(Terminal *, term_utf8_decode *, unsigned char);
|
||||
|
||||
bool line_limit;
|
||||
bool line_start;
|
||||
size_t line_chars_remaining;
|
||||
|
||||
BinarySink *bs_out;
|
||||
|
||||
StripCtrlChars public;
|
||||
};
|
||||
|
||||
static void stripctrl_locale_BinarySink_write(
|
||||
BinarySink *bs, const void *vp, size_t len);
|
||||
static void stripctrl_term_BinarySink_write(
|
||||
BinarySink *bs, const void *vp, size_t len);
|
||||
|
||||
static StripCtrlCharsImpl *stripctrl_new_common(
|
||||
BinarySink *bs_out, bool permit_cr, wchar_t substitution)
|
||||
{
|
||||
StripCtrlCharsImpl *scc = snew(StripCtrlCharsImpl);
|
||||
memset(scc, 0, sizeof(StripCtrlCharsImpl)); /* zeroes mbstates */
|
||||
scc->bs_out = bs_out;
|
||||
scc->permit_cr = permit_cr;
|
||||
scc->substitution = substitution;
|
||||
return scc;
|
||||
}
|
||||
|
||||
StripCtrlChars *stripctrl_new(
|
||||
BinarySink *bs_out, bool permit_cr, wchar_t substitution)
|
||||
{
|
||||
StripCtrlCharsImpl *scc = stripctrl_new_common(
|
||||
bs_out, permit_cr, substitution);
|
||||
BinarySink_INIT(&scc->public, stripctrl_locale_BinarySink_write);
|
||||
return &scc->public;
|
||||
}
|
||||
|
||||
StripCtrlChars *stripctrl_new_term_fn(
|
||||
BinarySink *bs_out, bool permit_cr, wchar_t substitution,
|
||||
Terminal *term, unsigned long (*translate)(
|
||||
Terminal *, term_utf8_decode *, unsigned char))
|
||||
{
|
||||
StripCtrlCharsImpl *scc = stripctrl_new_common(
|
||||
bs_out, permit_cr, substitution);
|
||||
scc->term = term;
|
||||
scc->translate = translate;
|
||||
BinarySink_INIT(&scc->public, stripctrl_term_BinarySink_write);
|
||||
return &scc->public;
|
||||
}
|
||||
|
||||
void stripctrl_retarget(StripCtrlChars *sccpub, BinarySink *new_bs_out)
|
||||
{
|
||||
StripCtrlCharsImpl *scc =
|
||||
container_of(sccpub, StripCtrlCharsImpl, public);
|
||||
scc->bs_out = new_bs_out;
|
||||
stripctrl_reset(sccpub);
|
||||
}
|
||||
|
||||
void stripctrl_reset(StripCtrlChars *sccpub)
|
||||
{
|
||||
StripCtrlCharsImpl *scc =
|
||||
container_of(sccpub, StripCtrlCharsImpl, public);
|
||||
|
||||
/*
|
||||
* Clear all the fields that might have been in the middle of a
|
||||
* multibyte character or non-default shift state, so that we can
|
||||
* start converting a fresh piece of data to send to a channel
|
||||
* that hasn't seen the previous output.
|
||||
*/
|
||||
memset(&scc->utf8, 0, sizeof(scc->utf8));
|
||||
memset(&scc->mbs_in, 0, sizeof(scc->mbs_in));
|
||||
memset(&scc->mbs_out, 0, sizeof(scc->mbs_out));
|
||||
|
||||
/*
|
||||
* Also, reset the line-limiting system to its starting state.
|
||||
*/
|
||||
scc->line_start = true;
|
||||
}
|
||||
|
||||
void stripctrl_free(StripCtrlChars *sccpub)
|
||||
{
|
||||
StripCtrlCharsImpl *scc =
|
||||
container_of(sccpub, StripCtrlCharsImpl, public);
|
||||
smemclr(scc, sizeof(StripCtrlCharsImpl));
|
||||
sfree(scc);
|
||||
}
|
||||
|
||||
void stripctrl_enable_line_limiting(StripCtrlChars *sccpub)
|
||||
{
|
||||
StripCtrlCharsImpl *scc =
|
||||
container_of(sccpub, StripCtrlCharsImpl, public);
|
||||
scc->line_limit = true;
|
||||
scc->line_start = true;
|
||||
}
|
||||
|
||||
static inline bool stripctrl_ctrlchar_ok(StripCtrlCharsImpl *scc, wchar_t wc)
|
||||
{
|
||||
return wc == L'\n' || (wc == L'\r' && scc->permit_cr);
|
||||
}
|
||||
|
||||
static inline void stripctrl_check_line_limit(
|
||||
StripCtrlCharsImpl *scc, wchar_t wc, size_t width)
|
||||
{
|
||||
if (!scc->line_limit)
|
||||
return; /* nothing to do */
|
||||
|
||||
if (scc->line_start) {
|
||||
put_datapl(scc->bs_out, PTRLEN_LITERAL("| "));
|
||||
scc->line_start = false;
|
||||
scc->line_chars_remaining = LINE_LIMIT;
|
||||
}
|
||||
|
||||
if (wc == '\n') {
|
||||
scc->line_start = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scc->line_chars_remaining < width) {
|
||||
put_datapl(scc->bs_out, PTRLEN_LITERAL("\r\n> "));
|
||||
scc->line_chars_remaining = LINE_LIMIT;
|
||||
}
|
||||
|
||||
assert(width <= scc->line_chars_remaining);
|
||||
scc->line_chars_remaining -= width;
|
||||
}
|
||||
|
||||
static inline void stripctrl_locale_put_wc(StripCtrlCharsImpl *scc, wchar_t wc)
|
||||
{
|
||||
int width = mk_wcwidth(wc);
|
||||
if ((iswprint(wc) && width >= 0) || stripctrl_ctrlchar_ok(scc, wc)) {
|
||||
/* Printable character, or one we're going to let through anyway. */
|
||||
if (width < 0)
|
||||
width = 0; /* sanitise for stripctrl_check_line_limit */
|
||||
} else if (scc->substitution) {
|
||||
wc = scc->substitution;
|
||||
width = mk_wcwidth(wc);
|
||||
assert(width >= 0);
|
||||
} else {
|
||||
/* No defined substitution, so don't write any output wchar_t. */
|
||||
return;
|
||||
}
|
||||
|
||||
stripctrl_check_line_limit(scc, wc, width);
|
||||
|
||||
char outbuf[MB_LEN_MAX];
|
||||
size_t produced = wcrtomb(outbuf, wc, &scc->mbs_out);
|
||||
if (produced > 0)
|
||||
put_data(scc->bs_out, outbuf, produced);
|
||||
}
|
||||
|
||||
static inline void stripctrl_term_put_wc(
|
||||
StripCtrlCharsImpl *scc, unsigned long wc)
|
||||
{
|
||||
ptrlen prefix = PTRLEN_LITERAL("");
|
||||
int width = term_char_width(scc->term, wc);
|
||||
|
||||
if (!(wc & ~0x9F) || width < 0) {
|
||||
/* This is something the terminal interprets as a control
|
||||
* character. */
|
||||
if (!stripctrl_ctrlchar_ok(scc, wc)) {
|
||||
if (!scc->substitution) {
|
||||
return;
|
||||
} else {
|
||||
wc = scc->substitution;
|
||||
width = term_char_width(scc->term, wc);
|
||||
assert(width >= 0);
|
||||
}
|
||||
} else {
|
||||
if (width < 0)
|
||||
width = 0; /* sanitise for stripctrl_check_line_limit */
|
||||
}
|
||||
|
||||
if (wc == '\012') {
|
||||
/* Precede \n with \r, because our terminal will not
|
||||
* generally be in the ONLCR mode where it assumes that
|
||||
* internally, and any \r on input has been stripped
|
||||
* out. */
|
||||
prefix = PTRLEN_LITERAL("\r");
|
||||
}
|
||||
}
|
||||
|
||||
stripctrl_check_line_limit(scc, wc, width);
|
||||
|
||||
if (prefix.len)
|
||||
put_datapl(scc->bs_out, prefix);
|
||||
|
||||
char outbuf[6];
|
||||
size_t produced;
|
||||
|
||||
/*
|
||||
* The Terminal implementation encodes 7-bit ASCII characters in
|
||||
* UTF-8 mode, and all printing characters in non-UTF-8 (i.e.
|
||||
* single-byte character set) mode, as values in the surrogate
|
||||
* range (a conveniently unused piece of space in this context)
|
||||
* whose low byte is the original 1-byte representation of the
|
||||
* character.
|
||||
*/
|
||||
if ((wc - 0xD800) < (0xE000 - 0xD800))
|
||||
wc &= 0xFF;
|
||||
|
||||
if (in_utf(scc->term)) {
|
||||
produced = encode_utf8(outbuf, wc);
|
||||
} else {
|
||||
outbuf[0] = wc;
|
||||
produced = 1;
|
||||
}
|
||||
|
||||
if (produced > 0)
|
||||
put_data(scc->bs_out, outbuf, produced);
|
||||
}
|
||||
|
||||
static inline size_t stripctrl_locale_try_consume(
|
||||
StripCtrlCharsImpl *scc, const char *p, size_t len)
|
||||
{
|
||||
wchar_t wc;
|
||||
mbstate_t mbs_orig = scc->mbs_in;
|
||||
size_t consumed = mbrtowc(&wc, p, len, &scc->mbs_in);
|
||||
|
||||
if (consumed == (size_t)-2) {
|
||||
/*
|
||||
* The buffer is too short to see the end of the multibyte
|
||||
* character that it appears to be starting with. We return 0
|
||||
* for 'no data consumed', restore the conversion state from
|
||||
* before consuming the partial character, and our caller will
|
||||
* come back when it has more data available.
|
||||
*/
|
||||
scc->mbs_in = mbs_orig;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (consumed == (size_t)-1) {
|
||||
/*
|
||||
* The buffer contains an illegal multibyte sequence. There's
|
||||
* no really good way to recover from this, so we'll just
|
||||
* reset our input state, consume a single byte without
|
||||
* emitting anything, and hope we can resynchronise to
|
||||
* _something_ sooner or later.
|
||||
*/
|
||||
memset(&scc->mbs_in, 0, sizeof(scc->mbs_in));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (consumed == 0) {
|
||||
/*
|
||||
* A zero wide character is encoded by the data, but mbrtowc
|
||||
* hasn't told us how many input bytes it takes. There isn't
|
||||
* really anything good we can do here, so we just advance by
|
||||
* one byte in the hope that that was the NUL.
|
||||
*
|
||||
* (If it wasn't - that is, if we're in a multibyte encoding
|
||||
* in which the terminator of a normal C string is encoded in
|
||||
* some way other than a single zero byte - then probably lots
|
||||
* of other things will have gone wrong before we get here!)
|
||||
*/
|
||||
stripctrl_locale_put_wc(scc, L'\0');
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, this is the easy case: consumed > 0, and we've eaten
|
||||
* a valid multibyte character.
|
||||
*/
|
||||
stripctrl_locale_put_wc(scc, wc);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void stripctrl_locale_BinarySink_write(
|
||||
BinarySink *bs, const void *vp, size_t len)
|
||||
{
|
||||
StripCtrlChars *sccpub = BinarySink_DOWNCAST(bs, StripCtrlChars);
|
||||
StripCtrlCharsImpl *scc =
|
||||
container_of(sccpub, StripCtrlCharsImpl, public);
|
||||
const char *p = (const char *)vp;
|
||||
|
||||
const char *previous_locale = setlocale(LC_CTYPE, NULL);
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
/*
|
||||
* Deal with any partial multibyte character buffered from last
|
||||
* time.
|
||||
*/
|
||||
while (scc->buflen > 0) {
|
||||
size_t to_copy = SCC_BUFSIZE - scc->buflen;
|
||||
if (to_copy > len)
|
||||
to_copy = len;
|
||||
|
||||
memcpy(scc->buf + scc->buflen, p, to_copy);
|
||||
size_t consumed = stripctrl_locale_try_consume(
|
||||
scc, scc->buf, scc->buflen + to_copy);
|
||||
|
||||
if (consumed >= scc->buflen) {
|
||||
/*
|
||||
* We've consumed a multibyte character that includes all
|
||||
* the data buffered from last time. So we can clear our
|
||||
* buffer and move on to processing the main input string
|
||||
* in situ, having first discarded whatever initial
|
||||
* segment of it completed our previous character.
|
||||
*/
|
||||
size_t consumed_from_main_string = consumed - scc->buflen;
|
||||
assert(consumed_from_main_string <= len);
|
||||
p += consumed_from_main_string;
|
||||
len -= consumed_from_main_string;
|
||||
scc->buflen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (consumed == 0) {
|
||||
/*
|
||||
* If we didn't manage to consume anything, i.e. the whole
|
||||
* buffer contains an incomplete sequence, it had better
|
||||
* be because our entire input string _this_ time plus
|
||||
* whatever leftover data we had from _last_ time still
|
||||
* comes to less than SCC_BUFSIZE. In other words, we've
|
||||
* already copied all the new data on to the end of our
|
||||
* buffer, and it still hasn't helped. So increment buflen
|
||||
* to reflect the new data, and return.
|
||||
*/
|
||||
assert(to_copy == len);
|
||||
scc->buflen += to_copy;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, we've somehow consumed _less_ data than we had
|
||||
* buffered, and yet we weren't able to consume that data in
|
||||
* the last call to this function. That sounds impossible, but
|
||||
* I can think of one situation in which it could happen: if
|
||||
* we had an incomplete MB sequence last time, and now more
|
||||
* data has arrived, it turns out to be an _illegal_ one, so
|
||||
* we consume one byte in the hope of resynchronising.
|
||||
*
|
||||
* Anyway, in this case we move the buffer up and go back
|
||||
* round this initial loop.
|
||||
*/
|
||||
scc->buflen -= consumed;
|
||||
memmove(scc->buf, scc->buf + consumed, scc->buflen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now charge along the main string.
|
||||
*/
|
||||
while (len > 0) {
|
||||
size_t consumed = stripctrl_locale_try_consume(scc, p, len);
|
||||
if (consumed == 0)
|
||||
break;
|
||||
assert(consumed <= len);
|
||||
p += consumed;
|
||||
len -= consumed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Any data remaining should be copied into our buffer, to keep
|
||||
* for next time.
|
||||
*/
|
||||
assert(len <= SCC_BUFSIZE);
|
||||
memcpy(scc->buf, p, len);
|
||||
scc->buflen = len;
|
||||
|
||||
out:
|
||||
setlocale(LC_CTYPE, previous_locale);
|
||||
}
|
||||
|
||||
static void stripctrl_term_BinarySink_write(
|
||||
BinarySink *bs, const void *vp, size_t len)
|
||||
{
|
||||
StripCtrlChars *sccpub = BinarySink_DOWNCAST(bs, StripCtrlChars);
|
||||
StripCtrlCharsImpl *scc =
|
||||
container_of(sccpub, StripCtrlCharsImpl, public);
|
||||
|
||||
bool utf = in_utf(scc->term);
|
||||
if (utf != scc->last_term_utf) {
|
||||
scc->last_term_utf = utf;
|
||||
scc->utf8.state = 0;
|
||||
}
|
||||
|
||||
for (const unsigned char *p = (const unsigned char *)vp;
|
||||
len > 0; len--, p++) {
|
||||
unsigned long t = scc->translate(scc->term, &scc->utf8, *p);
|
||||
if (t == UCSTRUNCATED) {
|
||||
stripctrl_term_put_wc(scc, 0xFFFD);
|
||||
/* go round again */
|
||||
t = scc->translate(scc->term, &scc->utf8, *p);
|
||||
}
|
||||
if (t == UCSINCOMPLETE)
|
||||
continue;
|
||||
if (t == UCSINVALID)
|
||||
t = 0xFFFD;
|
||||
|
||||
stripctrl_term_put_wc(scc, t);
|
||||
}
|
||||
}
|
||||
|
||||
char *stripctrl_string_ptrlen(StripCtrlChars *sccpub, ptrlen str)
|
||||
{
|
||||
strbuf *out = strbuf_new();
|
||||
stripctrl_retarget(sccpub, BinarySink_UPCAST(out));
|
||||
put_datapl(sccpub, str);
|
||||
stripctrl_retarget(sccpub, NULL);
|
||||
return strbuf_to_str(out);
|
||||
}
|
||||
|
||||
#ifdef STRIPCTRL_TEST
|
||||
|
||||
/*
|
||||
gcc -std=c99 -DSTRIPCTRL_TEST -o scctest stripctrl.c marshal.c utils.c memory.c wcwidth.c -I . -I unix -I charset
|
||||
*/
|
||||
|
||||
void out_of_memory(void) { fprintf(stderr, "out of memory\n"); abort(); }
|
||||
|
||||
void stripctrl_write(BinarySink *bs, const void *vdata, size_t len)
|
||||
{
|
||||
const uint8_t *p = vdata;
|
||||
printf("[");
|
||||
for (size_t i = 0; i < len; i++)
|
||||
printf("%*s%02x", i?1:0, "", (unsigned)p[i]);
|
||||
printf("]");
|
||||
}
|
||||
|
||||
void stripctrl_test(StripCtrlChars *scc, ptrlen pl)
|
||||
{
|
||||
stripctrl_write(NULL, pl.ptr, pl.len);
|
||||
printf(" -> ");
|
||||
put_datapl(scc, pl);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct foo { BinarySink_IMPLEMENTATION; } foo;
|
||||
BinarySink_INIT(&foo, stripctrl_write);
|
||||
StripCtrlChars *scc = stripctrl_new(BinarySink_UPCAST(&foo), false, '?');
|
||||
stripctrl_test(scc, PTRLEN_LITERAL("a\033[1mb"));
|
||||
stripctrl_test(scc, PTRLEN_LITERAL("a\xC2\x9B[1mb"));
|
||||
stripctrl_test(scc, PTRLEN_LITERAL("a\xC2\xC2[1mb"));
|
||||
stripctrl_test(scc, PTRLEN_LITERAL("\xC3"));
|
||||
stripctrl_test(scc, PTRLEN_LITERAL("\xA9"));
|
||||
stripctrl_test(scc, PTRLEN_LITERAL("\xE2\x80\x8F"));
|
||||
stripctrl_test(scc, PTRLEN_LITERAL("a\0b"));
|
||||
stripctrl_free(scc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* STRIPCTRL_TEST */
|
1611
utils/tree234.c
Normal file
1611
utils/tree234.c
Normal file
File diff suppressed because it is too large
Load Diff
12
utils/utils.h
Normal file
12
utils/utils.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Internal header to the utils subdirectory, for definitions shared
|
||||
* between the library implementations but not intended to be exposed
|
||||
* further than that.
|
||||
*/
|
||||
|
||||
void dputs(const char *);
|
||||
|
||||
char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr,
|
||||
const char *fmt, va_list ap);
|
||||
|
||||
const char *host_strchr_internal(const char *s, const char *set, bool first);
|
116
utils/validate_manual_hostkey.c
Normal file
116
utils/validate_manual_hostkey.c
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Validate a manual host key specification (either entered in the
|
||||
* GUI, or via -hostkey). If valid, we return true, and update 'key'
|
||||
* to contain a canonicalised version of the key string in 'key'
|
||||
* (which is guaranteed to take up at most as much space as the
|
||||
* original version), suitable for putting into the Conf. If not
|
||||
* valid, we return false.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define BASE64_CHARS_NOEQ \
|
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
|
||||
#define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "="
|
||||
|
||||
bool validate_manual_hostkey(char *key)
|
||||
{
|
||||
char *p, *q, *r, *s;
|
||||
|
||||
/*
|
||||
* Step through the string word by word, looking for a word that's
|
||||
* in one of the formats we like.
|
||||
*/
|
||||
p = key;
|
||||
while ((p += strspn(p, " \t"))[0]) {
|
||||
q = p;
|
||||
p += strcspn(p, " \t");
|
||||
if (*p) *p++ = '\0';
|
||||
|
||||
/*
|
||||
* Now q is our word.
|
||||
*/
|
||||
|
||||
if (strstartswith(q, "SHA256:")) {
|
||||
/* Test for a valid SHA256 key fingerprint. */
|
||||
r = q + 7;
|
||||
if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
r = q;
|
||||
if (strstartswith(r, "MD5:"))
|
||||
r += 4;
|
||||
if (strlen(r) == 16*3 - 1 &&
|
||||
r[strspn(r, "0123456789abcdefABCDEF:")] == 0) {
|
||||
/*
|
||||
* Test for a valid MD5 key fingerprint. Check the colons
|
||||
* are in the right places, and if so, return the same
|
||||
* fingerprint canonicalised into lowercase.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < 16; i++)
|
||||
if (r[3*i] == ':' || r[3*i+1] == ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 15; i++)
|
||||
if (r[3*i+2] != ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 16*3 - 1; i++)
|
||||
key[i] = tolower(r[i]);
|
||||
key[16*3 - 1] = '\0';
|
||||
return true;
|
||||
}
|
||||
not_fingerprint:;
|
||||
|
||||
/*
|
||||
* Before we check for a public-key blob, trim newlines out of
|
||||
* the middle of the word, in case someone's managed to paste
|
||||
* in a public-key blob _with_ them.
|
||||
*/
|
||||
for (r = s = q; *r; r++)
|
||||
if (*r != '\n' && *r != '\r')
|
||||
*s++ = *r;
|
||||
*s = '\0';
|
||||
|
||||
if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
|
||||
q[strspn(q, BASE64_CHARS_ALL)] == 0) {
|
||||
/*
|
||||
* Might be a base64-encoded SSH-2 public key blob. Check
|
||||
* that it starts with a sensible algorithm string. No
|
||||
* canonicalisation is necessary for this string type.
|
||||
*
|
||||
* The algorithm string must be at most 64 characters long
|
||||
* (RFC 4251 section 6).
|
||||
*/
|
||||
unsigned char decoded[6];
|
||||
unsigned alglen;
|
||||
int minlen;
|
||||
int len = 0;
|
||||
|
||||
len += base64_decode_atom(q, decoded+len);
|
||||
if (len < 3)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
len += base64_decode_atom(q+4, decoded+len);
|
||||
if (len < 4)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
alglen = GET_32BIT_MSB_FIRST(decoded);
|
||||
if (alglen > 64)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
minlen = ((alglen + 4) + 2) / 3;
|
||||
if (strlen(q) < minlen)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
strcpy(key, q);
|
||||
return true;
|
||||
}
|
||||
not_ssh2_blob:;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
23
utils/version.c
Normal file
23
utils/version.c
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* PuTTY version numbering
|
||||
*/
|
||||
|
||||
/*
|
||||
* The difficult part of deciding what goes in these version strings
|
||||
* is done in Buildscr, and then written into version.h. All we have
|
||||
* to do here is to drop it into variables of the right names.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
const char ver[] = TEXTVER;
|
||||
const char sshver[] = SSHVER;
|
||||
|
||||
/*
|
||||
* SSH local version string MUST be under 40 characters. Here's a
|
||||
* compile time assertion to verify this.
|
||||
*/
|
||||
enum { vorpal_sword = 1 / (sizeof(sshver) <= 40) };
|
558
utils/wcwidth.c
Normal file
558
utils/wcwidth.c
Normal file
@ -0,0 +1,558 @@
|
||||
/*
|
||||
* This is an implementation of wcwidth() and wcswidth() (defined in
|
||||
* IEEE Std 1002.1-2001) for Unicode.
|
||||
*
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
|
||||
*
|
||||
* In fixed-width output devices, Latin characters all occupy a single
|
||||
* "cell" position of equal width, whereas ideographic CJK characters
|
||||
* occupy two such cells. Interoperability between terminal-line
|
||||
* applications and (teletype-style) character terminals using the
|
||||
* UTF-8 encoding requires agreement on which character should advance
|
||||
* the cursor by how many cell positions. No established formal
|
||||
* standards exist at present on which Unicode character shall occupy
|
||||
* how many cell positions on character terminals. These routines are
|
||||
* a first attempt of defining such behavior based on simple rules
|
||||
* applied to data provided by the Unicode Consortium.
|
||||
*
|
||||
* For some graphical characters, the Unicode standard explicitly
|
||||
* defines a character-cell width via the definition of the East Asian
|
||||
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
|
||||
* In all these cases, there is no ambiguity about which width a
|
||||
* terminal shall use. For characters in the East Asian Ambiguous (A)
|
||||
* class, the width choice depends purely on a preference of backward
|
||||
* compatibility with either historic CJK or Western practice.
|
||||
* Choosing single-width for these characters is easy to justify as
|
||||
* the appropriate long-term solution, as the CJK practice of
|
||||
* displaying these characters as double-width comes from historic
|
||||
* implementation simplicity (8-bit encoded characters were displayed
|
||||
* single-width and 16-bit ones double-width, even for Greek,
|
||||
* Cyrillic, etc.) and not any typographic considerations.
|
||||
*
|
||||
* Much less clear is the choice of width for the Not East Asian
|
||||
* (Neutral) class. Existing practice does not dictate a width for any
|
||||
* of these characters. It would nevertheless make sense
|
||||
* typographically to allocate two character cells to characters such
|
||||
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
|
||||
* represented adequately with a single-width glyph. The following
|
||||
* routines at present merely assign a single-cell width to all
|
||||
* neutral characters, in the interest of simplicity. This is not
|
||||
* entirely satisfactory and should be reconsidered before
|
||||
* establishing a formal standard in this area. At the moment, the
|
||||
* decision which Not East Asian (Neutral) characters should be
|
||||
* represented by double-width glyphs cannot yet be answered by
|
||||
* applying a simple rule from the Unicode database content. Setting
|
||||
* up a proper standard for the behavior of UTF-8 character terminals
|
||||
* will require a careful analysis not only of each Unicode character,
|
||||
* but also of each presentation form, something the author of these
|
||||
* routines has avoided to do so far.
|
||||
*
|
||||
* http://www.unicode.org/unicode/reports/tr11/
|
||||
*
|
||||
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* for any purpose and without fee is hereby granted. The author
|
||||
* disclaims all warranties with regard to this software.
|
||||
*
|
||||
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
*/
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "putty.h" /* for prototypes */
|
||||
|
||||
struct interval {
|
||||
unsigned int first;
|
||||
unsigned int last;
|
||||
};
|
||||
|
||||
/* auxiliary function for binary search in interval table */
|
||||
static bool bisearch(unsigned int ucs, const struct interval *table, int max) {
|
||||
int min = 0;
|
||||
int mid;
|
||||
|
||||
if (ucs < table[0].first || ucs > table[max].last)
|
||||
return false;
|
||||
while (max >= min) {
|
||||
mid = (min + max) / 2;
|
||||
if (ucs > table[mid].last)
|
||||
min = mid + 1;
|
||||
else if (ucs < table[mid].first)
|
||||
max = mid - 1;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* The following two functions define the column width of an ISO 10646
|
||||
* character as follows:
|
||||
*
|
||||
* - The null character (U+0000) has a column width of 0.
|
||||
*
|
||||
* - Other C0/C1 control characters and DEL will lead to a return
|
||||
* value of -1.
|
||||
*
|
||||
* - Non-spacing and enclosing combining characters (general
|
||||
* category code Mn or Me in the Unicode database) have a
|
||||
* column width of 0.
|
||||
*
|
||||
* - SOFT HYPHEN (U+00AD) has a column width of 1.
|
||||
*
|
||||
* - Other format characters (general category code Cf in the Unicode
|
||||
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
|
||||
*
|
||||
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
|
||||
* have a column width of 0.
|
||||
*
|
||||
* - Spacing characters in the East Asian Wide (W) or East Asian
|
||||
* Full-width (F) category as defined in Unicode Technical
|
||||
* Report #11 have a column width of 2.
|
||||
*
|
||||
* - All remaining characters (including all printable
|
||||
* ISO 8859-1 and WGL4 characters, Unicode control characters,
|
||||
* etc.) have a column width of 1.
|
||||
*
|
||||
* This implementation assumes that wchar_t characters are encoded
|
||||
* in ISO 10646.
|
||||
*/
|
||||
|
||||
int mk_wcwidth(unsigned int ucs)
|
||||
{
|
||||
/* sorted list of non-overlapping intervals of non-spacing characters */
|
||||
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
|
||||
static const struct interval combining[] = {
|
||||
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
|
||||
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
|
||||
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
|
||||
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
|
||||
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
|
||||
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
|
||||
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
|
||||
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
|
||||
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
|
||||
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
|
||||
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
|
||||
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
|
||||
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
|
||||
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
|
||||
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
|
||||
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
|
||||
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
|
||||
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
|
||||
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
|
||||
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
|
||||
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
|
||||
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
|
||||
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
|
||||
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
|
||||
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
|
||||
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
|
||||
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
|
||||
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
|
||||
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
|
||||
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
|
||||
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
|
||||
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
|
||||
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
|
||||
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
|
||||
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
|
||||
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
|
||||
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
|
||||
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
|
||||
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
|
||||
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
|
||||
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
|
||||
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
|
||||
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
|
||||
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
|
||||
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
|
||||
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
|
||||
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
|
||||
{ 0xE0100, 0xE01EF }
|
||||
};
|
||||
|
||||
/* A sorted list of intervals of double-width characters generated by:
|
||||
* https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl
|
||||
* from the Unicode 9.0.0 data files available at:
|
||||
* https://www.unicode.org/Public/13.0.0/ucd/
|
||||
*/
|
||||
static const struct interval wide[] = {
|
||||
{0x1100, 0x115F},
|
||||
{0x231A, 0x231B},
|
||||
{0x2329, 0x232A},
|
||||
{0x23E9, 0x23EC},
|
||||
{0x23F0, 0x23F0},
|
||||
{0x23F3, 0x23F3},
|
||||
{0x25FD, 0x25FE},
|
||||
{0x2614, 0x2615},
|
||||
{0x2648, 0x2653},
|
||||
{0x267F, 0x267F},
|
||||
{0x2693, 0x2693},
|
||||
{0x26A1, 0x26A1},
|
||||
{0x26AA, 0x26AB},
|
||||
{0x26BD, 0x26BE},
|
||||
{0x26C4, 0x26C5},
|
||||
{0x26CE, 0x26CE},
|
||||
{0x26D4, 0x26D4},
|
||||
{0x26EA, 0x26EA},
|
||||
{0x26F2, 0x26F3},
|
||||
{0x26F5, 0x26F5},
|
||||
{0x26FA, 0x26FA},
|
||||
{0x26FD, 0x26FD},
|
||||
{0x2705, 0x2705},
|
||||
{0x270A, 0x270B},
|
||||
{0x2728, 0x2728},
|
||||
{0x274C, 0x274C},
|
||||
{0x274E, 0x274E},
|
||||
{0x2753, 0x2755},
|
||||
{0x2757, 0x2757},
|
||||
{0x2795, 0x2797},
|
||||
{0x27B0, 0x27B0},
|
||||
{0x27BF, 0x27BF},
|
||||
{0x2B1B, 0x2B1C},
|
||||
{0x2B50, 0x2B50},
|
||||
{0x2B55, 0x2B55},
|
||||
{0x2E80, 0x2E99},
|
||||
{0x2E9B, 0x2EF3},
|
||||
{0x2F00, 0x2FD5},
|
||||
{0x2FF0, 0x2FFB},
|
||||
{0x3000, 0x303E},
|
||||
{0x3041, 0x3096},
|
||||
{0x3099, 0x30FF},
|
||||
{0x3105, 0x312F},
|
||||
{0x3131, 0x318E},
|
||||
{0x3190, 0x31E3},
|
||||
{0x31F0, 0x321E},
|
||||
{0x3220, 0x3247},
|
||||
{0x3250, 0x4DBF},
|
||||
{0x4E00, 0xA48C},
|
||||
{0xA490, 0xA4C6},
|
||||
{0xA960, 0xA97C},
|
||||
{0xAC00, 0xD7A3},
|
||||
{0xF900, 0xFAFF},
|
||||
{0xFE10, 0xFE19},
|
||||
{0xFE30, 0xFE52},
|
||||
{0xFE54, 0xFE66},
|
||||
{0xFE68, 0xFE6B},
|
||||
{0xFF01, 0xFF60},
|
||||
{0xFFE0, 0xFFE6},
|
||||
{0x16FE0, 0x16FE4},
|
||||
{0x16FF0, 0x16FF1},
|
||||
{0x17000, 0x187F7},
|
||||
{0x18800, 0x18CD5},
|
||||
{0x18D00, 0x18D08},
|
||||
{0x1B000, 0x1B11E},
|
||||
{0x1B150, 0x1B152},
|
||||
{0x1B164, 0x1B167},
|
||||
{0x1B170, 0x1B2FB},
|
||||
{0x1F004, 0x1F004},
|
||||
{0x1F0CF, 0x1F0CF},
|
||||
{0x1F18E, 0x1F18E},
|
||||
{0x1F191, 0x1F19A},
|
||||
{0x1F200, 0x1F202},
|
||||
{0x1F210, 0x1F23B},
|
||||
{0x1F240, 0x1F248},
|
||||
{0x1F250, 0x1F251},
|
||||
{0x1F260, 0x1F265},
|
||||
{0x1F300, 0x1F320},
|
||||
{0x1F32D, 0x1F335},
|
||||
{0x1F337, 0x1F37C},
|
||||
{0x1F37E, 0x1F393},
|
||||
{0x1F3A0, 0x1F3CA},
|
||||
{0x1F3CF, 0x1F3D3},
|
||||
{0x1F3E0, 0x1F3F0},
|
||||
{0x1F3F4, 0x1F3F4},
|
||||
{0x1F3F8, 0x1F43E},
|
||||
{0x1F440, 0x1F440},
|
||||
{0x1F442, 0x1F4FC},
|
||||
{0x1F4FF, 0x1F53D},
|
||||
{0x1F54B, 0x1F54E},
|
||||
{0x1F550, 0x1F567},
|
||||
{0x1F57A, 0x1F57A},
|
||||
{0x1F595, 0x1F596},
|
||||
{0x1F5A4, 0x1F5A4},
|
||||
{0x1F5FB, 0x1F64F},
|
||||
{0x1F680, 0x1F6C5},
|
||||
{0x1F6CC, 0x1F6CC},
|
||||
{0x1F6D0, 0x1F6D2},
|
||||
{0x1F6D5, 0x1F6D7},
|
||||
{0x1F6EB, 0x1F6EC},
|
||||
{0x1F6F4, 0x1F6FC},
|
||||
{0x1F7E0, 0x1F7EB},
|
||||
{0x1F90C, 0x1F93A},
|
||||
{0x1F93C, 0x1F945},
|
||||
{0x1F947, 0x1F978},
|
||||
{0x1F97A, 0x1F9CB},
|
||||
{0x1F9CD, 0x1F9FF},
|
||||
{0x1FA70, 0x1FA74},
|
||||
{0x1FA78, 0x1FA7A},
|
||||
{0x1FA80, 0x1FA86},
|
||||
{0x1FA90, 0x1FAA8},
|
||||
{0x1FAB0, 0x1FAB6},
|
||||
{0x1FAC0, 0x1FAC2},
|
||||
{0x1FAD0, 0x1FAD6},
|
||||
{0x20000, 0x2FFFD},
|
||||
{0x30000, 0x3FFFD},
|
||||
};
|
||||
|
||||
/* test for 8-bit control characters */
|
||||
if (ucs == 0)
|
||||
return 0;
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
|
||||
return -1;
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, combining,
|
||||
sizeof(combining) / sizeof(struct interval) - 1))
|
||||
return 0;
|
||||
|
||||
/* if we arrive here, ucs is not a combining or C0/C1 control character */
|
||||
|
||||
/* binary search in table of double-width characters */
|
||||
if (bisearch(ucs, wide,
|
||||
sizeof(wide) / sizeof(struct interval) - 1))
|
||||
return 2;
|
||||
|
||||
/* normal width character */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int mk_wcswidth(const unsigned int *pwcs, size_t n)
|
||||
{
|
||||
int w, width = 0;
|
||||
|
||||
for (;*pwcs && n-- > 0; pwcs++)
|
||||
if ((w = mk_wcwidth(*pwcs)) < 0)
|
||||
return -1;
|
||||
else
|
||||
width += w;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The following functions are the same as mk_wcwidth() and
|
||||
* mk_wcswidth(), except that spacing characters in the East Asian
|
||||
* Ambiguous (A) category as defined in Unicode Technical Report #11
|
||||
* have a column width of 2. This variant might be useful for users of
|
||||
* CJK legacy encodings who want to migrate to UCS without changing
|
||||
* the traditional terminal character-width behaviour. It is not
|
||||
* otherwise recommended for general use.
|
||||
*/
|
||||
int mk_wcwidth_cjk(unsigned int ucs)
|
||||
{
|
||||
/* A sorted list of intervals of ambiguous width characters generated by:
|
||||
* https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl
|
||||
* from the Unicode 9.0.0 data files available at:
|
||||
* http://www.unicode.org/Public/9.0.0/ucd/
|
||||
*/
|
||||
static const struct interval ambiguous[] = {
|
||||
{0x00A1, 0x00A1},
|
||||
{0x00A4, 0x00A4},
|
||||
{0x00A7, 0x00A8},
|
||||
{0x00AA, 0x00AA},
|
||||
{0x00AD, 0x00AE},
|
||||
{0x00B0, 0x00B4},
|
||||
{0x00B6, 0x00BA},
|
||||
{0x00BC, 0x00BF},
|
||||
{0x00C6, 0x00C6},
|
||||
{0x00D0, 0x00D0},
|
||||
{0x00D7, 0x00D8},
|
||||
{0x00DE, 0x00E1},
|
||||
{0x00E6, 0x00E6},
|
||||
{0x00E8, 0x00EA},
|
||||
{0x00EC, 0x00ED},
|
||||
{0x00F0, 0x00F0},
|
||||
{0x00F2, 0x00F3},
|
||||
{0x00F7, 0x00FA},
|
||||
{0x00FC, 0x00FC},
|
||||
{0x00FE, 0x00FE},
|
||||
{0x0101, 0x0101},
|
||||
{0x0111, 0x0111},
|
||||
{0x0113, 0x0113},
|
||||
{0x011B, 0x011B},
|
||||
{0x0126, 0x0127},
|
||||
{0x012B, 0x012B},
|
||||
{0x0131, 0x0133},
|
||||
{0x0138, 0x0138},
|
||||
{0x013F, 0x0142},
|
||||
{0x0144, 0x0144},
|
||||
{0x0148, 0x014B},
|
||||
{0x014D, 0x014D},
|
||||
{0x0152, 0x0153},
|
||||
{0x0166, 0x0167},
|
||||
{0x016B, 0x016B},
|
||||
{0x01CE, 0x01CE},
|
||||
{0x01D0, 0x01D0},
|
||||
{0x01D2, 0x01D2},
|
||||
{0x01D4, 0x01D4},
|
||||
{0x01D6, 0x01D6},
|
||||
{0x01D8, 0x01D8},
|
||||
{0x01DA, 0x01DA},
|
||||
{0x01DC, 0x01DC},
|
||||
{0x0251, 0x0251},
|
||||
{0x0261, 0x0261},
|
||||
{0x02C4, 0x02C4},
|
||||
{0x02C7, 0x02C7},
|
||||
{0x02C9, 0x02CB},
|
||||
{0x02CD, 0x02CD},
|
||||
{0x02D0, 0x02D0},
|
||||
{0x02D8, 0x02DB},
|
||||
{0x02DD, 0x02DD},
|
||||
{0x02DF, 0x02DF},
|
||||
{0x0300, 0x036F},
|
||||
{0x0391, 0x03A1},
|
||||
{0x03A3, 0x03A9},
|
||||
{0x03B1, 0x03C1},
|
||||
{0x03C3, 0x03C9},
|
||||
{0x0401, 0x0401},
|
||||
{0x0410, 0x044F},
|
||||
{0x0451, 0x0451},
|
||||
{0x2010, 0x2010},
|
||||
{0x2013, 0x2016},
|
||||
{0x2018, 0x2019},
|
||||
{0x201C, 0x201D},
|
||||
{0x2020, 0x2022},
|
||||
{0x2024, 0x2027},
|
||||
{0x2030, 0x2030},
|
||||
{0x2032, 0x2033},
|
||||
{0x2035, 0x2035},
|
||||
{0x203B, 0x203B},
|
||||
{0x203E, 0x203E},
|
||||
{0x2074, 0x2074},
|
||||
{0x207F, 0x207F},
|
||||
{0x2081, 0x2084},
|
||||
{0x20AC, 0x20AC},
|
||||
{0x2103, 0x2103},
|
||||
{0x2105, 0x2105},
|
||||
{0x2109, 0x2109},
|
||||
{0x2113, 0x2113},
|
||||
{0x2116, 0x2116},
|
||||
{0x2121, 0x2122},
|
||||
{0x2126, 0x2126},
|
||||
{0x212B, 0x212B},
|
||||
{0x2153, 0x2154},
|
||||
{0x215B, 0x215E},
|
||||
{0x2160, 0x216B},
|
||||
{0x2170, 0x2179},
|
||||
{0x2189, 0x2189},
|
||||
{0x2190, 0x2199},
|
||||
{0x21B8, 0x21B9},
|
||||
{0x21D2, 0x21D2},
|
||||
{0x21D4, 0x21D4},
|
||||
{0x21E7, 0x21E7},
|
||||
{0x2200, 0x2200},
|
||||
{0x2202, 0x2203},
|
||||
{0x2207, 0x2208},
|
||||
{0x220B, 0x220B},
|
||||
{0x220F, 0x220F},
|
||||
{0x2211, 0x2211},
|
||||
{0x2215, 0x2215},
|
||||
{0x221A, 0x221A},
|
||||
{0x221D, 0x2220},
|
||||
{0x2223, 0x2223},
|
||||
{0x2225, 0x2225},
|
||||
{0x2227, 0x222C},
|
||||
{0x222E, 0x222E},
|
||||
{0x2234, 0x2237},
|
||||
{0x223C, 0x223D},
|
||||
{0x2248, 0x2248},
|
||||
{0x224C, 0x224C},
|
||||
{0x2252, 0x2252},
|
||||
{0x2260, 0x2261},
|
||||
{0x2264, 0x2267},
|
||||
{0x226A, 0x226B},
|
||||
{0x226E, 0x226F},
|
||||
{0x2282, 0x2283},
|
||||
{0x2286, 0x2287},
|
||||
{0x2295, 0x2295},
|
||||
{0x2299, 0x2299},
|
||||
{0x22A5, 0x22A5},
|
||||
{0x22BF, 0x22BF},
|
||||
{0x2312, 0x2312},
|
||||
{0x2460, 0x24E9},
|
||||
{0x24EB, 0x254B},
|
||||
{0x2550, 0x2573},
|
||||
{0x2580, 0x258F},
|
||||
{0x2592, 0x2595},
|
||||
{0x25A0, 0x25A1},
|
||||
{0x25A3, 0x25A9},
|
||||
{0x25B2, 0x25B3},
|
||||
{0x25B6, 0x25B7},
|
||||
{0x25BC, 0x25BD},
|
||||
{0x25C0, 0x25C1},
|
||||
{0x25C6, 0x25C8},
|
||||
{0x25CB, 0x25CB},
|
||||
{0x25CE, 0x25D1},
|
||||
{0x25E2, 0x25E5},
|
||||
{0x25EF, 0x25EF},
|
||||
{0x2605, 0x2606},
|
||||
{0x2609, 0x2609},
|
||||
{0x260E, 0x260F},
|
||||
{0x261C, 0x261C},
|
||||
{0x261E, 0x261E},
|
||||
{0x2640, 0x2640},
|
||||
{0x2642, 0x2642},
|
||||
{0x2660, 0x2661},
|
||||
{0x2663, 0x2665},
|
||||
{0x2667, 0x266A},
|
||||
{0x266C, 0x266D},
|
||||
{0x266F, 0x266F},
|
||||
{0x269E, 0x269F},
|
||||
{0x26BF, 0x26BF},
|
||||
{0x26C6, 0x26CD},
|
||||
{0x26CF, 0x26D3},
|
||||
{0x26D5, 0x26E1},
|
||||
{0x26E3, 0x26E3},
|
||||
{0x26E8, 0x26E9},
|
||||
{0x26EB, 0x26F1},
|
||||
{0x26F4, 0x26F4},
|
||||
{0x26F6, 0x26F9},
|
||||
{0x26FB, 0x26FC},
|
||||
{0x26FE, 0x26FF},
|
||||
{0x273D, 0x273D},
|
||||
{0x2776, 0x277F},
|
||||
{0x2B56, 0x2B59},
|
||||
{0x3248, 0x324F},
|
||||
{0xE000, 0xF8FF},
|
||||
{0xFE00, 0xFE0F},
|
||||
{0xFFFD, 0xFFFD},
|
||||
{0x1F100, 0x1F10A},
|
||||
{0x1F110, 0x1F12D},
|
||||
{0x1F130, 0x1F169},
|
||||
{0x1F170, 0x1F18D},
|
||||
{0x1F18F, 0x1F190},
|
||||
{0x1F19B, 0x1F1AC},
|
||||
{0xE0100, 0xE01EF},
|
||||
{0xF0000, 0xFFFFD},
|
||||
{0x100000, 0x10FFFD},
|
||||
};
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, ambiguous,
|
||||
sizeof(ambiguous) / sizeof(struct interval) - 1))
|
||||
return 2;
|
||||
|
||||
return mk_wcwidth(ucs);
|
||||
}
|
||||
|
||||
|
||||
int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n)
|
||||
{
|
||||
int w, width = 0;
|
||||
|
||||
for (;*pwcs && n-- > 0; pwcs++)
|
||||
if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
|
||||
return -1;
|
||||
else
|
||||
width += w;
|
||||
|
||||
return width;
|
||||
}
|
486
utils/wildcard.c
Normal file
486
utils/wildcard.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* Wildcard matching engine for use with SFTP-based file transfer
|
||||
* programs (PSFTP, new-look PSCP): since SFTP has no notion of
|
||||
* getting the remote side to do globbing (and rightly so) we have
|
||||
* to do it locally, by retrieving all the filenames in a directory
|
||||
* and checking each against the wildcard pattern.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
/*
|
||||
* Definition of wildcard syntax:
|
||||
*
|
||||
* - * matches any sequence of characters, including zero.
|
||||
* - ? matches exactly one character which can be anything.
|
||||
* - [abc] matches exactly one character which is a, b or c.
|
||||
* - [a-f] matches anything from a through f.
|
||||
* - [^a-f] matches anything _except_ a through f.
|
||||
* - [-_] matches - or _; [^-_] matches anything else. (The - is
|
||||
* non-special if it occurs immediately after the opening
|
||||
* bracket or ^.)
|
||||
* - [a^] matches an a or a ^. (The ^ is non-special if it does
|
||||
* _not_ occur immediately after the opening bracket.)
|
||||
* - \*, \?, \[, \], \\ match the single characters *, ?, [, ], \.
|
||||
* - All other characters are non-special and match themselves.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Some notes on differences from POSIX globs (IEEE Std 1003.1, 2003 ed.):
|
||||
* - backslashes act as escapes even within [] bracket expressions
|
||||
* - does not support [!...] for non-matching list (POSIX are weird);
|
||||
* NB POSIX allows [^...] as well via "A bracket expression starting
|
||||
* with an unquoted circumflex character produces unspecified
|
||||
* results". If we wanted to allow [!...] we might want to define
|
||||
* [^!] as having its literal meaning (match '^' or '!').
|
||||
* - none of the scary [[:class:]] stuff, etc
|
||||
*/
|
||||
|
||||
/*
|
||||
* The wildcard matching technique we use is very simple and
|
||||
* potentially O(N^2) in running time, but I don't anticipate it
|
||||
* being that bad in reality (particularly since N will be the size
|
||||
* of a filename, which isn't all that much). Perhaps one day, once
|
||||
* PuTTY has grown a regexp matcher for some other reason, I might
|
||||
* come back and reimplement wildcards by translating them into
|
||||
* regexps or directly into NFAs; but for the moment, in the
|
||||
* absence of any other need for the NFA->DFA translation engine,
|
||||
* anything more than the simplest possible wildcard matcher is
|
||||
* vast code-size overkill.
|
||||
*
|
||||
* Essentially, these wildcards are much simpler than regexps in
|
||||
* that they consist of a sequence of rigid fragments (? and [...]
|
||||
* can never match more or less than one character) separated by
|
||||
* asterisks. It is therefore extremely simple to look at a rigid
|
||||
* fragment and determine whether or not it begins at a particular
|
||||
* point in the test string; so we can search along the string
|
||||
* until we find each fragment, then search for the next. As long
|
||||
* as we find each fragment in the _first_ place it occurs, there
|
||||
* will never be a danger of having to backpedal and try to find it
|
||||
* again somewhere else.
|
||||
*/
|
||||
|
||||
enum {
|
||||
WC_TRAILINGBACKSLASH = 1,
|
||||
WC_UNCLOSEDCLASS,
|
||||
WC_INVALIDRANGE
|
||||
};
|
||||
|
||||
/*
|
||||
* Error reporting is done by returning various negative values
|
||||
* from the wildcard routines. Passing any such value to wc_error
|
||||
* will give a human-readable message.
|
||||
*/
|
||||
const char *wc_error(int value)
|
||||
{
|
||||
value = abs(value);
|
||||
switch (value) {
|
||||
case WC_TRAILINGBACKSLASH:
|
||||
return "'\' occurred at end of string (expected another character)";
|
||||
case WC_UNCLOSEDCLASS:
|
||||
return "expected ']' to close character class";
|
||||
case WC_INVALIDRANGE:
|
||||
return "character range was not terminated (']' just after '-')";
|
||||
}
|
||||
return "INTERNAL ERROR: unrecognised wildcard error number";
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the routine that tests a target string to see if an
|
||||
* initial substring of it matches a fragment. If successful, it
|
||||
* returns 1, and advances both `fragment' and `target' past the
|
||||
* fragment and matching substring respectively. If unsuccessful it
|
||||
* returns zero. If the wildcard fragment suffers a syntax error,
|
||||
* it returns <0 and the precise value indexes into wc_error.
|
||||
*/
|
||||
static int wc_match_fragment(const char **fragment, const char **target,
|
||||
const char *target_end)
|
||||
{
|
||||
const char *f, *t;
|
||||
|
||||
f = *fragment;
|
||||
t = *target;
|
||||
/*
|
||||
* The fragment terminates at either the end of the string, or
|
||||
* the first (unescaped) *.
|
||||
*/
|
||||
while (*f && *f != '*' && t < target_end) {
|
||||
/*
|
||||
* Extract one character from t, and one character's worth
|
||||
* of pattern from f, and step along both. Return 0 if they
|
||||
* fail to match.
|
||||
*/
|
||||
if (*f == '\\') {
|
||||
/*
|
||||
* Backslash, which means f[1] is to be treated as a
|
||||
* literal character no matter what it is. It may not
|
||||
* be the end of the string.
|
||||
*/
|
||||
if (!f[1])
|
||||
return -WC_TRAILINGBACKSLASH; /* error */
|
||||
if (f[1] != *t)
|
||||
return 0; /* failed to match */
|
||||
f += 2;
|
||||
} else if (*f == '?') {
|
||||
/*
|
||||
* Question mark matches anything.
|
||||
*/
|
||||
f++;
|
||||
} else if (*f == '[') {
|
||||
bool invert = false;
|
||||
bool matched = false;
|
||||
/*
|
||||
* Open bracket introduces a character class.
|
||||
*/
|
||||
f++;
|
||||
if (*f == '^') {
|
||||
invert = true;
|
||||
f++;
|
||||
}
|
||||
while (*f != ']') {
|
||||
if (*f == '\\')
|
||||
f++; /* backslashes still work */
|
||||
if (!*f)
|
||||
return -WC_UNCLOSEDCLASS; /* error again */
|
||||
if (f[1] == '-') {
|
||||
int lower, upper, ourchr;
|
||||
lower = (unsigned char) *f++;
|
||||
f++; /* eat the minus */
|
||||
if (*f == ']')
|
||||
return -WC_INVALIDRANGE; /* different error! */
|
||||
if (*f == '\\')
|
||||
f++; /* backslashes _still_ work */
|
||||
if (!*f)
|
||||
return -WC_UNCLOSEDCLASS; /* error again */
|
||||
upper = (unsigned char) *f++;
|
||||
ourchr = (unsigned char) *t;
|
||||
if (lower > upper) {
|
||||
int t = lower; lower = upper; upper = t;
|
||||
}
|
||||
if (ourchr >= lower && ourchr <= upper)
|
||||
matched = true;
|
||||
} else {
|
||||
matched |= (*t == *f++);
|
||||
}
|
||||
}
|
||||
if (invert == matched)
|
||||
return 0; /* failed to match character class */
|
||||
f++; /* eat the ] */
|
||||
} else {
|
||||
/*
|
||||
* Non-special character matches itself.
|
||||
*/
|
||||
if (*f != *t)
|
||||
return 0;
|
||||
f++;
|
||||
}
|
||||
/*
|
||||
* Now we've done that, increment t past the character we
|
||||
* matched.
|
||||
*/
|
||||
t++;
|
||||
}
|
||||
if (!*f || *f == '*') {
|
||||
/*
|
||||
* We have reached the end of f without finding a mismatch;
|
||||
* so we're done. Update the caller pointers and return 1.
|
||||
*/
|
||||
*fragment = f;
|
||||
*target = t;
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* Otherwise, we must have reached the end of t before we
|
||||
* reached the end of f; so we've failed. Return 0.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the real wildcard matching routine. It returns 1 for a
|
||||
* successful match, 0 for an unsuccessful match, and <0 for a
|
||||
* syntax error in the wildcard.
|
||||
*/
|
||||
static int wc_match_inner(
|
||||
const char *wildcard, const char *target, size_t target_len)
|
||||
{
|
||||
const char *target_end = target + target_len;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Every time we see a '*' _followed_ by a fragment, we just
|
||||
* search along the string for a location at which the fragment
|
||||
* matches. The only special case is when we see a fragment
|
||||
* right at the start, in which case we just call the matching
|
||||
* routine once and give up if it fails.
|
||||
*/
|
||||
if (*wildcard != '*') {
|
||||
ret = wc_match_fragment(&wildcard, &target, target_end);
|
||||
if (ret <= 0)
|
||||
return ret; /* pass back failure or error alike */
|
||||
}
|
||||
|
||||
while (*wildcard) {
|
||||
assert(*wildcard == '*');
|
||||
while (*wildcard == '*')
|
||||
wildcard++;
|
||||
|
||||
/*
|
||||
* It's possible we've just hit the end of the wildcard
|
||||
* after seeing a *, in which case there's no need to
|
||||
* bother searching any more because we've won.
|
||||
*/
|
||||
if (!*wildcard)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Now `wildcard' points at the next fragment. So we
|
||||
* attempt to match it against `target', and if that fails
|
||||
* we increment `target' and try again, and so on. When we
|
||||
* find we're about to try matching against the empty
|
||||
* string, we give up and return 0.
|
||||
*/
|
||||
ret = 0;
|
||||
while (*target) {
|
||||
const char *save_w = wildcard, *save_t = target;
|
||||
|
||||
ret = wc_match_fragment(&wildcard, &target, target_end);
|
||||
|
||||
if (ret < 0)
|
||||
return ret; /* syntax error */
|
||||
|
||||
if (ret > 0 && !*wildcard && target != target_end) {
|
||||
/*
|
||||
* Final special case - literally.
|
||||
*
|
||||
* This situation arises when we are matching a
|
||||
* _terminal_ fragment of the wildcard (that is,
|
||||
* there is nothing after it, e.g. "*a"), and it
|
||||
* has matched _too early_. For example, matching
|
||||
* "*a" against "parka" will match the "a" fragment
|
||||
* against the _first_ a, and then (if it weren't
|
||||
* for this special case) matching would fail
|
||||
* because we're at the end of the wildcard but not
|
||||
* at the end of the target string.
|
||||
*
|
||||
* In this case what we must do is measure the
|
||||
* length of the fragment in the target (which is
|
||||
* why we saved `target'), jump straight to that
|
||||
* distance from the end of the string using
|
||||
* strlen, and match the same fragment again there
|
||||
* (which is why we saved `wildcard'). Then we
|
||||
* return whatever that operation returns.
|
||||
*/
|
||||
target = target_end - (target - save_t);
|
||||
wildcard = save_w;
|
||||
return wc_match_fragment(&wildcard, &target, target_end);
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
break;
|
||||
target++;
|
||||
}
|
||||
if (ret > 0)
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we reach here, it must be because we successfully matched
|
||||
* a fragment and then found ourselves right at the end of the
|
||||
* wildcard. Hence, we return 1 if and only if we are also
|
||||
* right at the end of the target.
|
||||
*/
|
||||
return target == target_end;
|
||||
}
|
||||
|
||||
int wc_match(const char *wildcard, const char *target)
|
||||
{
|
||||
return wc_match_inner(wildcard, target, strlen(target));
|
||||
}
|
||||
|
||||
int wc_match_pl(const char *wildcard, ptrlen target)
|
||||
{
|
||||
return wc_match_inner(wildcard, target.ptr, target.len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Another utility routine that translates a non-wildcard string
|
||||
* into its raw equivalent by removing any escaping backslashes.
|
||||
* Expects a target string buffer of anything up to the length of
|
||||
* the original wildcard. You can also pass NULL as the output
|
||||
* buffer if you're only interested in the return value.
|
||||
*
|
||||
* Returns true on success, or false if a wildcard character was
|
||||
* encountered. In the latter case the output string MAY not be
|
||||
* zero-terminated and you should not use it for anything!
|
||||
*/
|
||||
bool wc_unescape(char *output, const char *wildcard)
|
||||
{
|
||||
while (*wildcard) {
|
||||
if (*wildcard == '\\') {
|
||||
wildcard++;
|
||||
/* We are lenient about trailing backslashes in non-wildcards. */
|
||||
if (*wildcard) {
|
||||
if (output)
|
||||
*output++ = *wildcard;
|
||||
wildcard++;
|
||||
}
|
||||
} else if (*wildcard == '*' || *wildcard == '?' ||
|
||||
*wildcard == '[' || *wildcard == ']') {
|
||||
return false; /* it's a wildcard! */
|
||||
} else {
|
||||
if (output)
|
||||
*output++ = *wildcard;
|
||||
wildcard++;
|
||||
}
|
||||
}
|
||||
if (output)
|
||||
*output = '\0';
|
||||
return true; /* it's clean */
|
||||
}
|
||||
|
||||
#ifdef TESTMODE
|
||||
|
||||
struct test {
|
||||
const char *wildcard;
|
||||
const char *target;
|
||||
int expected_result;
|
||||
};
|
||||
|
||||
const struct test fragment_tests[] = {
|
||||
/*
|
||||
* We exhaustively unit-test the fragment matching routine
|
||||
* itself, which should save us the need to test all its
|
||||
* intricacies during the full wildcard tests.
|
||||
*/
|
||||
{"abc", "abc", 1},
|
||||
{"abc", "abd", 0},
|
||||
{"abc", "abcd", 1},
|
||||
{"abcd", "abc", 0},
|
||||
{"ab[cd]", "abc", 1},
|
||||
{"ab[cd]", "abd", 1},
|
||||
{"ab[cd]", "abe", 0},
|
||||
{"ab[^cd]", "abc", 0},
|
||||
{"ab[^cd]", "abd", 0},
|
||||
{"ab[^cd]", "abe", 1},
|
||||
{"ab\\", "abc", -WC_TRAILINGBACKSLASH},
|
||||
{"ab\\*", "ab*", 1},
|
||||
{"ab\\?", "ab*", 0},
|
||||
{"ab?", "abc", 1},
|
||||
{"ab?", "ab", 0},
|
||||
{"ab[", "abc", -WC_UNCLOSEDCLASS},
|
||||
{"ab[c-", "abb", -WC_UNCLOSEDCLASS},
|
||||
{"ab[c-]", "abb", -WC_INVALIDRANGE},
|
||||
{"ab[c-e]", "abb", 0},
|
||||
{"ab[c-e]", "abc", 1},
|
||||
{"ab[c-e]", "abd", 1},
|
||||
{"ab[c-e]", "abe", 1},
|
||||
{"ab[c-e]", "abf", 0},
|
||||
{"ab[e-c]", "abb", 0},
|
||||
{"ab[e-c]", "abc", 1},
|
||||
{"ab[e-c]", "abd", 1},
|
||||
{"ab[e-c]", "abe", 1},
|
||||
{"ab[e-c]", "abf", 0},
|
||||
{"ab[^c-e]", "abb", 1},
|
||||
{"ab[^c-e]", "abc", 0},
|
||||
{"ab[^c-e]", "abd", 0},
|
||||
{"ab[^c-e]", "abe", 0},
|
||||
{"ab[^c-e]", "abf", 1},
|
||||
{"ab[^e-c]", "abb", 1},
|
||||
{"ab[^e-c]", "abc", 0},
|
||||
{"ab[^e-c]", "abd", 0},
|
||||
{"ab[^e-c]", "abe", 0},
|
||||
{"ab[^e-c]", "abf", 1},
|
||||
{"ab[a^]", "aba", 1},
|
||||
{"ab[a^]", "ab^", 1},
|
||||
{"ab[a^]", "abb", 0},
|
||||
{"ab[^a^]", "aba", 0},
|
||||
{"ab[^a^]", "ab^", 0},
|
||||
{"ab[^a^]", "abb", 1},
|
||||
{"ab[-c]", "ab-", 1},
|
||||
{"ab[-c]", "abc", 1},
|
||||
{"ab[-c]", "abd", 0},
|
||||
{"ab[^-c]", "ab-", 0},
|
||||
{"ab[^-c]", "abc", 0},
|
||||
{"ab[^-c]", "abd", 1},
|
||||
{"ab[\\[-\\]]", "abZ", 0},
|
||||
{"ab[\\[-\\]]", "ab[", 1},
|
||||
{"ab[\\[-\\]]", "ab\\", 1},
|
||||
{"ab[\\[-\\]]", "ab]", 1},
|
||||
{"ab[\\[-\\]]", "ab^", 0},
|
||||
{"ab[^\\[-\\]]", "abZ", 1},
|
||||
{"ab[^\\[-\\]]", "ab[", 0},
|
||||
{"ab[^\\[-\\]]", "ab\\", 0},
|
||||
{"ab[^\\[-\\]]", "ab]", 0},
|
||||
{"ab[^\\[-\\]]", "ab^", 1},
|
||||
{"ab[a-fA-F]", "aba", 1},
|
||||
{"ab[a-fA-F]", "abF", 1},
|
||||
{"ab[a-fA-F]", "abZ", 0},
|
||||
};
|
||||
|
||||
const struct test full_tests[] = {
|
||||
{"a", "argh", 0},
|
||||
{"a", "ba", 0},
|
||||
{"a", "a", 1},
|
||||
{"a*", "aardvark", 1},
|
||||
{"a*", "badger", 0},
|
||||
{"*a", "park", 0},
|
||||
{"*a", "pArka", 1},
|
||||
{"*a", "parka", 1},
|
||||
{"*a*", "park", 1},
|
||||
{"*a*", "perk", 0},
|
||||
{"?b*r?", "abracadabra", 1},
|
||||
{"?b*r?", "abracadabr", 0},
|
||||
{"?b*r?", "abracadabzr", 0},
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
int fails, passes;
|
||||
|
||||
fails = passes = 0;
|
||||
|
||||
for (i = 0; i < sizeof(fragment_tests)/sizeof(*fragment_tests); i++) {
|
||||
const char *f, *t;
|
||||
int eret, aret;
|
||||
f = fragment_tests[i].wildcard;
|
||||
t = fragment_tests[i].target;
|
||||
eret = fragment_tests[i].expected_result;
|
||||
aret = wc_match_fragment(&f, &t, t + strlen(t));
|
||||
if (aret != eret) {
|
||||
printf("failed test: /%s/ against /%s/ returned %d not %d\n",
|
||||
fragment_tests[i].wildcard, fragment_tests[i].target,
|
||||
aret, eret);
|
||||
fails++;
|
||||
} else
|
||||
passes++;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(full_tests)/sizeof(*full_tests); i++) {
|
||||
const char *f, *t;
|
||||
int eret, aret;
|
||||
f = full_tests[i].wildcard;
|
||||
t = full_tests[i].target;
|
||||
eret = full_tests[i].expected_result;
|
||||
aret = wc_match(f, t);
|
||||
if (aret != eret) {
|
||||
printf("failed test: /%s/ against /%s/ returned %d not %d\n",
|
||||
full_tests[i].wildcard, full_tests[i].target,
|
||||
aret, eret);
|
||||
fails++;
|
||||
} else
|
||||
passes++;
|
||||
}
|
||||
|
||||
printf("passed %d, failed %d\n", passes, fails);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
31
utils/write_c_string_literal.c
Normal file
31
utils/write_c_string_literal.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Write data to a file in the form of a C string literal, with any
|
||||
* non-printable-ASCII character escaped appropriately.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
|
||||
void write_c_string_literal(FILE *fp, ptrlen str)
|
||||
{
|
||||
for (const char *p = str.ptr; p < (const char *)str.ptr + str.len; p++) {
|
||||
char c = *p;
|
||||
|
||||
if (c == '\n')
|
||||
fputs("\\n", fp);
|
||||
else if (c == '\r')
|
||||
fputs("\\r", fp);
|
||||
else if (c == '\t')
|
||||
fputs("\\t", fp);
|
||||
else if (c == '\b')
|
||||
fputs("\\b", fp);
|
||||
else if (c == '\\')
|
||||
fputs("\\\\", fp);
|
||||
else if (c == '"')
|
||||
fputs("\\\"", fp);
|
||||
else if (c >= 32 && c <= 126)
|
||||
fputc(c, fp);
|
||||
else
|
||||
fprintf(fp, "\\%03o", (unsigned char)c);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user