pax_global_header00006660000000000000000000000064133365675300014525gustar00rootroot0000000000000052 comment=30313659572ad9e6f1d6a773d03ce5892a3317eb its-playback-time-0.2017-08-30.3c40fd3/000077500000000000000000000000001333656753000166475ustar00rootroot00000000000000its-playback-time-0.2017-08-30.3c40fd3/.gitignore000066400000000000000000000003201333656753000206320ustar00rootroot00000000000000/sbcsdat.c /*.o /ipbt /build.log /build.out /ipbt.1 /ipbt.html /configure /Makefile.in /Makefile /autom4te.cache /aclocal.m4 /depcomp /.deps /config.status /config.log /stamp-h1 /missing /install-sh /compile its-playback-time-0.2017-08-30.3c40fd3/Buildscr000066400000000000000000000021141333656753000203370ustar00rootroot00000000000000# -*- sh -*- # Build script for IPBT. module ipbt set Version $(!builddate).$(vcsid) in ipbt do sed '/AC_INIT/s/6.66/$(Version)/' configure.ac > tmp.ac in ipbt do mv tmp.ac configure.ac in ipbt do ./mkauto.sh in ipbt do rm -rf autom4te.cache # use perl to avoid inconsistent behaviour of echo '\v' in ipbt do perl -e 'print "\n\\versionid ipbt version $$ARGV[0]\n"' $(Version) >> ipbt.but # Build the documentation. This also tests the automake setup to # ensure configure actually works; but we use a self-delegation (i.e. # do all of this in a throwaway copy of the build directory) to avoid # accidentally shipping any outputs of configure. Instead we return # only the actual man page from the delegation environment. delegate - in ipbt do ./configure in ipbt do make doc return ipbt/*.1 return ipbt/*.html enddelegate # Build a source archive with the right name. in . do ln -s ipbt ipbt-$(Version) in . do tar chzvf ipbt-$(Version).tar.gz ipbt-$(Version) # Deliver the source archive. deliver ipbt-$(Version).tar.gz $@ # And deliver the HTML man page. deliver ipbt/ipbt.html $@ its-playback-time-0.2017-08-30.3c40fd3/LICENCE000066400000000000000000000030321333656753000176320ustar00rootroot00000000000000This directory contains IPBT, an application derived from the PuTTY source base by Simon Tatham. | PuTTY is copyright 1997-2017 Simon Tatham. | | Portions copyright Robert de Bath, Joris van Rantwijk, Delian | Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, | Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus | Kuhn, and CORE SDI S.A. Those portions of IPBT which are not part of PuTTY are copyright 2005-2007 Simon Tatham. The same licence terms apply to them as to PuTTY: | Permission is hereby granted, free of charge, to any person | obtaining a copy of this software and associated documentation files | (the "Software"), to deal in the Software without restriction, | including without limitation the rights to use, copy, modify, merge, | publish, distribute, sublicense, and/or sell copies of the Software, | and to permit persons to whom the Software is furnished to do so, | subject to the following conditions: | | The above copyright notice and this permission notice shall be | included in all copies or substantial portions of the Software. | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. its-playback-time-0.2017-08-30.3c40fd3/Makefile.am000066400000000000000000000023101333656753000206770ustar00rootroot00000000000000bin_PROGRAMS = ipbt ipbt_SOURCES = be_none.c fromucs.c ipbt.c ldiscucs.c localenc.c macenc.c \ mimeenc.c minibidi.c misc.c noprint.c notiming.c sbcs.c sbcsdat.c \ settings.c slookup.c terminal.c time.c toucs.c tree234.c utf8.c \ uxmisc.c uxucs.c wcwidth.c xenc.c conf.c uxnogtk.c nogss.c ipbt_LDADD = $(LIBOBJS) $(CURSES_LIB) $(CURSES_LIBS) ipbt_CPPFLAGS = $(CURSES_CFLAGS) man1_MANS = ipbt.1 BUILT_SOURCES = sbcsdat.c sbcsdat.c: sbcsgen.pl sbcs.dat perl $(srcdir)/sbcsgen.pl $(srcdir)/sbcs.dat # If Halibut is available to rebuild the man pages from their .but # source, then man pages are treated as derived files in the obvious # way, and deleted by 'make clean'; we also build (and clean) the HTML # equivalents. If Halibut is not available (the typical case if # someone has downloaded the source archive and rerun mkauto.sh), the # man pages are treated as source files by this makefile. if HAVE_HALIBUT BUILT_MANS = $(man1_MANS) HTMLFILES = ipbt.html CLEANFILES = $(BUILT_MANS) $(HTMLFILES) $(BUILT_SOURCES) .SUFFIXES = .but .1 .html .but.1: halibut --man=$@ $< .but.html: halibut --html=$@ $< doc: $(BUILT_MANS) $(HTMLFILES) all-local: $(HTMLFILES) else CLEANFILES = $(BUILT_SOURCES) endif its-playback-time-0.2017-08-30.3c40fd3/README000066400000000000000000000074361333656753000175410ustar00rootroot00000000000000This directory contains `ipbt', a high-tech player for `ttyrec' files. `ttyrec' is a program similar to script(1): it records all the terminal output during a Unix terminal session. Unlike script, it writes timestamps into the output file as well as raw output data, so that the session can be played back at the original speed. It is commonly used for recording games of NetHack. The ttyrec recorder program can be found at http://namazu.org/~satoru/ttyrec/index.html.en Conventional ttyrec players are little more than a loop interspersing sleep(2) with write(2); it's easy to pause and fast-forward, and a particularly advanced player might be able to rewind by noticing screen clears and using them as marker points (since replay can be restarted reasonably safely from a screen clear), but precision single-step backward and forward motion is entirely beyond such an application. ipbt works by embedding a terminal emulator: it is essentially a derivative work of the PuTTY code base. The input file is read in full and run through the internal terminal emulator, and the resulting screen states are saved in an internal movie array in such a way that any screen state (`frame') can be retrieved efficiently. Once this is done, the player application simply displays a sequence of frames using libncurses; the user can perform the usual pause and speed adjustment operations, but can also jump to an arbitrary frame number and move backwards as well as forwards. The downside is that ipbt takes time to read the entire input file before starting. I've been testing it on an 8Mb ttyrec of a NetHack ascension; on a 1GHz Pentium III it takes about a minute to read the file. After that setup phase, however, playback is efficient. Current status of ipbt: * Basically works. * Amenities such as online help and proper documentation are not available yet. * Dependent on libncurses; uses the use_default_colors() extension function, which means it probably won't compile on other curses implementations without a bit of autoconf work. * Makefile is rudimentary at best. Porting work required. See the TODO file for a full list of desirable features. Proper documentation has yet to be written (as mentioned above), but here's a quick list of the available keystrokes during playback: * `q' (or `Q') quits ipbt. * Space moves on by a frame. Prefix it by a number to move on by multiple frames at a time. * `b' moves back by a frame. Prefix it by a number to move backwards by multiple frames at a time. * `o' toggles the on-screen display, which shows the current status of ipbt. * `L' toggles logarithmic time compression (designed to have little effect on small delays but massively compress long ones). * Typing a number followed by `x' causes play to proceed at that many times normal speed (e.g. `3x' means play at triple speed). Typing just `x' is equivalent to `1x': it restores normal speed. * `X' is like `x', but slows down rather than speeding up: `2X' means half speed and `3X' means one third speed. `X' on its own restores normal speed, just like `x'. * `g' jumps to the beginning of the animation. Prefixing a number jumps to a particular frame. * `G' jumps to the end of the animation. Prefixing a number jumps to that many frames from the end. * `p' (and also `s', for historical reasons) toggles pause mode. Pause mode is initially enabled. * '/' searches forward for a frame containing the given text. * '?' and `\' both search backward for a frame containing the given text. * `n' repeats the previous search: searches for the same text in the same direction (i.e. `go to next match'). * `N' repeats the previous search in reverse: searches for the same text in the opposite direction (i.e. `go to _previous_ match'). Oh, and `ipbt' stands for `It's Play-Back Time'. its-playback-time-0.2017-08-30.3c40fd3/TODO000066400000000000000000000153021333656753000173400ustar00rootroot00000000000000TODO/wishlist file for IPBT =========================== Things I'd definitely like to see done: - [RESUME] Make it easy to resume playback of the same set of ttyrecs at another time, by means of printing out on exit the actual command line which would load the same files and resume the playback at the same place. (Perhaps command-line options to restore the same speed settings would improve functionality in this area and be generally useful too. Also we should output appropriate width and height options. And probably Bourne-shell- quote the filenames in the output command line. And supply enough -N/-T options. Generally a pain, in fact, but useful.) - [HELP] In the player, the user should be able to press `h' or `?' (probably both) for a help screen. Currently the available keypresses are only documented in README. - [MAN] Man page. - [UXPORT] Unix portability work. There ought to be an autoconf framework, and any dependencies on ncurses rather than vanilla curses (use_default_colors() in particular) ought to be hidden behind autoconf-derived ifdefs. - [OUTSIZE] Make intelligent use of the size of the output screen. If you play an 80x24 animation on an 80x25 screen, for example, there would be space to put a permanent OSD at the bottom rather than being dependent on the current intrusive OSD. Conversely, if you display a large animation on a small screen then it might be nice to permit the user to scroll around the display area. While we're here, we could usefully detect screen resizes in mid-replay and do something useful with them (but see [UXPORT] - this has to be done differently on vanilla curses which doesn't support ncurses's incredibly useful KEY_RESIZE). Things I haven't quite decided whether I want yet: - [GAP] Currently, if you specify more than one ttyrec file on the input command line, ttyrec assumes a one-second delay between the final frame of one and the initial frame of the next. This delay could easily be made configurable using a command-line option. I'm not sure whether this is actually worth bothering with. - [MMAP] It might be an interesting idea to optionally be able to store the internal movie data in a disk file, probably just by using mmap(2) instead of malloc(3). This would be convenient if you planned to watch the same movie in multiple sittings: you'd only have to go through IPBT's lengthy setup phase once. Depends on [CLOPT]. - [NHTURN] When reading a NetHack replay in particular, the program could usefully spot the turn counter on the status line and track it throughout the replay, which would permit the user to jump straight to a particular turn number if you knew the turn number where something interesting had happened. However, I'm uncertain as to whether this is a sensible feature to add because it's rather NH-specific: it would set a precedent that adding specific support for everyone's favourite application was a sensible thing to do. Perhaps better to leave it as it currently is: you can jump to an arbitrary ttyrec frame by its frame number, and if you need to track down a particular NetHack turn number you can binary-search the available frames. An approach feasible with existing features is just to search for `T:12345', although this takes linear time rather than the log time one could achieve by making use of the known monotonicity. - [SAVEFILE] Save the movie data to a file once processed and sorted. If one is planning to watch a very long replay in many stages, this would allow the tedious preparation stage to be done only once. Ties in to [RESUME], if implemented: the resume command line would want to specify the output movie file rather than the inputs. Downside is that there would be confusing portability concerns about the movie file itself; carefully making sure of its cross-platform portability would slow down lookups in it, but conversely I predict that if I _don't_ make it a well-defined portable format then Murphy's Law says people will probably try to use it as an interchange format and complain when it doesn't work... + well, a simple answer to this is to start the file off with a dummy movie record containing known data, make sure we can load it in correctly, and give a reasonably informative error message if not. Longer-range ideas which would involve a lot of work: - [ABSTRACTION] Abstract the core technology away from the Curses player UI, to permit non-Curses output drivers. I envisage a source module which exports functions along the lines of read_ttyrec_file(), sort_movie_array() and the current int_for_frame(). Possibly also move some of the player logic into another output-independent source module, so that things like the logarithmic time compression and the rather carefully tuned behaviour of the `b' key don't have to be re-implemented between platforms. - [WINPORT] A Windows GUI player, displaying in a PuTTY-like terminal window, would be nice. The terminal output code might be able to steal a lot from PuTTY again (in particular, it would be nice to take _enough_ code from PuTTY that copy-and-pasting from a replay window was still supported). This would involve considerable GUI work: Windows users would probably expect friendly drop-down File/Edit menus and a GUI toolbar showing the contents of the OSD, and would be a bit miffed to find an exact Windows clone of the current Curses keyboard-and-command-line interface. Depends on [ABSTRACTION]. - [SEARCHREFINEMENTS] It might be useful to remember at what _point on the screen_ the search text was found in previous frames, so that we can avoid finding it again in that precise place. In other words, we'd now be searching for frames in which the search text has _just appeared_ rather than simply being present. Helpful if, for example, you search for something which appears in a Nethack player's inventory, because otherwise the search will stop at _every_ frame during a lengthy inventory manipulation. However, this is significantly more complex than the current simple searching, so I haven't done it yet. - [BGLOAD] Have a mode in which the player starts up instantly, and the rest of the file(s) are read in the background while processing keypresses. There would still be an unavoidable delay if the user wanted to go straight to the end of the file or jump to a frame that wasn't loaded yet, but it might be an improvement on the current non-negotiable delay. On the other hand, the complexity of background-loading the file might be prohibitive: it might require multithreading to do properly, for example. its-playback-time-0.2017-08-30.3c40fd3/be_none.c000066400000000000000000000002571333656753000204240ustar00rootroot00000000000000/* * Linking module for programs that do not support selection of backend * (such as pterm). */ #include #include "putty.h" Backend *backends[] = { NULL }; its-playback-time-0.2017-08-30.3c40fd3/charset.h000066400000000000000000000105171333656753000204550ustar00rootroot00000000000000/* * charset.h - header file for general character set conversion * routines. */ #ifndef charset_charset_h #define charset_charset_h #include /* * Enumeration that lists all the multibyte or single-byte * character sets known to this library. */ typedef enum { CS_NONE, /* used for reporting errors, etc */ CS_ISO8859_1, CS_ISO8859_1_X11, /* X font encoding with VT100 glyphs */ CS_ISO8859_2, CS_ISO8859_3, CS_ISO8859_4, CS_ISO8859_5, CS_ISO8859_6, CS_ISO8859_7, CS_ISO8859_8, CS_ISO8859_9, CS_ISO8859_10, CS_ISO8859_11, CS_ISO8859_13, CS_ISO8859_14, CS_ISO8859_15, CS_ISO8859_16, CS_CP437, CS_CP850, CS_CP852, CS_CP866, CS_CP1250, CS_CP1251, CS_CP1252, CS_CP1253, CS_CP1254, CS_CP1255, CS_CP1256, CS_CP1257, CS_CP1258, CS_KOI8_R, CS_KOI8_U, CS_MAC_ROMAN, CS_MAC_TURKISH, CS_MAC_CROATIAN, CS_MAC_ICELAND, CS_MAC_ROMANIAN, CS_MAC_GREEK, CS_MAC_CYRILLIC, CS_MAC_THAI, CS_MAC_CENTEURO, CS_MAC_SYMBOL, CS_MAC_DINGBATS, CS_MAC_ROMAN_OLD, CS_MAC_CROATIAN_OLD, CS_MAC_ICELAND_OLD, CS_MAC_ROMANIAN_OLD, CS_MAC_GREEK_OLD, CS_MAC_CYRILLIC_OLD, CS_MAC_UKRAINE, CS_MAC_VT100, CS_MAC_VT100_OLD, CS_VISCII, CS_HP_ROMAN8, CS_DEC_MCS, CS_UTF8 } charset_t; typedef struct { unsigned long s0; } charset_state; /* * Routine to convert a MB/SB character set to Unicode. * * This routine accepts some number of bytes, updates a state * variable, and outputs some number of Unicode characters. There * are no guarantees. You can't even guarantee that at most one * Unicode character will be output per byte you feed in; for * example, suppose you're reading UTF-8, you've seen E1 80, and * then you suddenly see FE. Now you need to output _two_ error * characters - one for the incomplete sequence E1 80, and one for * the completely invalid UTF-8 byte FE. * * Returns the number of wide characters output; will never output * more than the size of the buffer (as specified on input). * Advances the `input' pointer and decrements `inlen', to indicate * how far along the input string it got. * * The sequence of `errlen' wide characters pointed to by `errstr' * will be used to indicate a conversion error. If `errstr' is * NULL, `errlen' will be ignored, and the library will choose * something sensible to do on its own. For Unicode, this will be * U+FFFD (REPLACEMENT CHARACTER). */ int charset_to_unicode(const char **input, int *inlen, wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen); /* * Routine to convert Unicode to an MB/SB character set. * * This routine accepts some number of Unicode characters, updates * a state variable, and outputs some number of bytes. * * Returns the number of bytes characters output; will never output * more than the size of the buffer (as specified on input), and * will never output a partial MB character. Advances the `input' * pointer and decrements `inlen', to indicate how far along the * input string it got. * * The sequence of `errlen' characters pointed to by `errstr' will * be used to indicate a conversion error. If `errstr' is NULL, * `errlen' will be ignored, and the library will choose something * sensible to do on its own (which will vary depending on the * output charset). */ int charset_from_unicode(const wchar_t **input, int *inlen, char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen); /* * Convert X11 encoding names to and from our charset identifiers. */ const char *charset_to_xenc(int charset); int charset_from_xenc(const char *name); /* * Convert MIME encoding names to and from our charset identifiers. */ const char *charset_to_mimeenc(int charset); int charset_from_mimeenc(const char *name); /* * Convert our own encoding names to and from our charset * identifiers. */ const char *charset_to_localenc(int charset); int charset_from_localenc(const char *name); int charset_localenc_nth(int n); /* * Convert Mac OS script/region/font to our charset identifiers. */ int charset_from_macenc(int script, int region, int sysvers, const char *fontname); #endif /* charset_charset_h */ its-playback-time-0.2017-08-30.3c40fd3/conf.c000066400000000000000000000373701333656753000177520ustar00rootroot00000000000000/* * conf.c: implementation of the internal storage format used for * the configuration of a PuTTY session. */ #include #include #include #include "tree234.h" #include "putty.h" /* * Enumeration of types used in keys and values. */ typedef enum { TYPE_NONE, 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 { 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_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; } 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_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); } int conf_serialised_size(Conf *conf) { int i; struct conf_entry *entry; int size = 0; for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { size += 4; /* primary key */ switch (subkeytypes[entry->key.primary]) { case TYPE_INT: size += 4; break; case TYPE_STR: size += 1 + strlen(entry->key.secondary.s); break; } switch (valuetypes[entry->key.primary]) { case TYPE_INT: size += 4; break; case TYPE_STR: size += 1 + strlen(entry->value.u.stringval); break; case TYPE_FILENAME: size += filename_serialise(entry->value.u.fileval, NULL); break; case TYPE_FONT: size += fontspec_serialise(entry->value.u.fontval, NULL); break; } } size += 4; /* terminator value */ return size; } void conf_serialise(Conf *conf, void *vdata) { unsigned char *data = (unsigned char *)vdata; int i, len; struct conf_entry *entry; for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { PUT_32BIT_MSB_FIRST(data, entry->key.primary); data += 4; switch (subkeytypes[entry->key.primary]) { case TYPE_INT: PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i); data += 4; break; case TYPE_STR: len = strlen(entry->key.secondary.s); memcpy(data, entry->key.secondary.s, len); data += len; *data++ = 0; break; } switch (valuetypes[entry->key.primary]) { case TYPE_INT: PUT_32BIT_MSB_FIRST(data, entry->value.u.intval); data += 4; break; case TYPE_STR: len = strlen(entry->value.u.stringval); memcpy(data, entry->value.u.stringval, len); data += len; *data++ = 0; break; case TYPE_FILENAME: data += filename_serialise(entry->value.u.fileval, data); break; case TYPE_FONT: data += fontspec_serialise(entry->value.u.fontval, data); break; } } PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU); } int conf_deserialise(Conf *conf, void *vdata, int maxsize) { unsigned char *data = (unsigned char *)vdata; unsigned char *start = data; struct conf_entry *entry; unsigned primary; int used; unsigned char *zero; while (maxsize >= 4) { primary = GET_32BIT_MSB_FIRST(data); data += 4, maxsize -= 4; if (primary >= N_CONFIG_OPTIONS) break; entry = snew(struct conf_entry); entry->key.primary = primary; switch (subkeytypes[entry->key.primary]) { case TYPE_INT: if (maxsize < 4) { sfree(entry); goto done; } entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data)); data += 4, maxsize -= 4; break; case TYPE_STR: zero = memchr(data, 0, maxsize); if (!zero) { sfree(entry); goto done; } entry->key.secondary.s = dupstr((char *)data); maxsize -= (zero + 1 - data); data = zero + 1; break; } switch (valuetypes[entry->key.primary]) { case TYPE_INT: if (maxsize < 4) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data)); data += 4, maxsize -= 4; break; case TYPE_STR: zero = memchr(data, 0, maxsize); if (!zero) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } entry->value.u.stringval = dupstr((char *)data); maxsize -= (zero + 1 - data); data = zero + 1; break; case TYPE_FILENAME: entry->value.u.fileval = filename_deserialise(data, maxsize, &used); if (!entry->value.u.fileval) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } data += used; maxsize -= used; break; case TYPE_FONT: entry->value.u.fontval = fontspec_deserialise(data, maxsize, &used); if (!entry->value.u.fontval) { if (subkeytypes[entry->key.primary] == TYPE_STR) sfree(entry->key.secondary.s); sfree(entry); goto done; } data += used; maxsize -= used; break; } conf_insert(conf, entry); } done: return (int)(data - start); } its-playback-time-0.2017-08-30.3c40fd3/configure.ac000066400000000000000000000010361333656753000211350ustar00rootroot00000000000000# autoconf input for ipbt. AC_INIT([ipbt], [6.66], [anakin@pobox.com]) AC_CONFIG_SRCDIR([ipbt.c]) AM_INIT_AUTOMAKE(foreign) # Checks for programs. AC_PROG_CC AC_PROG_CC_C99 AC_PROG_INSTALL AC_CHECK_PROG([HALIBUT],[halibut],[yes],[no]) AM_CONDITIONAL([HAVE_HALIBUT],[test "x$HALIBUT" = "xyes"]) AC_SEARCH_LIBS(log, m) AX_WITH_CURSES if test "x$ax_cv_ncursesw" != xyes; then AC_MSG_WARN([the -U option may not work correctly without libncursesw]) fi AC_SUBST(CURSES_CFLAGS) AC_SUBST(CURSES_LIBS) AC_CONFIG_FILES([Makefile]) AC_OUTPUT its-playback-time-0.2017-08-30.3c40fd3/debian/000077500000000000000000000000001333656753000200715ustar00rootroot00000000000000its-playback-time-0.2017-08-30.3c40fd3/debian/changelog000066400000000000000000000001221333656753000217360ustar00rootroot00000000000000ipbt (0.2017-08-30.3c40fd3-1) unstable; urgency=medium * Initial upload. -- its-playback-time-0.2017-08-30.3c40fd3/debian/compat000066400000000000000000000000031333656753000212700ustar00rootroot0000000000000010 its-playback-time-0.2017-08-30.3c40fd3/debian/control000066400000000000000000000007021333656753000214730ustar00rootroot00000000000000Source: ipbt Section: misc Priority: optional Maintainer: Ian Jackson Standards-Version: 4.2.0.1 Build-Depends: debhelper (>= 9) Package: ipbt Depends: ${misc:Depends}, ${shlibs:Depends} Suggests: ttyrec Architecture: any Description: ttyrec time-travelling playback tool ttyrec player which lets you go backwards and forwards through the recording. (a ttyrec is a timestamped recording of a terminal session.) its-playback-time-0.2017-08-30.3c40fd3/debian/rules000077500000000000000000000000361333656753000211500ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ its-playback-time-0.2017-08-30.3c40fd3/enum.c000066400000000000000000000012551333656753000177620ustar00rootroot00000000000000/* * enum.c - enumerate all charsets defined by the library. * * This file maintains a list of every other source file which * contains ENUM_CHARSET definitions. It #includes each one with * ENUM_CHARSETS defined, which causes those source files to do * nothing at all except call the ENUM_CHARSET macro on each * charset they define. * * This file in turn is included from various other places, with * the ENUM_CHARSET macro defined to various different things. This * allows us to have multiple implementations of the master charset * lookup table (a static one and a dynamic one). */ #define ENUM_CHARSETS #include "sbcsdat.c" #include "utf8.c" #undef ENUM_CHARSETS its-playback-time-0.2017-08-30.3c40fd3/fromucs.c000066400000000000000000000037771333656753000205070ustar00rootroot00000000000000/* * fromucs.c - convert Unicode to other character sets. */ #include "charset.h" #include "internal.h" struct charset_emit_param { char *output; int outlen; const char *errstr; int errlen; int stopped; }; static void charset_emit(void *ctx, long int output) { struct charset_emit_param *param = (struct charset_emit_param *)ctx; char outval; char const *p; int outlen; if (output == ERROR) { p = param->errstr; outlen = param->errlen; } else { outval = output; p = &outval; outlen = 1; } if (param->outlen >= outlen) { while (outlen > 0) { *param->output++ = *p++; param->outlen--; outlen--; } } else { param->stopped = 1; } } int charset_from_unicode(const wchar_t **input, int *inlen, char *output, int outlen, int charset, charset_state *state, const char *errstr, int errlen) { charset_spec const *spec = charset_find_spec(charset); charset_state localstate; struct charset_emit_param param; param.output = output; param.outlen = outlen; param.stopped = 0; /* * charset_emit will expect a valid errstr. */ if (!errstr) { /* *shrug* this is good enough, and consistent across all SBCS... */ param.errstr = "."; param.errlen = 1; } param.errstr = errstr; param.errlen = errlen; if (!state) { localstate.s0 = 0; } else { localstate = *state; /* structure copy */ } state = &localstate; while (*inlen > 0) { int lenbefore = param.output - output; spec->write(spec, **input, &localstate, charset_emit, ¶m); if (param.stopped) { /* * The emit function has _tried_ to output some * characters, but ran up against the end of the * buffer. Leave immediately, and return what happened * _before_ attempting to process this character. */ return lenbefore; } if (state) *state = localstate; /* structure copy */ (*input)++; (*inlen)--; } return param.output - output; } its-playback-time-0.2017-08-30.3c40fd3/internal.h000066400000000000000000000060131333656753000206340ustar00rootroot00000000000000/* * internal.h - internal header stuff for the charset library. */ #ifndef charset_internal_h #define charset_internal_h /* This invariably comes in handy */ #define lenof(x) ( sizeof((x)) / sizeof(*(x)) ) /* This is an invalid Unicode value used to indicate an error. */ #define ERROR 0xFFFFL /* Unicode value representing error */ typedef struct charset_spec charset_spec; typedef struct sbcs_data sbcs_data; struct charset_spec { int charset; /* numeric identifier */ /* * A function to read the character set and output Unicode * characters. The `emit' function expects to get Unicode chars * passed to it; it should be sent ERROR for any encoding error * on the input. */ void (*read)(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); /* * A function to read Unicode characters and output in this * character set. The `emit' function expects to get byte * values passed to it; it should be sent ERROR for any * non-representable characters on the input. */ void (*write)(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); void const *data; }; /* * This is the format of `data' used by the SBCS read and write * functions; so it's the format used in all SBCS definitions. */ struct sbcs_data { /* * This is a simple mapping table converting each SBCS position * to a Unicode code point. Some positions may contain ERROR, * indicating that that byte value is not defined in the SBCS * in question and its occurrence in input is an error. */ unsigned long sbcs2ucs[256]; /* * This lookup table is used to convert Unicode back to the * SBCS. It consists of the valid byte values in the SBCS, * sorted in order of their Unicode translation. So given a * Unicode value U, you can do a binary search on this table * using the above table as a lookup: when testing the Xth * position in this table, you branch according to whether * sbcs2ucs[ucs2sbcs[X]] is less than, greater than, or equal * to U. * * Note that since there may be fewer than 256 valid byte * values in a particular SBCS, we must supply the length of * this table as well as the contents. */ unsigned char ucs2sbcs[256]; int nvalid; }; /* * Prototypes for internal library functions. */ charset_spec const *charset_find_spec(int charset); void read_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); void write_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); /* * Placate compiler warning about unused parameters, of which we * expect to have some in this library. */ #define UNUSEDARG(x) ( (x) = (x) ) #endif /* charset_internal_h */ its-playback-time-0.2017-08-30.3c40fd3/ipbt.but000066400000000000000000000204321333656753000203220ustar00rootroot00000000000000\cfg{man-identity}{ipbt}{1}{2007-04-19}{Simon Tatham}{Simon Tatham} \title Man page for \c{ipbt} \U NAME \c{ipbt} - play back tty recording files with random access \U SYNOPSIS \c ipbt [ options ] file [ file ... ] \e bbbb iiiiiii iiii iiii \U DESCRIPTION \c{ipbt} is a \c{curses}-based playback utility for recordings of terminal sessions in either \c{ttyrec} or \c{nh-recorder} format. \c{ttyrec} and \c{nh-recorder} are both programs which record the output of a terminal session, interspersed with timestamps. This allows the session to be replayed on another terminal at the original speed, so that the replaying terminal screen looks identical to the original, provided the recording and replaying terminals are sufficiently compatible. \c{ttyrec} and \c{nh-recorder} produce differently formatted files, but the principle is very similar. To replay a tty recording in a forward direction, a program such as \c{ttyplay} will simply write the contents of the file to the output terminal, stopping to wait for the appropriate period every time it encounters a timestamp. Speeding up, slowing down and pausing the replay are easy. Rewinding, however, is almost impossible with this replay architecture. \c{ipbt} solves this by running the entire tty recording through an internal terminal emulator and storing the resulting screen contents in a manner that permits random access. Once this loading process is complete, \c{ipbt} can play forwards and backwards, or jump to an arbitrary point in the recording, with equal ease. This flexibility comes with two disadvantages: \b \c{ipbt} takes noticeable time to load a tty recording in the first place. At the time of writing this, it typically takes a few seconds per megabyte of input. Simpler programs such as \c{ttyplay} can begin playing instantly. \b Since \c{ipbt} does its terminal emulation internally, it can support only one type of terminal, namely that provided by PuTTY (of which \c{ipbt} is a derived work). Any tty recording which is not compatible with that terminal type will not come out looking right. Simpler programs such as \c{ttyplay} rely on the terminal they are running in to interpret the terminal control sequences, so they can be made to support any terminal type simply by running them in that type of terminal. If either of these is a serious problem, you probably don't want to be using \c{ipbt}. \U OPTIONS By default, \c{ipbt} interprets tty recordings as if they were intended to be replayed on an 80\u00D7{x}24 screen. You can alter this using the following options: \dt \c{-w} \e{width} \dd Set the width of the emulated terminal to \e{width} columns. \dt \c{-h} \e{height} \dd Set the height of the emulated terminal to \e{height} rows. \dt \c{-u} \dd Set the width and height of the emulated terminal to be the same as that of the real terminal in which you are running \c{ipbt}. By default, \c{ipbt} will attempt to analyse the input files and automatically guess whether they are in \c{ttyrec} or \c{nh-recorder} format. On rare occasions this automatic guessing may fail: for example, a \c{ttyrec} made on a system whose clock occasionally jumps backwards may contain timestamps in non-increasing order, in which case \c{ipbt} may consider the file not to be a valid \c{ttyrec} and either try to interpret it as \c{nh-recorder} format or (more likely) refuse to load it at all. If this happens, you will need to explicitly tell \c{ipbt} what type of file it is dealing with using the following options: \dt \c{-T} \dd Specify that all files appearing on the command line after this point (until a subsequent format-specifying option) should be assumed to be in \c{ttyrec} format. \dt \c{-N} \dd Specify that all files appearing on the command line after this point (until a subsequent format-specifying option) should be assumed to be in \c{nh-recorder} format. \c{ipbt} also provides the following miscellaneous options: \dt \c{-f} \e{frame} \dd After loading the tty recordings, jump to the specified frame number. Frame numbers are allocated starting from zero, as if all the input files were concatenated; so if you provided two input files, one with 100 frames and one with 50, then frame numbers 0-99 would indicate positions in the first file and frame numbers 100-149 would indicate positions in the second. \dt \c{-P} \dd After loading the tty recordings, terminate immediately. This option is unlikely to be very useful at present, although it might have specialist uses for people wanting to measure \c{ipbt}'s loading speed or test its automatic format detection. \dt \c{-U} \dd Treat the input terminal data as being encoded in UTF-8. \lcont{ If \c{ipbt} has been compiled against the \c{ncursesw} library and is running in a UTF-8 terminal, it will reproduce the Unicode characters in the input file as faithfully as it can. If not, it will at least try to reproduce as many of the characters as it can on whatever terminal it does have. } \U PLAYING Once \c{ipbt} has loaded a set of tty recordings, it enters a full-screen playing mode. This section describes the keypresses which control the player. To terminate the player, press \cq{q}, or Control-C. Press \cq{p} or \cq{s} to toggle between pause and play mode. The player starts up in pause mode, so you may well want to press this immediately to begin playing. To go back by one frame, press \cq{b} or \cq{<}. To go back by many frames, type a number followed by \cq{b} or \cq{<}. To go forward by one frame, press the space bar or \cq{>}. To go back by many frames, type a number followed by the space bar or \cq{>}. To jump to a particular frame number, type the number followed by \cq{g}. Pressing \cq{g} on its own will jump to the start of the recording. Pressing \e{capital} \cq{G} will jump to the \e{end} of the recording. If you type a number followed by \cq{G}, it will jump to that many frames \e{before} the end. To play back the recording at higher speed, type a number followed by \cq{x}. For example, typing \cq{3x} will set the player to three times normal speed. (This will not automatically bring you out of pause mode; you still need to press \cq{p} or \cq{s} for that.) To play back at a \e{lower} speed, type a number followed by \e{capital} \cq{X}; for example, \cq{3X} will set the player to one-third normal speed. To return to normal speed, you can type \cq{1x} or \cq{1X}, or just \cq{x} or \cq{X}. If you press \cq{l}, the player will toggle logarithmic time compression. This is a mode in which the delay between frames is scaled in a non-linear fashion: small delays are changed very little, but extremely large delays are made significantly less large, so that an hour between frames in the original recording becomes only about eight seconds, a day becomes eleven, and even a \e{year} between frames would become around seventeen seconds. This is helpful if the person who made the recording left the terminal for a long time in the middle of their session. Logarithmic mode can be used in conjunction with the \cq{x}/\cq{X} scaling feature. If you press \cq{o}, the player will show an on-screen display near the top left of the screen, showing the current frame number and playback settings. Press \cq{o} again to make the display go away. You can also search the recording for a frame in which a particular piece of text appears on the screen. Press \cq{/} to search forwards from the current position, or \cq{\\} or \cq{?} to search backwards. Each of these commands will cause \cw{ipbt} to prompt for a piece of text to find, and will then search for a frame that contains that text. Press \cq{n} to search for the same piece of text again in the same direction. \U BUGS \c{ipbt}'s internal terminal emulation is that of PuTTY, but its display of the resulting data is rather more simplistic. Some output features supported by PuTTY are therefore not supported by \c{ipbt} For example, \c{xterm} 256-colour mode is not supported at all. Also, \c{ipbt}'s Unicode support does not currently handle various Unicode complications such as characters outside the Basic Multilingual Plane, double-width CJK characters, right-to-left scripts, or combining characters. (Patches or help from people who know how to persuade \c{ncursesw} to do any or all of those things would be welcome!) \U LICENCE \cw{ipbt} is free software, distributed under the MIT licence. Type \cw{ipbt --licence} to see the full licence text. its-playback-time-0.2017-08-30.3c40fd3/ipbt.c000066400000000000000000001402271333656753000177570ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #if !HAVE_NCURSESW #include #endif #define _XOPEN_SOURCE_EXTENDED #if defined HAVE_NCURSESW_CURSES_H # define NCURSES_WIDECHAR 1 # include #elif defined HAVE_NCURSESW_H # define NCURSES_WIDECHAR 1 # include #elif defined HAVE_NCURSES_CURSES_H # include #elif defined HAVE_NCURSES_H # include #elif defined HAVE_CURSES_H # include #else # error "SysV or X/Open-compatible Curses header file required" #endif #include #include #include #include #include #include "putty.h" #include "terminal.h" #include "misc.h" const char usagemsg[] = "usage: ipbt [ [ -w width ] [ -h height ] | -u ] [ -T | -N ]\n" " [ -f frame ] [ -P ] file [file...]\n" "where: -w width specify width of emulated terminal screen (default 80)\n" " -h height specify height of emulated terminal screen (default 24)\n" " -u use size of real terminal for emulated terminal\n" " -U assume input terminal data to be encoded in UTF-8\n" " -T assume input files to be ttyrec format\n" " -N assume input files to be nh-recorder format\n" " -f frame start viewing at a particular frame number\n" " -P terminate immediately after reading input\n" " -A automatically start playing after reading input\n" " -L loop back to the start when playback reaches the end\n" " also: ipbt --version report version number\n" " ipbt --help display this help text\n" " ipbt --licence display the (MIT) licence text\n" ; void usage(void) { fputs(usagemsg, stdout); } const char licencemsg[] = "IPBT is an application derived from the PuTTY source base by Simon\n" "Tatham.\n" "\n" "The PuTTY copyright notice is reproduced below. The long list of\n" "copyright holders are not all actually relevant, since some of them\n" "contributed code to PuTTY which is not included in IPBT.\n" "\n" "| PuTTY is copyright 1997-2007 Simon Tatham.\n" "|\n" "| Portions copyright Robert de Bath, Joris van Rantwijk, Delian\n" "| Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,\n" "| Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus\n" "| Kuhn, and CORE SDI S.A.\n" "\n" "Those portions of IPBT which are not part of PuTTY are copyright\n" "2005-2007 Simon Tatham. The same licence terms apply to them as to\n" "PuTTY:\n" "\n" "| Permission is hereby granted, free of charge, to any person\n" "| obtaining a copy of this software and associated documentation files\n" "| (the \"Software\"), to deal in the Software without restriction,\n" "| including without limitation the rights to use, copy, modify, merge,\n" "| publish, distribute, sublicense, and/or sell copies of the Software,\n" "| and to permit persons to whom the Software is furnished to do so,\n" "| subject to the following conditions:\n" "|\n" "| The above copyright notice and this permission notice shall be\n" "| included in all copies or substantial portions of the Software.\n" "|\n" "| THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n" "| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n" "| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n" "| NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE\n" "| FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\n" "| CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n" "| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" ; void licence(void) { fputs(licencemsg, stdout); } void version(void) { #ifdef PACKAGE_VERSION printf("ipbt version %s\n", PACKAGE_VERSION); #else printf("ipbt: unknown version\n"); #endif } int curses_active; /* * We're only _replaying_ terminal data, so we never need to send back * to a real application. Thus, ldisc_send and ldisc_echoedit_update * as called from terminal.c are stub functions. */ void ldisc_send(void *handle, const char *buf, int len, int interactive) {} void ldisc_echoedit_update(void *handle) {} void cleanup_exit(int code) { exit(code); } void fatalbox(const char *p, ...) { va_list ap; if (curses_active) endwin(); fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); cleanup_exit(1); } void modalfatalbox(const char *p, ...) { va_list ap; if (curses_active) endwin(); fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); cleanup_exit(1); } int char_width(Context ctx, int uc) { /* * I don't expect this to come up very often. */ return 1; } /* * All the terminal report functions, and many of the front end * terminal functions, are pointless stubs in this implementation. */ void set_iconic(void *frontend, int iconic) {} void move_window(void *frontend, int x, int y) {} void set_zorder(void *frontend, int top) {} void refresh_window(void *frontend) {} void set_zoomed(void *frontend, int zoomed) {} int is_iconic(void *frontend) { return 0; } void get_window_pos(void *frontend, int *x, int *y) { *x = *y = 0; } void get_window_pixels(void *frontend, int *x, int *y) { *x = *y = 0; } char *get_window_title(void *frontend, int icon) { return ""; } void set_title(void *frontend, char *title) {} void set_icon(void *frontend, char *title) {} void set_sbar(void *frontend, int total, int start, int page) {} void get_clip(void *frontend, wchar_t ** p, int *len) { *p = NULL; *len = 0; } void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect) {} void request_paste(void *frontend) {} void request_resize(void *frontend, int w, int h) {} void palette_reset(void *frontend) {} void palette_set(void *frontend, int n, int r, int g, int b) {} void set_raw_mouse_mode(void *frontend, int activate) {} void logflush(void *handle) {} void logtraffic(void *handle, unsigned char c, int logmode) {} void do_beep(void *frontend, int mode) {} /* * We don't save and load PuTTY configuration data, so these are * all stubs too. */ void *open_settings_w(const char *sessionname, char **errmsg) { return NULL; } void write_setting_s(void *handle, const char *key, const char *value) {} void write_setting_i(void *handle, const char *key, int value) {} void close_settings_w(void *handle) {} void *open_settings_r(const char *sessionname) { return NULL; } char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) { return NULL; } int read_setting_i(void *handle, const char *key, int defvalue) { return defvalue; } int read_setting_fontspec(void *handle, const char *name, FontSpec *result) { return 0; } int read_setting_filename(void *handle, const char *name, Filename *result) { return 0; } void write_setting_fontspec(void *handle, const char *name, FontSpec result) {} void write_setting_filename(void *handle, const char *name, Filename result) {} void close_settings_r(void *handle) {} void *enum_settings_start(void) { return NULL; } char *enum_settings_next(void *handle, char *buffer, int buflen) {return NULL;} FontSpec *platform_default_fontspec(const char *name) { FontSpec *ret = snew(FontSpec); ret->name = dupstr(""); return ret; } Filename *platform_default_filename(const char *name) { Filename *ret = snew(Filename); ret->path = dupstr(""); return ret; } char *platform_default_s(const char *name) { return NULL; } int platform_default_i(const char *name, int def) { return def; } void enum_settings_finish(void *handle) {} int default_port = -1, default_protocol = -1; /* * Other miscellaneous stubs. */ const int buildinfo_gtk_relevant = FALSE; const char commitid[] = "dummy"; void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx) {} #define FG 0x000F0000 #define BG 0x00F00000 #define FGBG 0x00FF0000 #define BOLD 0x01000000 #define UNDER 0x02000000 #define REV 0x04000000 #define BLINK 0x08000000 #define FGSHIFT 16 #define BGSHIFT 20 #define FGBGSHIFT FGSHIFT #define INVALID_SCREENVAL INT_MAX /* an invalid value for any screen[] entry */ #define NODEFAULT -1 #define TTYREC 0 #define NHRECORDER 1 #define NTYPES 2 static const char *const typenames[] = { "ttyrec", "nh-recorder" }; struct filename { char *name; int type; }; struct parray; struct inst { Terminal *term; struct unicode_data ucsdata; Conf *conf; int *screen, *oldscreen, w, h, screenlen; struct parray **parrays; int frames; unsigned long long movietime; struct filename *filenames; int nfiles, filesize; int cpairs[(FGBG >> FGSHIFT) + 1]; int pairsused, nines; int number, osd; int playing; int logmod; double speedmod; char *searchstr; int searchback; int unicode; }; int frontend_is_utf8(void *frontend) { /* At the time of writing this, nothing IPBT cares about will use * this query function, but just in case that changes in * future... */ struct inst *inst = (struct inst *)frontend; return inst->unicode; } /* * Our notional screen data structure is simply an array of 32-bit * integers: W*H screen positions, plus a magic one for the cursor * position. * * This rather simplistic and flat architecture is because a lot of * the time we won't be directly storing these. Instead, we'll be * storing a list of how each integer changed over time. Our * _movie_ data structure will be a collection of pseudo-`arrays', * one for each of the integers in our screen array, containing * elements of the form (frame number in which the integer changed, * what it changed to). This means that we can determine the value * of integer I at frame F by a binary search of the array. This * enables us to play back and forth through the entire movie with * arbitrary rewind. Hence the need to have the entire terminal * state encoded as an unstructured list of integers: if I had to * give separate treatment to the cursor position and any other * future enhancements such as line attributes, it would all get * more complicated. * * To prevent memory wastage by repeatedly reallocing several * actual arrays, we instead use the concept of a `pseudo-array', * which is structured much like an ext2fs file: initially the * array is a single block of memory in the obvious format, but * once it overflows that block we move to a two-layer structure * containing an index block with (frame, sub-block) records each * indexing a block of real array. When the index block overflows, * we move to a three-layer structure with a second-level index * block indexing the first-level ones, and so on. */ struct parray_block; struct parray_level0 { int frame; int data; }; struct parray_level1 { int frame; struct parray_block *subblock; }; #ifdef PARRAY_TEST #define PARRAY_L0COUNT 5 #define PARRAY_L1COUNT 3 #else #define PARRAY_BLKSIZE 16384 #define PARRAY_L0COUNT (PARRAY_BLKSIZE / sizeof(struct parray_level0)) #define PARRAY_L1COUNT (PARRAY_BLKSIZE / sizeof(struct parray_level1)) #endif struct parray { int toplevel; int items; int memusage; struct parray_block *root; }; struct parray_block { union { struct parray_level0 level0[PARRAY_L0COUNT]; struct parray_level1 level1[PARRAY_L1COUNT]; } u; }; struct parray *parray_new(void) { struct parray *pa = snew(struct parray); pa->toplevel = -1; pa->items = 0; pa->memusage = sizeof(struct parray); pa->root = NULL; return pa; } void parray_append(struct parray *pa, int frame, int data) { struct parray_block *pb, *pb2; int i, n, index, count; /* * Special case: the very first item. */ if (!pa->items) { pb = snew(struct parray_block); pa->memusage += sizeof(struct parray_block); for (i = 0; i < PARRAY_L0COUNT; i++) { pb->u.level0[i].frame = INT_MAX; pb->u.level0[i].data = 0; } pb->u.level0[0].frame = frame; pb->u.level0[0].data = data; pa->items++; pa->toplevel = 0; pa->root = pb; return; } /* * Figure out how many items are covered by a single block at * the parray's current top level. */ count = PARRAY_L0COUNT; for (i = 1; i <= pa->toplevel; i++) count *= PARRAY_L1COUNT; /* * If this is equal to the parray's current total item count, * we must create a new top-level block. */ assert(pa->items <= count); if (pa->items == count) { pb = snew(struct parray_block); pa->memusage += sizeof(struct parray_block); /* * pa->root->u.level0[0].frame and * pa->root->u.level1[0].frame overlap exactly (guaranteed * by the C standard), so we don't need to worry about * which one to access through. */ pb->u.level1[0].frame = pa->root->u.level1[0].frame; pb->u.level1[0].subblock = pa->root; pa->toplevel++; pa->root = pb; count *= PARRAY_L1COUNT; /* we've moved up a level */ } /* * Now work down the tree. At each level, create a new block * and descend to it if necessary, otherwise descend to the * last existing block if it's not completely full. */ pb = pa->root; index = pa->items; for (i = pa->toplevel; i-- > 0 ;) { count /= PARRAY_L1COUNT; n = index / count; assert(n < PARRAY_L1COUNT); index %= count; if (!index) { /* * Create a new empty block at the next level down. */ pb2 = snew(struct parray_block); pb->u.level1[n].frame = frame; pb->u.level1[n].subblock = pb2; } /* * Descend to the partially filled end block, whether or * not we just had to create it. */ pb = pb->u.level1[n].subblock; } /* * Now we're sitting on a level-0 block which is known to have * spare space. Add our entry. */ pb->u.level0[index].frame = frame; pb->u.level0[index].data = data; pa->items++; } int parray_search(struct parray *pa, int frame, int *index_out) { struct parray_block *pb; int count, total, i, n, top, bot, mid, index; assert(pa->root); assert(pa->items > 0); assert(frame >= pa->root->u.level1[0].frame); /* * Figure out how many items are covered by a single block at * the parray's current top level. This will tell us how many * blocks to check at each level of the parray. */ count = PARRAY_L0COUNT; for (i = 1; i <= pa->toplevel; i++) count *= PARRAY_L1COUNT; index = 0; /* * Binary search each block on the way down. */ pb = pa->root; total = pa->items; for (i = pa->toplevel; i-- > 0 ;) { count /= PARRAY_L1COUNT; n = (total + count - 1) / count; bot = 0; top = n; while (top - bot > 1) { mid = (top + bot) / 2; if (pb->u.level1[mid].frame > frame) top = mid; else bot = mid; } total -= bot * count; index += bot * count; if (total > count) total = count; pb = pb->u.level1[bot].subblock; } /* * And binary-search the bottom block. */ bot = 0; top = total; while (top - bot > 1) { mid = (top + bot) / 2; if (pb->u.level0[mid].frame > frame) top = mid; else bot = mid; } index += bot; if (index_out) *index_out = index; return pb->u.level0[bot].data; } int parray_retrieve(struct parray *pa, int index, int *frame) { struct parray_block *pb; int count, total, i, n; assert(pa->root); assert(index >= 0 && index < pa->items); /* * Figure out how many items are covered by a single block at * the parray's current top level. */ count = PARRAY_L0COUNT; for (i = 1; i <= pa->toplevel; i++) count *= PARRAY_L1COUNT; /* * Search down the tree. */ pb = pa->root; total = pa->items; for (i = pa->toplevel; i-- > 0 ;) { count /= PARRAY_L1COUNT; n = index / count; index -= n * count; pb = pb->u.level1[n].subblock; } if (frame) *frame = pb->u.level0[index].frame; return pb->u.level0[index].data; } #define CURSOR (inst->w * inst->h) #define TIMETOP (inst->w * inst->h + 1) #define TIMEBOT (inst->w * inst->h + 2) #define FILENO (inst->w * inst->h + 3) #define OFFSET (inst->w * inst->h + 4) #define TOTAL (inst->w * inst->h + 5) void sys_cursor(void *frontend, int x, int y) { } Context get_ctx(void *frontend) { return (Context)frontend; } void do_text(Context ctx, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr) { struct inst *inst = (struct inst *)ctx; int i, index; unsigned int fg, bg, val; for (i = 0; i < len; i++) { assert(y >= 0 && y < inst->h); assert(x+i >= 0 && x+i < inst->w); index = y * inst->w + (x+i); if (inst->unicode) { val = text[i] & 0xFFFF; } else { val = text[i] & 0xFF; if (text[i] >= 0xD95F && text[i] < 0xD97F) val += 0x100; } if (attr & ATTR_BOLD) val |= BOLD; if (attr & ATTR_UNDER) val |= UNDER; if (attr & ATTR_REVERSE) val |= REV; if (attr & ATTR_BLINK) val |= BLINK; fg = (attr & ATTR_FGMASK) >> ATTR_FGSHIFT; bg = (attr & ATTR_BGMASK) >> ATTR_BGSHIFT; if (fg >= 8) fg = 9; if (bg >= 8) bg = 9; val |= (fg << FGSHIFT) | (bg << BGSHIFT); inst->screen[index] = val; /* Track whether we've seen the cursor in this repaint */ if (attr & (TATTR_ACTCURS | TATTR_PASCURS | TATTR_RIGHTCURS)) inst->screen[CURSOR] = y * inst->w + x; } } void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr) { do_text(ctx, x, y, text, len, attr, lattr); } void free_ctx(Context ctx) { } void store_frame(struct inst *inst, unsigned long long delay, int fileno, long fileoff) { int i, n; /* * Force the terminal to refresh, so that our data is up to * date. */ inst->screen[CURSOR] = -1; term_invalidate(inst->term); term_update(inst->term); /* * Now see which terminal integers have changed, and write * movie records for the ones that have. */ inst->movietime += delay; inst->frames++; inst->screen[TIMETOP] = (unsigned long long)inst->movietime >> 32; inst->screen[TIMEBOT] = (unsigned long long)inst->movietime & 0xFFFFFFFF; inst->screen[FILENO] = fileno; inst->screen[OFFSET] = fileoff; n = 0; for (i = 0; i < inst->screenlen; i++) { assert(inst->screen[i] != INVALID_SCREENVAL); if (inst->screen[i] != inst->oldscreen[i]) n++; } for (i = 0; i < inst->screenlen; i++) { if (inst->screen[i] != inst->oldscreen[i]) { parray_append(inst->parrays[i], inst->frames-1, inst->screen[i]); inst->oldscreen[i] = inst->screen[i]; } } } void start_player(struct inst *inst) { int i; setlocale(LC_CTYPE, ""); /* arrange that curses can query the charset */ initscr(); noecho(); move(0,0); refresh(); if (has_colors()) { start_color(); for (i = 0; i < lenof(inst->cpairs); i++) inst->cpairs[i] = -1; inst->pairsused = 1; inst->nines = (use_default_colors() == OK); } else { inst->pairsused = -1; } curses_active = TRUE; } void end_player(struct inst *inst) { if (!curses_active) return; endwin(); curses_active = FALSE; } unsigned int_for_frame(struct inst *inst, int i, int f) { return parray_search(inst->parrays[i], f, NULL); } attr_t attr_cpair(struct inst *inst, int col) { int fg, bg; if (!inst->nines) { /* * If default fg and bg are not supported, fall back to * white on black as a default. */ fg = ((col << FGBGSHIFT) & FG) >> FGSHIFT; bg = ((col << FGBGSHIFT) & BG) >> BGSHIFT; if (fg == 9) fg = 7; if (bg == 9) bg = 0; col = ((fg << FGSHIFT) | (bg << BGSHIFT)) >> FGBGSHIFT; } if (col != 0x99) { if (inst->cpairs[col] == -1) { inst->cpairs[col] = inst->pairsused++; fg = ((col << FGBGSHIFT) & FG) >> FGSHIFT; bg = ((col << FGBGSHIFT) & BG) >> BGSHIFT; init_pair(inst->cpairs[col], (fg < 8 ? fg : -1), (bg < 8 ? bg : -1)); } return inst->cpairs[col]; } return 0; } void display_frame(struct inst *inst, int f) { int i, x, y; /* * Fetch the screen state in this frame. */ for (i = 0; i < inst->screenlen; i++) inst->screen[i] = int_for_frame(inst, i, f); /* * Now display it. */ for (y = 0; y < inst->h; y++) for (x = 0; x < inst->w; x++) { unsigned val = inst->screen[y*inst->w + x]; int col, ch; attr_t attrs = A_NORMAL; short cpair = 0; if (val & BOLD) attrs |= A_BOLD; if (val & UNDER) attrs |= A_UNDERLINE; if (val & REV) attrs |= A_REVERSE; if (val & BLINK) attrs |= A_BLINK; if (inst->pairsused >= 0) { col = (val & FGBG) >> FGBGSHIFT; cpair = attr_cpair(inst, col); } wmove(stdscr, y, x); if (inst->unicode) { #if HAVE_NCURSESW /* * Turn our Unicode character into an ncursesw * 'complex character', and display that directly. */ cchar_t cch; wchar_t ws[2]; ws[0] = val & 0xFFFF; ws[1] = L'\0'; setcchar(&cch, ws, attrs, cpair, NULL); wadd_wch(stdscr, &cch); #else /* * If we don't have ncursesw, we can at least _try_ to * render this Unicode character into a single-byte * character in the local character set. If * successful, display that using ordinary curses. */ char buf[MB_LEN_MAX]; char ch; mbstate_t st; memset(&st, 0, sizeof(st)); if (wcrtomb(buf, val & 0xFFFF, &st) == 1) ch = buf[0]; else ch = '?'; wattrset(stdscr, attrs | COLOR_PAIR(cpair)); waddch(stdscr, ch); #endif } else if (val & 0x100) { switch (val & 0xFF) { /* * Use the ncurses codes for the VT100 line * drawing characters where available. We can't * do all of them: the control character * representations such as HT and VT are not * encoded by ncurses. We replace missing * characters with ACS_BLOCK, on the grounds * that they've got to be _something_. */ case 0x5f: ch = ' '; break; case 0x60: ch = ACS_DIAMOND; break; case 0x61: ch = ACS_CKBOARD; break; case 0x66: ch = ACS_DEGREE; break; case 0x67: ch = ACS_PLMINUS; break; case 0x6a: ch = ACS_LRCORNER; break; case 0x6b: ch = ACS_URCORNER; break; case 0x6c: ch = ACS_ULCORNER; break; case 0x6d: ch = ACS_LLCORNER; break; case 0x6e: ch = ACS_PLUS; break; case 0x6f: ch = ACS_S1; break; case 0x70: ch = ACS_S3; break; case 0x71: ch = ACS_HLINE; break; case 0x72: ch = ACS_S7; break; case 0x73: ch = ACS_S9; break; case 0x74: ch = ACS_LTEE; break; case 0x75: ch = ACS_RTEE; break; case 0x76: ch = ACS_BTEE; break; case 0x77: ch = ACS_TTEE; break; case 0x78: ch = ACS_VLINE; break; case 0x79: ch = ACS_LEQUAL; break; case 0x7a: ch = ACS_GEQUAL; break; case 0x7b: ch = ACS_PI; break; case 0x7c: ch = ACS_NEQUAL; break; case 0x7d: ch = ACS_STERLING; break; case 0x7e: ch = ACS_BULLET; break; default: ch = ACS_BLOCK; break; } wattrset(stdscr, attrs | COLOR_PAIR(cpair)); waddch(stdscr, ch); } else { wattrset(stdscr, attrs | COLOR_PAIR(cpair)); waddch(stdscr, val & 0xFF); } } /* * Draw the OSD and the numeric count, if any. */ if (inst->number) { char buf[40]; int len = sprintf(buf, " %d ", inst->number); wmove(stdscr, 1, inst->w - len - 1); wattrset(stdscr, A_NORMAL); wattron(stdscr, A_BOLD); wattron(stdscr, COLOR_PAIR(attr_cpair(inst,0x47))); /* white on blue */ waddstr(stdscr, buf); } if (inst->osd) { char buf1[80], buf2[80], buf3[80], buf4[80]; long long t; t = int_for_frame(inst, TIMETOP, f); t = (t << 32) + int_for_frame(inst, TIMEBOT, f); sprintf(buf2, "%s x %g", inst->logmod ? "LOG" : "", inst->speedmod); if (inst->logmod || inst->speedmod != 1.0) sprintf(buf4, " Speed:%20s ", buf2); else buf4[0] = '\0'; sprintf(buf2, "%d / %d", f, inst->frames); sprintf(buf1, " Frame:%20s ", buf2); sprintf(buf2, " Time:%21.3f ", t / 1000000.0); sprintf(buf3, " Mode:%21s ", (inst->playing ? "PLAY" : "PAUSE")); wattrset(stdscr, A_NORMAL); wattron(stdscr, A_BOLD); wattron(stdscr, COLOR_PAIR(attr_cpair(inst,0x47))); /* white on blue */ wmove(stdscr, 1, 1); waddstr(stdscr, buf1); wmove(stdscr, 2, 1); waddstr(stdscr, buf2); wmove(stdscr, 3, 1); waddstr(stdscr, buf3); wmove(stdscr, 4, 1); waddstr(stdscr, buf4); } /* * Position the cursor. */ x = inst->screen[CURSOR]; if (x == -1) { curs_set(0); } else { curs_set(1); y = x / inst->w; x %= inst->w; wmove(stdscr, y, x); } } /* * Search the movie array for a frame containing a given piece of * text. Returns the frame in which the text was found, or <0 if * not. */ int search(struct inst *inst, char *string, int start_frame, int backwards) { int f = start_frame; int i, j, k, len; int *searchlines; int *indices, *nextframes; char *scrbuf; /* * Check the bounds. */ if (start_frame >= inst->frames || start_frame < 0) return -1; /* not found */ /* * We track which lines of the display actually changed between * frames, in order to avoid repeatedly searching an unchanged * line. Initially, of course, we set all these flags to TRUE * because the first frame must be searched in full. */ searchlines = snewn(inst->h, int); for (i = 0; i < inst->h; i++) searchlines[i] = TRUE; /* * Allocate space for tracking indices, and the next frame in * which each integer changes, in the display parrays. */ indices = snewn(inst->w * inst->h, int); nextframes = snewn(inst->w * inst->h, int); for (i = 0; i < inst->w * inst->h; i++) indices[i] = -1; /* * And allocate space for the actual display buffer. */ scrbuf = snewn(inst->w * inst->h, char); memset(scrbuf, 0, inst->w * inst->h); len = strlen(string); while (1) { /* * Retrieve the current frame. */ for (i = 0; i < inst->w * inst->h; i++) { int integer, nextframe = -1; int changed = FALSE; if (indices[i] < 0) { /* * This is the first time we've retrieved this * integer, so we need to do a conventional * retrieve operation and set up our index. */ integer = parray_search(inst->parrays[i], f, &indices[i]); changed = TRUE; } else if (backwards && f < nextframes[i]) { /* * This integer has changed in this frame (reverse * search version). */ indices[i]--; integer = parray_retrieve(inst->parrays[i], indices[i], &nextframe); changed = TRUE; } else if (!backwards && f >= nextframes[i]) { /* * This integer has changed in this frame (forward * search version). */ indices[i]++; integer = parray_retrieve(inst->parrays[i], indices[i], NULL); changed = TRUE; } if (changed) { char bufval; /* * Update the screen buffer and mark this line as * changed. */ if (integer & 0x100) bufval = 0; /* ignore line drawing characters */ else bufval = integer; if (scrbuf[i] != bufval) { scrbuf[i] = bufval; searchlines[i / inst->w] = TRUE; } /* * Find the next frame in which this integer * changes. */ if (nextframe < 0) { if (backwards) parray_retrieve(inst->parrays[i], indices[i], &nextframe); else { if (indices[i]+1 < inst->parrays[i]->items) parray_retrieve(inst->parrays[i], indices[i]+1, &nextframe); else nextframe = inst->frames; } } nextframes[i] = nextframe; } } /* * Search whatever lines of the current frame we need to. */ for (i = 0; i < inst->h; i++) if (searchlines[i]) { int found; searchlines[i] = FALSE; /* * FIXME: for the moment we'll just do a naive * string search. */ found = FALSE; for (j = 0; j <= inst->w - len; j++) { for (k = 0; k < len; k++) if (scrbuf[i * inst->w + j + k] != string[k]) break; if (k == len) { found = TRUE; break; } } if (found) goto found_it; } /* * Not found, so move to next frame. */ if (backwards) { f--; if (f < 0) { f = -1; goto found_it; } } else { f++; if (f >= inst->frames) { f = -1; goto found_it; } } } found_it: sfree(scrbuf); sfree(nextframes); sfree(indices); sfree(searchlines); return f; } long long time_after_frame(struct inst *inst, int f) { unsigned long long t1, t2; if (f+1 >= inst->frames) return -1; t1 = int_for_frame(inst, TIMETOP, f); t1 = (t1 << 32) + int_for_frame(inst, TIMEBOT, f); t2 = int_for_frame(inst, TIMETOP, f+1); t2 = (t2 << 32) + int_for_frame(inst, TIMEBOT, f+1); return t2 - t1; } char *getstring(struct inst *inst, const char *prompt) { int w, h, plen, slen, i, c; char *str; int size, len; size = len = 0; str = NULL; getmaxyx(stdscr, h, w); plen = strlen(prompt); if (plen > w-2) plen = w-2; slen = w - plen - 1; while (1) { /* * Display the prompt and the current input. */ wmove(stdscr, h-1, 0); wattrset(stdscr, A_NORMAL); wattron(stdscr, A_BOLD); wattron(stdscr, COLOR_PAIR(attr_cpair(inst,0x47))); /* white on blue */ waddnstr(stdscr, prompt, plen); if (len > slen) { waddch(stdscr, '<'); waddnstr(stdscr, str + len - slen + 1, slen - 1); wmove(stdscr, h-1, plen + slen); } else { waddnstr(stdscr, str, len); for (i = len + plen; i < w; i++) waddch(stdscr, ' '); wmove(stdscr, h-1, plen + len); } /* * Get a character. */ c = getch(); if (c >= ' ' && c <= '~') { /* * Append this character to the string. */ if (len >= size) { size = (len + 5) * 3 / 2; str = sresize(str, size, char); } str[len++] = c; } else if (c == '\010' || c == '\177') { if (len > 0) len--; } else if (c == '\025') { len = 0; /* ^U clears line */ } else if (c == '\027') { /* ^W deletes a word */ while (len > 0 && isspace((unsigned char)(str[len-1]))) len--; while (len > 0 && !isspace((unsigned char)(str[len-1]))) len--; } else if (c == '\r' || c == '\n') { break; } else if (c == '\033') { len = 0; break; } } if (len == 0) { sfree(str); return NULL; } else { str = sresize(str, len+1, char); str[len] = '\0'; return str; } } int main(int argc, char **argv) { struct inst tinst, *inst = &tinst; char *pname; int i, totalsize; time_t start, end; int doing_opts; int iw, ih, startframe = 0; int deftype = NODEFAULT; int prepareonly = FALSE; int autoplay = FALSE, loop = FALSE; /* FILE *debugfp = fopen("/home/simon/.f", "w"); setvbuf(debugfp, NULL, _IONBF, 0); */ #ifdef PARRAY_TEST { struct parray *pa; int i, j, k; pa = parray_new(); for (i = 0; i < 5*3*3*3; i++) { parray_append(pa, i, i*i); for (j = 0; j <= i; j++) { k = parray_search(pa, j, NULL); if (k != j*j) { printf("FAIL: i=%d j=%d wrong=%d right=%d\n", i, j, k, j*j); } } } exit(0); } #endif pname = argv[0]; setlocale(LC_CTYPE, ""); inst->conf = conf_new(); do_defaults(NULL, inst->conf); inst->filenames = NULL; inst->nfiles = inst->filesize = 0; doing_opts = TRUE; iw = 80; ih = 24; while (--argc) { char *p = *++argv; if (doing_opts && *p == '-') { char optbuf[3], *optstr, *optval; int optchr; /* * Special case "--" inhibits further option * processing. */ if (!strcmp(p, "--")) { doing_opts = FALSE; continue; } /* * All other "--" long options are translated into * short ones. */ if (p[1] == '-') { optval = strchr(p, '='); if (optval) *optval++ = '\0'; optstr = p; p += 2; if (!strcmp(p, "width")) optchr = 'w'; else if (!strcmp(p, "height")) optchr = 'h'; else if (!strcmp(p, "frame")) optchr = 'f'; else if (!strcmp(p, "ttyrec")) optchr = 'T'; else if (!strcmp(p, "prepare-only")) optchr = 'P'; else if (!strcmp(p, "autoplay")) optchr = 'A'; else if (!strcmp(p, "loop")) optchr = 'L'; else if (!strcmp(p, "nhrecorder") || !strcmp(p, "nh-recorder") || !strcmp(p, "nh_recorder") || !strcmp(p, "nhrecording") || !strcmp(p, "nh-recording") || !strcmp(p, "nh_recording")) optchr = 'N'; else if (!strcmp(p, "use-terminal-size")) optchr = 'u'; else if (!strcmp(p, "help")) { usage(); return 0; } else if (!strcmp(p, "version")) { version(); return 0; } else if (!strcmp(p, "licence")) { licence(); return 0; } else optchr = '\1'; /* definitely not defined */ } else { optbuf[0] = '-'; optbuf[1] = optchr = p[1]; optbuf[2] = '\0'; optstr = optbuf; if (p[2]) optval = p+2; else optval = NULL; } switch (optchr) { case 'w': case 'h': case 'f': /* * these options all require an argument */ if (!optval) { if (--argc) optval = *++argv; else { fprintf(stderr, "%s: option '%s' expects an" " argument\n", pname, optstr); return 1; } } break; } switch (optchr) { case 'w': assert(optval); iw = atoi(optval); if (iw <= 0) { fprintf(stderr, "%s: argument to '%s' must be positive\n", pname, optstr); return 1; } break; case 'h': assert(optval); ih = atoi(optval); if (ih <= 0) { fprintf(stderr, "%s: argument to '%s' must be positive\n", pname, optstr); return 1; } break; case 'f': assert(optval); startframe = atoi(optval); if (startframe < 0) { fprintf(stderr, "%s: argument to '%s' must be" " non-negative\n", pname, optstr); return 1; } break; case 'T': deftype = TTYREC; break; case 'N': deftype = NHRECORDER; break; case 'P': prepareonly = TRUE; break; case 'A': autoplay = TRUE; break; case 'L': loop = TRUE; break; case 'U': inst->unicode = TRUE; break; case 'u': /* * Use the current screen size as the internal * player's screen size. */ { struct winsize ws; if (!ioctl (0, TIOCGWINSZ, &ws)) { ih = ws.ws_row; iw = ws.ws_col; } else { fprintf(stderr, "%s: unable to discover" " terminal size%s\n", pname, strerror(errno)); } } break; default: fprintf(stderr, "%s: unrecognised option '%s'\n", pname, optstr); return 1; } } else { if (inst->nfiles >= inst->filesize) { inst->filesize = inst->nfiles + 32; inst->filenames = sresize(inst->filenames, inst->filesize, struct filename); } inst->filenames[inst->nfiles].name = dupstr(p); inst->filenames[inst->nfiles].type = deftype; inst->nfiles++; } } if (inst->unicode) { conf_set_str(inst->conf, CONF_line_codepage, "UTF-8"); } else { conf_set_str(inst->conf, CONF_line_codepage, ""); } conf_set_int(inst->conf, CONF_utf8_override, FALSE); init_ucs(&inst->ucsdata, conf_get_str(inst->conf, CONF_line_codepage), FALSE, CS_NONE, conf_get_int(inst->conf, CONF_vtmode)); /* * Fix up ucsdata so that it encodes the VT100 line drawing * characters in the D9xx page, for simplicity of * implementation in do_text(). */ for (i = 0; i < 256; i++) { if (i >= 0x5F && i < 0x7F) inst->ucsdata.unitab_xterm[i] = 0xD900 + i; else inst->ucsdata.unitab_xterm[i] = inst->ucsdata.unitab_line[i]; } inst->ucsdata.dbcs_screenfont = FALSE; inst->w = iw; inst->h = ih; inst->screenlen = TOTAL; inst->screen = snewn(inst->screenlen, int); inst->oldscreen = snewn(inst->screenlen, int); for (i = 0; i < inst->screenlen; i++) inst->oldscreen[i] = INVALID_SCREENVAL; inst->term = term_init(inst->conf, &inst->ucsdata, inst); inst->term->ldisc = NULL; term_size(inst->term, inst->h, inst->w, 0); inst->parrays = snewn(TOTAL, struct parray *); for (i = 0; i < TOTAL; i++) inst->parrays[i] = parray_new(); inst->movietime = 0LL; inst->frames = 0; term_pwron(inst->term, TRUE); start = time(NULL); totalsize = 0; for (i = 0; i < inst->nfiles; i++) { char *p = inst->filenames[i].name; FILE *fp; unsigned char hdrbuf[12]; unsigned char nhrbuf[4096]; char *termdata = NULL; int termdatasize = 0, termdatalen, ret, nframes = 0; unsigned long long timestamp, oldtimestamp = 0LL; unsigned long long frametime, totaltime = 0LL; int typemask, type, nhrstate; long fileoff, filelen; fp = fopen(p, "rb"); if (!fp) { fprintf(stderr, "%s: unable to open '%s': %s\n", pname, p, strerror(errno)); return 1; } if (deftype == NODEFAULT) { /* * First pass: try to identify the file type. We do * this by looking through the entire file to see which * formats it satisfies. */ typemask = 0; oldtimestamp = 0; fseek(fp, 0, SEEK_END); filelen = ftell(fp); rewind(fp); while (1) { /* * Try to parse the file as a ttyrec. */ long offset, newoffset; ret = fread(hdrbuf, 1, 12, fp); if (ret == 0) { typemask |= 1 << TTYREC; break; } else if (ret != 12) { break; } timestamp = GET_32BIT_LSB_FIRST(hdrbuf); timestamp = timestamp*1000000 + GET_32BIT_LSB_FIRST(hdrbuf+4); if (timestamp < oldtimestamp) break; oldtimestamp = timestamp; termdatalen = GET_32BIT_LSB_FIRST(hdrbuf + 8); offset = ftell(fp); ret = fseek(fp, termdatalen, SEEK_CUR); if (ret < 0) break; newoffset = ftell(fp); if (newoffset != offset + termdatalen || newoffset < 0 || newoffset > filelen) break; } rewind(fp); oldtimestamp = timestamp = 0; nhrstate = 0; while (1) { /* * Try to parse the file as a nh-recording. */ int i; ret = fread(nhrbuf, 1, 4096, fp); if (ret == 0) { if (nhrstate == 0 || nhrstate == 1) typemask |= 1 << NHRECORDER; break; } for (i = 0; i < ret; i++) { switch (nhrstate) { case 0: if (nhrbuf[i] == 0) { nhrstate = 1; timestamp = 0; } break; case 1: timestamp |= (unsigned char)nhrbuf[i]; nhrstate = 2; break; case 2: timestamp |= (unsigned char)nhrbuf[i] << 8; nhrstate = 3; break; case 3: timestamp |= (unsigned char)nhrbuf[i] << 16; nhrstate = 4; break; case 4: timestamp |= (unsigned char)nhrbuf[i] << 24; nhrstate = 0; if (oldtimestamp > timestamp) goto done_nhr_loop; /* goto as multi-level break */ oldtimestamp = timestamp; break; } } } done_nhr_loop: rewind(fp); if (!typemask) { /* * No file type matched. */ fprintf(stderr, "%s: '%s' is not a valid input file\n", pname, p); return 1; } else { for (type = 0; type < NTYPES; type++) if (typemask & (1 << type)) break; assert(type < NTYPES); if (typemask & (typemask-1)) { /* test for power of two */ /* * More than one file type matched. */ printf("%s matched more than one file type, assuming %s\n", p, typenames[type]); } } } else type = deftype; term_pwron(inst->term, TRUE); printf("Reading %s (%s) ... ", p, typenames[type]); fflush(stdout); switch (type) { case TTYREC: while (1) { ret = fread(hdrbuf, 1, 12, fp); fileoff = ftell(fp); if (ret == 0) { break; } else if (ret < 0) { fprintf(stderr, "%s: error reading '%s': %s\n", pname, p, strerror(errno)); return 1; } else if (ret < 12) { fprintf(stderr, "%s: unexpected EOF reading '%s'\n", pname, p); return 1; } termdatalen = GET_32BIT_LSB_FIRST(hdrbuf + 8); if (termdatasize < termdatalen) { termdatasize = termdatalen; termdata = sresize(termdata, termdatasize, char); } ret = fread(termdata, 1, termdatalen, fp); if (ret == 0) { break; } else if (ret < 0) { fprintf(stderr, "%s: error reading '%s': %s\n", pname, p, strerror(errno)); return 1; } else if (ret < termdatalen) { fprintf(stderr, "%s: unexpected EOF reading '%s'\n", pname, p); return 1; } totalsize += 12 + termdatalen; timestamp = GET_32BIT_LSB_FIRST(hdrbuf); timestamp = timestamp*1000000 + GET_32BIT_LSB_FIRST(hdrbuf+4); if (oldtimestamp) frametime = timestamp - oldtimestamp; else frametime = (inst->movietime == 0 ? 0 : 1000000); oldtimestamp = timestamp; term_data(inst->term, FALSE, termdata, termdatalen); store_frame(inst, frametime, i, fileoff); nframes++; totaltime += frametime; } break; case NHRECORDER: fileoff = 0; frametime = (inst->movietime == 0 ? 0 : 1000000); nhrstate = 0; oldtimestamp = 0; timestamp = 0; while (1) { int i; long thisoff = ftell(fp); ret = fread(nhrbuf, 1, 4096, fp); if (ret == 0) break; totalsize += ret; for (i = 0; i < ret; i++) { switch (nhrstate) { case 0: if (nhrbuf[i] == 0) { nhrstate = 1; timestamp = 0; } else { term_data(inst->term, FALSE, (char *)nhrbuf+i, 1); } break; case 1: timestamp |= (unsigned char)nhrbuf[i]; nhrstate = 2; break; case 2: timestamp |= (unsigned char)nhrbuf[i] << 8; nhrstate = 3; break; case 3: timestamp |= (unsigned char)nhrbuf[i] << 16; nhrstate = 4; break; case 4: timestamp |= (unsigned char)nhrbuf[i] << 24; nhrstate = 0; store_frame(inst, frametime, i, fileoff); nframes++; totaltime += frametime; frametime = (timestamp - oldtimestamp) * 10000; oldtimestamp = timestamp; fileoff = thisoff + i + 1; break; } } } break; } sfree(termdata); printf("%d frames\n", nframes); fclose(fp); } if (!inst->frames) { usage(); return 0; } end = time(NULL); { int memusage = 0, i; for (i = 0; i < TOTAL; i++) memusage += inst->parrays[i]->memusage; printf("Total %d frames, %d bytes loaded, %d bytes of memory used\n", inst->frames, totalsize, memusage); } printf("Total loading time: %d seconds (%.3g sec/Mb)\n", (int)difftime(end, start), difftime(end, start) * 1048576 / totalsize); if (prepareonly) { printf("Not starting player due to -P option.\n"); return 0; } { int f = startframe, fb = -1; long long t = -1; long long tsince = 0; int changed = TRUE; inst->number = 0; inst->osd = FALSE; inst->playing = autoplay; inst->logmod = FALSE; inst->speedmod = 1.0; inst->searchstr = NULL; inst->searchback = FALSE; start_player(inst); while (1) { int c; if (f < 0) f = 0; if (f >= inst->frames) f = inst->frames - 1; display_frame(inst, f); if (changed) { changed = FALSE; if (inst->playing) { if (loop && f == inst->frames - 1) t = 1000000; /* FIXME: configurable loop delay */ else t = time_after_frame(inst, f); if (t < 0) t = 0; /* just in case ttyrec is malformed */ if (inst->logmod) { /* * Logarithmic time compression: we replace * a time t seconds with log(1+t) seconds. * This starts off with gradient 1 at t=0, * so that short times still work normally; * but times compress gradually as you go * up the scale, so that the person you're * watching doesn't tediously stop and * think all the time. */ t = 1000000 * log(1.0 + t / 1000000.0); } t /= inst->speedmod; } else t = -1; } if (t >= 0) { struct timeval tv; fd_set r; int ret; long long tused; FD_ZERO(&r); FD_SET(0, &r); tv.tv_sec = t / 1000000; tv.tv_usec = t % 1000000; tused = t; wrefresh(stdscr); ret = select(1, &r, NULL, NULL, &tv); if (ret == 0) { c = -1; t = -1; } else { c = getch(); t = tv.tv_sec; t = t * 1000000 + tv.tv_usec; tused -= t; } tsince += tused; if (tsince > 500000) fb = -1; } else { c = getch(); fb = -1; } if (c == 'q' || c == 'Q' || c == '\003') break; if (c == 'b' || c == '<') { /* * When moving backwards, we move relative to the * last frame we moved backwards _to_ rather than * the current frame, provided it's been only a * short time since the last press of 'b'. This * enables the user to hold down 'b' to move * backwards in playing mode, without a very short * frame interval acting as a barrier. */ if (fb >= 0) f = fb; f -= (inst->number ? inst->number : 1); inst->number = 0; tsince = 0; fb = f; changed = TRUE; } else if (c >= '0' && c <= '9') { /* check against integer overflow */ if (inst->number <= INT_MAX / 10 && inst->number * 10 <= INT_MAX - (c - '0')) inst->number = inst->number * 10 + (c - '0'); } else if (c == 'o' || c == 'O') { inst->osd = !inst->osd; inst->number = 0; } else if (c == 'l' || c == 'L') { inst->logmod = !inst->logmod; inst->number = 0; /* * After toggling logarithmic time compression, set * `changed = TRUE' to recompute the current wait * interval. * * Ideally this would take account of the * proportion of that interval which had already * elapsed, but it's unclear exactly what that even * means: when switching from linear to log mode, * do you set the remaining wait interval to the * log of the remaining linear time, or to the same * proportion of the overall log time as was left * of the linear time? And vice versa going the * other way. So for the moment this is very simple * and just restarts the wait interval from the * beginning of its new length when you press L. */ changed = TRUE; } else if (c == 'x') { t *= inst->speedmod; inst->speedmod = (inst->number ? inst->number : 1); t /= inst->speedmod; inst->number = 0; } else if (c == 'X') { t *= inst->speedmod; inst->speedmod = 1.0 / (inst->number ? inst->number : 1); t /= inst->speedmod; inst->number = 0; } else if (c == 'g') { f = inst->number; inst->number = 0; changed = TRUE; } else if (c == 'G') { f = inst->frames - 1 - inst->number; inst->number = 0; changed = TRUE; } else if (c == ' ' || c == '>') { f += (inst->number ? inst->number : 1); inst->number = 0; changed = TRUE; } else if (c == -1 && inst->playing) { if (f+1 < inst->frames) f++; else if (loop) f = 0; changed = TRUE; } else if (c == 'p' || c == 'P' || c == 's' || c == 'S') { inst->playing = !inst->playing; if (inst->playing && f+1 < inst->frames) f++; inst->number = 0; changed = TRUE; } else if (c == '/' || c == '?' || c == '\\' || c == 'n' || c == 'N') { int sf, back; char *str; if (c == '/' || c == '?' || c == '\\') { changed = TRUE; /* need to redraw to remove prompt */ str = getstring(inst, (c == '/' ? "Search forward: " : "Search backward: ")); if (str) { sfree(inst->searchstr); inst->searchstr = str; inst->searchback = (c != '/'); } } else str = inst->searchstr; if (str) { if (c == 'N') back = !inst->searchback; else back = inst->searchback; sf = search(inst, str, f + (back ? -1 : 1), back); if (sf > 0) { f = sf; changed = TRUE;/* need to redraw because we've moved */ } else { beep(); /* not found */ } } } } end_player(inst); printf("\nPlayback finished.\nLast frame reached was %d\n", f); } return 0; } its-playback-time-0.2017-08-30.3c40fd3/ldisc.h000066400000000000000000000011421333656753000201140ustar00rootroot00000000000000/* * ldisc.h: defines the Ldisc data structure used by ldisc.c and * ldiscucs.c. (Unfortunately it was necessary to split the ldisc * module in two, to avoid unnecessarily linking in the Unicode * stuff in tools that don't require it.) */ #ifndef PUTTY_LDISC_H #define PUTTY_LDISC_H typedef struct ldisc_tag { Terminal *term; Backend *back; void *backhandle; void *frontend; /* * Values cached out of conf. */ int telnet_keyboard, telnet_newline, protocol, localecho, localedit; char *buf; int buflen, bufsiz, quotenext; } *Ldisc; #endif /* PUTTY_LDISC_H */ its-playback-time-0.2017-08-30.3c40fd3/ldiscucs.c000066400000000000000000000045371333656753000206350ustar00rootroot00000000000000/* * ldisc.c: PuTTY line discipline. Sits between the input coming * from keypresses in the window, and the output channel leading to * the back end. Implements echo and/or local line editing, * depending on what's currently configured. */ #include #include #include "putty.h" #include "terminal.h" #include "ldisc.h" void lpage_send(void *handle, int codepage, const char *buf, int len, int interactive) { Ldisc ldisc = (Ldisc)handle; wchar_t *widebuffer = 0; int widesize = 0; int wclen; if (codepage < 0) { ldisc_send(ldisc, buf, len, interactive); return; } widesize = len * 2; widebuffer = snewn(widesize, wchar_t); wclen = mb_to_wc(codepage, 0, buf, len, widebuffer, widesize); luni_send(ldisc, widebuffer, wclen, interactive); sfree(widebuffer); } void luni_send(void *handle, const wchar_t *widebuf, int len, int interactive) { Ldisc ldisc = (Ldisc)handle; int ratio = (in_utf(ldisc->term))?3:1; char *linebuffer; int linesize; int i; char *p; linesize = len * ratio * 2; linebuffer = snewn(linesize, char); if (in_utf(ldisc->term)) { /* UTF is a simple algorithm */ for (p = linebuffer, i = 0; i < len; i++) { unsigned long ch = widebuf[i]; if (IS_SURROGATE(ch)) { #ifdef PLATFORM_IS_UTF16 if (i+1 < len) { unsigned long ch2 = widebuf[i+1]; if (IS_SURROGATE_PAIR(ch, ch2)) { ch = FROM_SURROGATES(ch, ch2); i++; } } else #endif { /* Unrecognised UTF-16 sequence */ ch = '.'; } } if (ch < 0x80) { *p++ = (char) (ch); } else if (ch < 0x800) { *p++ = (char) (0xC0 | (ch >> 6)); *p++ = (char) (0x80 | (ch & 0x3F)); } else if (ch < 0x10000) { *p++ = (char) (0xE0 | (ch >> 12)); *p++ = (char) (0x80 | ((ch >> 6) & 0x3F)); *p++ = (char) (0x80 | (ch & 0x3F)); } else { *p++ = (char) (0xF0 | (ch >> 18)); *p++ = (char) (0x80 | ((ch >> 12) & 0x3F)); *p++ = (char) (0x80 | ((ch >> 6) & 0x3F)); *p++ = (char) (0x80 | (ch & 0x3F)); } } } else { int rv; rv = wc_to_mb(ldisc->term->ucsdata->line_codepage, 0, widebuf, len, linebuffer, linesize, NULL, NULL, ldisc->term->ucsdata); if (rv >= 0) p = linebuffer + rv; else p = linebuffer; } if (p > linebuffer) ldisc_send(ldisc, linebuffer, p - linebuffer, interactive); sfree(linebuffer); } its-playback-time-0.2017-08-30.3c40fd3/localenc.c000066400000000000000000000074521333656753000206030ustar00rootroot00000000000000/* * local.c - translate our internal character set codes to and from * our own set of plausibly legible character-set names. Also * provides a canonical name for each encoding (useful for software * announcing what character set it will be using), and a set of * enumeration functions which return a list of supported * encodings one by one. * * charset_from_localenc will attempt all other text translations * as well as this table, to maximise the number of different ways * you can select a supported charset. */ #include #include "charset.h" #include "internal.h" static const struct { const char *name; int charset; int return_in_enum; /* enumeration misses some charsets */ } localencs[] = { { "", CS_NONE, 0 }, { "UTF-8", CS_UTF8, 1 }, { "ISO-8859-1", CS_ISO8859_1, 1 }, { "ISO-8859-1 with X11 line drawing", CS_ISO8859_1_X11, 0 }, { "ISO-8859-2", CS_ISO8859_2, 1 }, { "ISO-8859-3", CS_ISO8859_3, 1 }, { "ISO-8859-4", CS_ISO8859_4, 1 }, { "ISO-8859-5", CS_ISO8859_5, 1 }, { "ISO-8859-6", CS_ISO8859_6, 1 }, { "ISO-8859-7", CS_ISO8859_7, 1 }, { "ISO-8859-8", CS_ISO8859_8, 1 }, { "ISO-8859-9", CS_ISO8859_9, 1 }, { "ISO-8859-10", CS_ISO8859_10, 1 }, { "ISO-8859-11", CS_ISO8859_11, 1 }, { "ISO-8859-13", CS_ISO8859_13, 1 }, { "ISO-8859-14", CS_ISO8859_14, 1 }, { "ISO-8859-15", CS_ISO8859_15, 1 }, { "ISO-8859-16", CS_ISO8859_16, 1 }, { "CP437", CS_CP437, 1 }, { "CP850", CS_CP850, 1 }, { "CP852", CS_CP852, 1 }, { "CP866", CS_CP866, 1 }, { "CP1250", CS_CP1250, 1 }, { "CP1251", CS_CP1251, 1 }, { "CP1252", CS_CP1252, 1 }, { "CP1253", CS_CP1253, 1 }, { "CP1254", CS_CP1254, 1 }, { "CP1255", CS_CP1255, 1 }, { "CP1256", CS_CP1256, 1 }, { "CP1257", CS_CP1257, 1 }, { "CP1258", CS_CP1258, 1 }, { "KOI8-R", CS_KOI8_R, 1 }, { "KOI8-U", CS_KOI8_U, 1 }, { "Mac Roman", CS_MAC_ROMAN, 1 }, { "Mac Turkish", CS_MAC_TURKISH, 1 }, { "Mac Croatian", CS_MAC_CROATIAN, 1 }, { "Mac Iceland", CS_MAC_ICELAND, 1 }, { "Mac Romanian", CS_MAC_ROMANIAN, 1 }, { "Mac Greek", CS_MAC_GREEK, 1 }, { "Mac Cyrillic", CS_MAC_CYRILLIC, 1 }, { "Mac Thai", CS_MAC_THAI, 1 }, { "Mac Centeuro", CS_MAC_CENTEURO, 1 }, { "Mac Symbol", CS_MAC_SYMBOL, 1 }, { "Mac Dingbats", CS_MAC_DINGBATS, 1 }, { "Mac Roman (old)", CS_MAC_ROMAN_OLD, 0 }, { "Mac Croatian (old)", CS_MAC_CROATIAN_OLD, 0 }, { "Mac Iceland (old)", CS_MAC_ICELAND_OLD, 0 }, { "Mac Romanian (old)", CS_MAC_ROMANIAN_OLD, 0 }, { "Mac Greek (old)", CS_MAC_GREEK_OLD, 0 }, { "Mac Cyrillic (old)", CS_MAC_CYRILLIC_OLD, 0 }, { "Mac Ukraine", CS_MAC_UKRAINE, 1 }, { "Mac VT100", CS_MAC_VT100, 1 }, { "Mac VT100 (old)", CS_MAC_VT100_OLD, 0 }, { "VISCII", CS_VISCII, 1 }, { "HP ROMAN8", CS_HP_ROMAN8, 1 }, { "DEC MCS", CS_DEC_MCS, 1 }, }; const char *charset_to_localenc(int charset) { int i; for (i = 0; i < (int)lenof(localencs); i++) if (charset == localencs[i].charset) return localencs[i].name; return NULL; /* not found */ } int charset_from_localenc(const char *name) { int i; if ( (i = charset_from_mimeenc(name)) != CS_NONE) return i; if ( (i = charset_from_xenc(name)) != CS_NONE) return i; for (i = 0; i < (int)lenof(localencs); i++) { const char *p, *q; p = name; q = localencs[i].name; while (*p || *q) { if (tolower((unsigned char)*p) != tolower((unsigned char)*q)) break; p++; q++; } if (!*p && !*q) return localencs[i].charset; } return CS_NONE; /* not found */ } int charset_localenc_nth(int n) { int i; for (i = 0; i < (int)lenof(localencs); i++) if (localencs[i].return_in_enum && !n--) return localencs[i].charset; return CS_NONE; /* end of list */ } its-playback-time-0.2017-08-30.3c40fd3/macenc.c000066400000000000000000000156431333656753000202520ustar00rootroot00000000000000/* * Copyright (c) 2003 Ben Harris * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * macenc.c -- Convert a Mac OS script/region/font combination to our * internal charset code. */ #include #include "charset.h" #include "internal.h" /* * These are defined by Mac OS's , but we'd like to be * independent of that. */ #define smRoman 0 #define smJapanese 1 #define smTradChinese 2 #define smKorean 3 #define smArabic 4 #define smHebrew 5 #define smCyrillic 7 #define smDevenagari 9 #define smGurmukhi 10 #define smGujurati 11 #define smThai 21 #define smSimpChinese 25 #define smTibetan 26 #define smEthiopic 28 #define smCentralEuroRoman 29 #define verGreece 20 #define verIceland 21 #define verTurkey 24 #define verYugoCroatian 25 #define verRomania 39 #define verFaroeIsl 47 #define verIran 48 #define verRussia 49 #define verSlovenian 66 #define verCroatia 68 #define verBulgaria 72 #define verScottishGaelic 75 #define verManxGaelic 76 #define verBreton 77 #define verNunavut 78 #define verWelsh 79 #define verIrishGaelicScript 81 static const struct { int script; int region; int sysvermin; char const *fontname; int charset; } macencs[] = { { smRoman, -1, 0x850, "VT100", CS_MAC_VT100 }, { smRoman, -1, 0, "VT100", CS_MAC_VT100_OLD }, /* * From here on, this table is largely derived from * , * with _OLD version added based on the comments in individual * mapping files. */ { smRoman, -1, 0, "Symbol", CS_MAC_SYMBOL }, { smRoman, -1, 0, "Zapf Dingbats", CS_MAC_DINGBATS }, { smRoman, verTurkey, 0, NULL, CS_MAC_TURKISH }, { smRoman, verYugoCroatian, 0x850, NULL, CS_MAC_CROATIAN }, { smRoman, verYugoCroatian, 0, NULL, CS_MAC_CROATIAN_OLD }, { smRoman, verSlovenian, 0x850, NULL, CS_MAC_CROATIAN }, { smRoman, verSlovenian, 0, NULL, CS_MAC_CROATIAN_OLD }, { smRoman, verCroatia, 0x850, NULL, CS_MAC_CROATIAN }, { smRoman, verCroatia, 0, NULL, CS_MAC_CROATIAN_OLD }, { smRoman, verIceland, 0x850, NULL, CS_MAC_ICELAND }, { smRoman, verIceland, 0, NULL, CS_MAC_ICELAND_OLD }, { smRoman, verFaroeIsl, 0x850, NULL, CS_MAC_ICELAND }, { smRoman, verFaroeIsl, 0, NULL, CS_MAC_ICELAND_OLD }, { smRoman, verRomania, 0x850, NULL, CS_MAC_ROMANIAN }, { smRoman, verRomania, 0, NULL, CS_MAC_ROMANIAN_OLD }, #if 0 /* No mapping table on ftp.unicode.org */ { smRoman, verIreland, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verIreland, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verScottishGaelic, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verScottishGaelic, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verManxGaelic, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verManxGaelic, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verBreton, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verBreton, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verWelsh, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verWelsh, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verIrishGaelicScript, 0x850, NULL, CS_MAC_GAELIC }, { smRoman, verIrishGaelicScript, 0, NULL, CS_MAC_GAELIC_OLD }, #endif { smRoman, verGreece, 0x922, NULL, CS_MAC_GREEK }, { smRoman, verGreece, 0, NULL, CS_MAC_GREEK_OLD }, { smRoman, -1, 0x850, NULL, CS_MAC_ROMAN }, { smRoman, -1, 0, NULL, CS_MAC_ROMAN_OLD }, #if 0 /* Multi-byte encodings, not yet supported */ { smJapanese, -1, 0, NULL, CS_MAC_JAPANESE }, { smTradChinese, -1, 0, NULL, CS_MAC_CHINTRAD }, { smKorean, -1, 0, NULL, CS_MAC_KOREAN }, #endif #if 0 /* Bidirectional encodings, not yet supported */ { smArabic, verIran, 0, NULL, CS_MAC_FARSI }, { smArabic, -1, 0, NULL, CS_MAC_ARABIC }, { smHebrew, -1, 0, NULL, CS_MAC_HEBREW }, #endif { smCyrillic, -1, 0x900, NULL, CS_MAC_CYRILLIC }, { smCyrillic, verRussia, 0, NULL, CS_MAC_CYRILLIC_OLD }, { smCyrillic, verBulgaria, 0, NULL, CS_MAC_CYRILLIC_OLD }, { smCyrillic, -1, 0, NULL, CS_MAC_UKRAINE }, #if 0 /* Complex Indic scripts, not yet supported */ { smDevanagari, -1, 0, NULL, CS_MAC_DEVENAGA }, { smGurmukhi, -1, 0, NULL, CS_MAC_GURMUKHI }, { smGujurati, -1, 0, NULL, CS_MAC_GUJURATI }, #endif { smThai, -1, 0, NULL, CS_MAC_THAI }, #if 0 /* Multi-byte encoding, not yet supported */ { smSimpChinese, -1, 0, NULL, CS_MAC_CHINSIMP }, #endif #if 0 /* No mapping table on ftp.unicode.org */ { smTibetan, -1, 0, NULL, CS_MAC_TIBETAN }, { smEthiopic, -1, 0, NULL, CS_MAC_ETHIOPIC }, { smEthiopic, verNanavut, 0, NULL, CS_MAC_INUIT }, #endif { smCentralEuroRoman, -1, 0, NULL, CS_MAC_CENTEURO }, }; int charset_from_macenc(int script, int region, int sysvers, char const *fontname) { int i; for (i = 0; i < (int)lenof(macencs); i++) if ((macencs[i].script == script) && (macencs[i].region < 0 || macencs[i].region == region) && (macencs[i].sysvermin <= sysvers) && (macencs[i].fontname == NULL || (fontname != NULL && strcmp(macencs[i].fontname, fontname) == 0))) return macencs[i].charset; return CS_NONE; } its-playback-time-0.2017-08-30.3c40fd3/mimeenc.c000066400000000000000000000131231333656753000204300ustar00rootroot00000000000000/* * mimeenc.c - translate our internal character set codes to and * from MIME standard character-set names. * */ #include #include "charset.h" #include "internal.h" static const struct { const char *name; int charset; } mimeencs[] = { /* * These names are taken from * * http://www.iana.org/assignments/character-sets * * Where multiple encoding names map to the same encoding id * (such as the variety of aliases for ISO-8859-1), the first * is considered canonical and will be returned when * translating the id to a string. */ { "ISO-8859-1", CS_ISO8859_1 }, { "iso-ir-100", CS_ISO8859_1 }, { "ISO_8859-1", CS_ISO8859_1 }, { "ISO_8859-1:1987", CS_ISO8859_1 }, { "latin1", CS_ISO8859_1 }, { "l1", CS_ISO8859_1 }, { "IBM819", CS_ISO8859_1 }, { "CP819", CS_ISO8859_1 }, { "csISOLatin1", CS_ISO8859_1 }, { "ISO-8859-2", CS_ISO8859_2 }, { "ISO_8859-2:1987", CS_ISO8859_2 }, { "iso-ir-101", CS_ISO8859_2 }, { "ISO_8859-2", CS_ISO8859_2 }, { "latin2", CS_ISO8859_2 }, { "l2", CS_ISO8859_2 }, { "csISOLatin2", CS_ISO8859_2 }, { "ISO-8859-3", CS_ISO8859_3 }, { "ISO_8859-3:1988", CS_ISO8859_3 }, { "iso-ir-109", CS_ISO8859_3 }, { "ISO_8859-3", CS_ISO8859_3 }, { "latin3", CS_ISO8859_3 }, { "l3", CS_ISO8859_3 }, { "csISOLatin3", CS_ISO8859_3 }, { "ISO-8859-4", CS_ISO8859_4 }, { "ISO_8859-4:1988", CS_ISO8859_4 }, { "iso-ir-110", CS_ISO8859_4 }, { "ISO_8859-4", CS_ISO8859_4 }, { "latin4", CS_ISO8859_4 }, { "l4", CS_ISO8859_4 }, { "csISOLatin4", CS_ISO8859_4 }, { "ISO-8859-5", CS_ISO8859_5 }, { "ISO_8859-5:1988", CS_ISO8859_5 }, { "iso-ir-144", CS_ISO8859_5 }, { "ISO_8859-5", CS_ISO8859_5 }, { "cyrillic", CS_ISO8859_5 }, { "csISOLatinCyrillic", CS_ISO8859_5 }, { "ISO-8859-6", CS_ISO8859_6 }, { "ISO_8859-6:1987", CS_ISO8859_6 }, { "iso-ir-127", CS_ISO8859_6 }, { "ISO_8859-6", CS_ISO8859_6 }, { "ECMA-114", CS_ISO8859_6 }, { "ASMO-708", CS_ISO8859_6 }, { "arabic", CS_ISO8859_6 }, { "csISOLatinArabic", CS_ISO8859_6 }, { "ISO-8859-7", CS_ISO8859_7 }, { "ISO_8859-7:1987", CS_ISO8859_7 }, { "iso-ir-126", CS_ISO8859_7 }, { "ISO_8859-7", CS_ISO8859_7 }, { "ELOT_928", CS_ISO8859_7 }, { "ECMA-118", CS_ISO8859_7 }, { "greek", CS_ISO8859_7 }, { "greek8", CS_ISO8859_7 }, { "csISOLatinGreek", CS_ISO8859_7 }, { "ISO-8859-8", CS_ISO8859_8 }, { "ISO_8859-8:1988", CS_ISO8859_8 }, { "iso-ir-138", CS_ISO8859_8 }, { "ISO_8859-8", CS_ISO8859_8 }, { "hebrew", CS_ISO8859_8 }, { "csISOLatinHebrew", CS_ISO8859_8 }, { "ISO-8859-9", CS_ISO8859_9 }, { "ISO_8859-9:1989", CS_ISO8859_9 }, { "iso-ir-148", CS_ISO8859_9 }, { "ISO_8859-9", CS_ISO8859_9 }, { "latin5", CS_ISO8859_9 }, { "l5", CS_ISO8859_9 }, { "csISOLatin5", CS_ISO8859_9 }, { "ISO-8859-10", CS_ISO8859_10 }, { "iso-ir-157", CS_ISO8859_10 }, { "l6", CS_ISO8859_10 }, { "ISO_8859-10:1992", CS_ISO8859_10 }, { "csISOLatin6", CS_ISO8859_10 }, { "latin6", CS_ISO8859_10 }, { "ISO-8859-13", CS_ISO8859_13 }, { "ISO-8859-14", CS_ISO8859_14 }, { "iso-ir-199", CS_ISO8859_14 }, { "ISO_8859-14:1998", CS_ISO8859_14 }, { "ISO_8859-14", CS_ISO8859_14 }, { "latin8", CS_ISO8859_14 }, { "iso-celtic", CS_ISO8859_14 }, { "l8", CS_ISO8859_14 }, { "ISO-8859-15", CS_ISO8859_15 }, { "ISO_8859-15", CS_ISO8859_15 }, { "Latin-9", CS_ISO8859_15 }, { "ISO-8859-16", CS_ISO8859_16 }, { "iso-ir-226", CS_ISO8859_16 }, { "ISO_8859-16", CS_ISO8859_16 }, { "ISO_8859-16:2001", CS_ISO8859_16 }, { "latin10", CS_ISO8859_16 }, { "l10", CS_ISO8859_16 }, { "IBM437", CS_CP437 }, { "cp437", CS_CP437 }, { "437", CS_CP437 }, { "csPC8CodePage437", CS_CP437 }, { "IBM850", CS_CP850 }, { "cp850", CS_CP850 }, { "850", CS_CP850 }, { "csPC850Multilingual", CS_CP850 }, { "IBM852", CS_CP852 }, { "cp852", CS_CP852 }, { "852", CS_CP852 }, { "csIBM852", CS_CP852 }, { "IBM866", CS_CP866 }, { "cp866", CS_CP866 }, { "866", CS_CP866 }, { "csIBM866", CS_CP866 }, { "windows-1250", CS_CP1250 }, { "windows-1251", CS_CP1251 }, { "windows-1252", CS_CP1252 }, { "windows-1253", CS_CP1253 }, { "windows-1254", CS_CP1254 }, { "windows-1255", CS_CP1255 }, { "windows-1256", CS_CP1256 }, { "windows-1257", CS_CP1257 }, { "windows-1258", CS_CP1258 }, { "KOI8-R", CS_KOI8_R }, { "csKOI8R", CS_KOI8_R }, { "KOI8-U", CS_KOI8_U }, { "macintosh", CS_MAC_ROMAN_OLD }, { "mac", CS_MAC_ROMAN_OLD }, { "csMacintosh", CS_MAC_ROMAN_OLD }, { "VISCII", CS_VISCII }, { "csVISCII", CS_VISCII }, { "hp-roman8", CS_HP_ROMAN8 }, { "roman8", CS_HP_ROMAN8 }, { "r8", CS_HP_ROMAN8 }, { "csHPRoman8", CS_HP_ROMAN8 }, { "DEC-MCS", CS_DEC_MCS }, { "dec", CS_DEC_MCS }, { "csDECMCS", CS_DEC_MCS }, { "UTF-8", CS_UTF8 }, }; const char *charset_to_mimeenc(int charset) { int i; for (i = 0; i < (int)lenof(mimeencs); i++) if (charset == mimeencs[i].charset) return mimeencs[i].name; return NULL; /* not found */ } int charset_from_mimeenc(const char *name) { int i; for (i = 0; i < (int)lenof(mimeencs); i++) { const char *p, *q; p = name; q = mimeencs[i].name; while (*p || *q) { if (tolower((unsigned char)*p) != tolower((unsigned char)*q)) break; p++; q++; } if (!*p && !*q) return mimeencs[i].charset; } return CS_NONE; /* not found */ } its-playback-time-0.2017-08-30.3c40fd3/minibidi.c000066400000000000000000001635261333656753000206140ustar00rootroot00000000000000/************************************************************************ * * ------------ * Description: * ------------ * This is an implementation of Unicode's Bidirectional Algorithm * (known as UAX #9). * * http://www.unicode.org/reports/tr9/ * * Author: Ahmad Khalifa * * (www.arabeyes.org - under MIT license) * ************************************************************************/ /* * TODO: * ===== * - Explicit marks need to be handled (they are not 100% now) * - Ligatures */ #include /* definition of wchar_t*/ #include "misc.h" #define LMASK 0x3F /* Embedding Level mask */ #define OMASK 0xC0 /* Override mask */ #define OISL 0x80 /* Override is L */ #define OISR 0x40 /* Override is R */ /* For standalone compilation in a testing mode. * Still depends on the PuTTY headers for snewn and sfree, but can avoid * _linking_ with any other PuTTY code. */ #ifdef TEST_GETTYPE #define safemalloc malloc #define safefree free #endif /* Shaping Helpers */ #define STYPE(xh) ((((xh) >= SHAPE_FIRST) && ((xh) <= SHAPE_LAST)) ? \ shapetypes[(xh)-SHAPE_FIRST].type : SU) /*))*/ #define SISOLATED(xh) (shapetypes[(xh)-SHAPE_FIRST].form_b) #define SFINAL(xh) ((xh)+1) #define SINITIAL(xh) ((xh)+2) #define SMEDIAL(ch) ((ch)+3) #define leastGreaterOdd(x) ( ((x)+1) | 1 ) #define leastGreaterEven(x) ( ((x)+2) &~ 1 ) typedef struct bidi_char { unsigned int origwc, wc; unsigned short index; } bidi_char; /* function declarations */ void flipThisRun(bidi_char *from, unsigned char* level, int max, int count); int findIndexOfRun(unsigned char* level , int start, int count, int tlevel); unsigned char getType(int ch); unsigned char setOverrideBits(unsigned char level, unsigned char override); int getPreviousLevel(unsigned char* level, int from); int do_shape(bidi_char *line, bidi_char *to, int count); int do_bidi(bidi_char *line, int count); void doMirror(unsigned int *ch); /* character types */ enum { L, LRE, LRO, R, AL, RLE, RLO, PDF, EN, ES, ET, AN, CS, NSM, BN, B, S, WS, ON }; /* Shaping Types */ enum { SL, /* Left-Joining, doesn't exist in U+0600 - U+06FF */ SR, /* Right-Joining, ie has Isolated, Final */ SD, /* Dual-Joining, ie has Isolated, Final, Initial, Medial */ SU, /* Non-Joining */ SC /* Join-Causing, like U+0640 (TATWEEL) */ }; typedef struct { char type; wchar_t form_b; } shape_node; /* Kept near the actual table, for verification. */ #define SHAPE_FIRST 0x621 #define SHAPE_LAST (SHAPE_FIRST + lenof(shapetypes) - 1) const shape_node shapetypes[] = { /* index, Typ, Iso, Ligature Index*/ /* 621 */ {SU, 0xFE80}, /* 622 */ {SR, 0xFE81}, /* 623 */ {SR, 0xFE83}, /* 624 */ {SR, 0xFE85}, /* 625 */ {SR, 0xFE87}, /* 626 */ {SD, 0xFE89}, /* 627 */ {SR, 0xFE8D}, /* 628 */ {SD, 0xFE8F}, /* 629 */ {SR, 0xFE93}, /* 62A */ {SD, 0xFE95}, /* 62B */ {SD, 0xFE99}, /* 62C */ {SD, 0xFE9D}, /* 62D */ {SD, 0xFEA1}, /* 62E */ {SD, 0xFEA5}, /* 62F */ {SR, 0xFEA9}, /* 630 */ {SR, 0xFEAB}, /* 631 */ {SR, 0xFEAD}, /* 632 */ {SR, 0xFEAF}, /* 633 */ {SD, 0xFEB1}, /* 634 */ {SD, 0xFEB5}, /* 635 */ {SD, 0xFEB9}, /* 636 */ {SD, 0xFEBD}, /* 637 */ {SD, 0xFEC1}, /* 638 */ {SD, 0xFEC5}, /* 639 */ {SD, 0xFEC9}, /* 63A */ {SD, 0xFECD}, /* 63B */ {SU, 0x0}, /* 63C */ {SU, 0x0}, /* 63D */ {SU, 0x0}, /* 63E */ {SU, 0x0}, /* 63F */ {SU, 0x0}, /* 640 */ {SC, 0x0}, /* 641 */ {SD, 0xFED1}, /* 642 */ {SD, 0xFED5}, /* 643 */ {SD, 0xFED9}, /* 644 */ {SD, 0xFEDD}, /* 645 */ {SD, 0xFEE1}, /* 646 */ {SD, 0xFEE5}, /* 647 */ {SD, 0xFEE9}, /* 648 */ {SR, 0xFEED}, /* 649 */ {SR, 0xFEEF}, /* SD */ /* 64A */ {SD, 0xFEF1}, /* 64B */ {SU, 0x0}, /* 64C */ {SU, 0x0}, /* 64D */ {SU, 0x0}, /* 64E */ {SU, 0x0}, /* 64F */ {SU, 0x0}, /* 650 */ {SU, 0x0}, /* 651 */ {SU, 0x0}, /* 652 */ {SU, 0x0}, /* 653 */ {SU, 0x0}, /* 654 */ {SU, 0x0}, /* 655 */ {SU, 0x0}, /* 656 */ {SU, 0x0}, /* 657 */ {SU, 0x0}, /* 658 */ {SU, 0x0}, /* 659 */ {SU, 0x0}, /* 65A */ {SU, 0x0}, /* 65B */ {SU, 0x0}, /* 65C */ {SU, 0x0}, /* 65D */ {SU, 0x0}, /* 65E */ {SU, 0x0}, /* 65F */ {SU, 0x0}, /* 660 */ {SU, 0x0}, /* 661 */ {SU, 0x0}, /* 662 */ {SU, 0x0}, /* 663 */ {SU, 0x0}, /* 664 */ {SU, 0x0}, /* 665 */ {SU, 0x0}, /* 666 */ {SU, 0x0}, /* 667 */ {SU, 0x0}, /* 668 */ {SU, 0x0}, /* 669 */ {SU, 0x0}, /* 66A */ {SU, 0x0}, /* 66B */ {SU, 0x0}, /* 66C */ {SU, 0x0}, /* 66D */ {SU, 0x0}, /* 66E */ {SU, 0x0}, /* 66F */ {SU, 0x0}, /* 670 */ {SU, 0x0}, /* 671 */ {SR, 0xFB50}, /* 672 */ {SU, 0x0}, /* 673 */ {SU, 0x0}, /* 674 */ {SU, 0x0}, /* 675 */ {SU, 0x0}, /* 676 */ {SU, 0x0}, /* 677 */ {SU, 0x0}, /* 678 */ {SU, 0x0}, /* 679 */ {SD, 0xFB66}, /* 67A */ {SD, 0xFB5E}, /* 67B */ {SD, 0xFB52}, /* 67C */ {SU, 0x0}, /* 67D */ {SU, 0x0}, /* 67E */ {SD, 0xFB56}, /* 67F */ {SD, 0xFB62}, /* 680 */ {SD, 0xFB5A}, /* 681 */ {SU, 0x0}, /* 682 */ {SU, 0x0}, /* 683 */ {SD, 0xFB76}, /* 684 */ {SD, 0xFB72}, /* 685 */ {SU, 0x0}, /* 686 */ {SD, 0xFB7A}, /* 687 */ {SD, 0xFB7E}, /* 688 */ {SR, 0xFB88}, /* 689 */ {SU, 0x0}, /* 68A */ {SU, 0x0}, /* 68B */ {SU, 0x0}, /* 68C */ {SR, 0xFB84}, /* 68D */ {SR, 0xFB82}, /* 68E */ {SR, 0xFB86}, /* 68F */ {SU, 0x0}, /* 690 */ {SU, 0x0}, /* 691 */ {SR, 0xFB8C}, /* 692 */ {SU, 0x0}, /* 693 */ {SU, 0x0}, /* 694 */ {SU, 0x0}, /* 695 */ {SU, 0x0}, /* 696 */ {SU, 0x0}, /* 697 */ {SU, 0x0}, /* 698 */ {SR, 0xFB8A}, /* 699 */ {SU, 0x0}, /* 69A */ {SU, 0x0}, /* 69B */ {SU, 0x0}, /* 69C */ {SU, 0x0}, /* 69D */ {SU, 0x0}, /* 69E */ {SU, 0x0}, /* 69F */ {SU, 0x0}, /* 6A0 */ {SU, 0x0}, /* 6A1 */ {SU, 0x0}, /* 6A2 */ {SU, 0x0}, /* 6A3 */ {SU, 0x0}, /* 6A4 */ {SD, 0xFB6A}, /* 6A5 */ {SU, 0x0}, /* 6A6 */ {SD, 0xFB6E}, /* 6A7 */ {SU, 0x0}, /* 6A8 */ {SU, 0x0}, /* 6A9 */ {SD, 0xFB8E}, /* 6AA */ {SU, 0x0}, /* 6AB */ {SU, 0x0}, /* 6AC */ {SU, 0x0}, /* 6AD */ {SD, 0xFBD3}, /* 6AE */ {SU, 0x0}, /* 6AF */ {SD, 0xFB92}, /* 6B0 */ {SU, 0x0}, /* 6B1 */ {SD, 0xFB9A}, /* 6B2 */ {SU, 0x0}, /* 6B3 */ {SD, 0xFB96}, /* 6B4 */ {SU, 0x0}, /* 6B5 */ {SU, 0x0}, /* 6B6 */ {SU, 0x0}, /* 6B7 */ {SU, 0x0}, /* 6B8 */ {SU, 0x0}, /* 6B9 */ {SU, 0x0}, /* 6BA */ {SR, 0xFB9E}, /* 6BB */ {SD, 0xFBA0}, /* 6BC */ {SU, 0x0}, /* 6BD */ {SU, 0x0}, /* 6BE */ {SD, 0xFBAA}, /* 6BF */ {SU, 0x0}, /* 6C0 */ {SR, 0xFBA4}, /* 6C1 */ {SD, 0xFBA6}, /* 6C2 */ {SU, 0x0}, /* 6C3 */ {SU, 0x0}, /* 6C4 */ {SU, 0x0}, /* 6C5 */ {SR, 0xFBE0}, /* 6C6 */ {SR, 0xFBD9}, /* 6C7 */ {SR, 0xFBD7}, /* 6C8 */ {SR, 0xFBDB}, /* 6C9 */ {SR, 0xFBE2}, /* 6CA */ {SU, 0x0}, /* 6CB */ {SR, 0xFBDE}, /* 6CC */ {SD, 0xFBFC}, /* 6CD */ {SU, 0x0}, /* 6CE */ {SU, 0x0}, /* 6CF */ {SU, 0x0}, /* 6D0 */ {SU, 0x0}, /* 6D1 */ {SU, 0x0}, /* 6D2 */ {SR, 0xFBAE}, }; /* * Flips the text buffer, according to max level, and * all higher levels * * Input: * from: text buffer, on which to apply flipping * level: resolved levels buffer * max: the maximum level found in this line (should be unsigned char) * count: line size in bidi_char */ void flipThisRun(bidi_char *from, unsigned char *level, int max, int count) { int i, j, k, tlevel; bidi_char temp; j = i = 0; while (i j; k--, j++) { temp = from[k]; from[k] = from[j]; from[j] = temp; } } } /* * Finds the index of a run with level equals tlevel */ int findIndexOfRun(unsigned char* level , int start, int count, int tlevel) { int i; for (i=start; i 1) { k = (i + j) / 2; if (ch < lookup[k].first) j = k; else if (ch > lookup[k].last) i = k; else return lookup[k].type; } /* * If we reach here, the character was not in any of the * intervals listed in the lookup table. This means we return * ON (`Other Neutrals'). This is the appropriate code for any * character genuinely not listed in the Unicode table, and * also the table above has deliberately left out any * characters _explicitly_ listed as ON (to save space!). */ return ON; } /* * Function exported to front ends to allow them to identify * bidi-active characters (in case, for example, the platform's * text display function can't conveniently be prevented from doing * its own bidi and so special treatment is required for characters * that would cause the bidi algorithm to activate). * * This function is passed a single Unicode code point, and returns * nonzero if the presence of this code point can possibly cause * the bidi algorithm to do any reordering. Thus, any string * composed entirely of characters for which is_rtl() returns zero * should be safe to pass to a bidi-active platform display * function without fear. * * (is_rtl() must therefore also return true for any character * which would be affected by Arabic shaping, but this isn't * important because all such characters are right-to-left so it * would have flagged them anyway.) */ int is_rtl(int c) { /* * After careful reading of the Unicode bidi algorithm (URL as * given at the top of this file) I believe that the only * character classes which can possibly cause trouble are R, * AL, RLE and RLO. I think that any string containing no * character in any of those classes will be displayed * uniformly left-to-right by the Unicode bidi algorithm. */ const int mask = (1< 0) { unsigned char current = level[--from]; while (from >= 0 && level[from] == current) from--; if (from >= 0) return level[from]; return -1; } else return -1; } /* The Main shaping function, and the only one to be used * by the outside world. * * line: buffer to apply shaping to. this must be passed by doBidi() first * to: output buffer for the shaped data * count: number of characters in line */ int do_shape(bidi_char *line, bidi_char *to, int count) { int i, tempShape, ligFlag; for (ligFlag=i=0; i 0) switch (line[i-1].wc) { case 0x622: ligFlag = 1; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEF6; else to[i].wc = 0xFEF5; break; case 0x623: ligFlag = 1; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEF8; else to[i].wc = 0xFEF7; break; case 0x625: ligFlag = 1; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEFA; else to[i].wc = 0xFEF9; break; case 0x627: ligFlag = 1; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEFC; else to[i].wc = 0xFEFB; break; } if (ligFlag) { to[i-1].wc = 0x20; ligFlag = 0; break; } } if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) { tempShape = (i > 0 ? STYPE(line[i-1].wc) : SU); if ((tempShape == SR) || (tempShape == SD) || (tempShape == SC)) to[i].wc = SMEDIAL((SISOLATED(line[i].wc))); else to[i].wc = SFINAL((SISOLATED(line[i].wc))); break; } tempShape = (i > 0 ? STYPE(line[i-1].wc) : SU); if ((tempShape == SR) || (tempShape == SD) || (tempShape == SC)) to[i].wc = SINITIAL((SISOLATED(line[i].wc))); else to[i].wc = SISOLATED(line[i].wc); break; } } return 1; } /* * The Main Bidi Function, and the only function that should * be used by the outside world. * * line: a buffer of size count containing text to apply * the Bidirectional algorithm to. */ int do_bidi(bidi_char *line, int count) { unsigned char* types; unsigned char* levels; unsigned char paragraphLevel; unsigned char currentEmbedding; unsigned char currentOverride; unsigned char tempType; int i, j, yes, bover; /* Check the presence of R or AL types as optimization */ yes = 0; for (i=0; i= 0) { if (types[j] == AL) { types[i] = AN; break; } else if (types[j] == R || types[j] == L) { break; } j--; } } } /* Rule (W3) * W3. Change all ALs to R. * * Optimization: on Rule Xn, we might set a flag on AL type * to prevent this loop in L R lines only... */ for (i=0; i 0 && types[i-1] == EN) { types[i] = EN; continue; } else if (i < count-1 && types[i+1] == EN) { types[i] = EN; continue; } else if (i < count-1 && types[i+1] == ET) { j=i; while (j = 0) { if (types[j] == L) { types[i] = L; break; } else if (types[j] == R || types[j] == AL) { break; } j--; } } } /* Rule (N1) * N1. A sequence of neutrals takes the direction of the surrounding * strong text if the text on both sides has the same direction. European * and Arabic numbers are treated as though they were R. */ if (count >= 2 && types[0] == ON) { if ((types[1] == R) || (types[1] == EN) || (types[1] == AN)) types[0] = R; else if (types[1] == L) types[0] = L; } for (i=1; i<(count-1); i++) { if (types[i] == ON) { if (types[i-1] == L) { j=i; while (j<(count-1) && types[j] == ON) { j++; } if (types[j] == L) { while (i= 2 && types[count-1] == ON) { if (types[count-2] == R || types[count-2] == EN || types[count-2] == AN) types[count-1] = R; else if (types[count-2] == L) types[count-1] = L; } /* Rule (N2) * N2. Any remaining neutrals take the embedding direction. */ for (i=0; i0 && (getType(line[j].wc) == WS)) { j--; } if (j < (count-1)) { for (j++; j=i ; j--) { levels[j] = paragraphLevel; } } } else if (tempType == B || tempType == S) { levels[i] = paragraphLevel; } } /* Rule (L4) NOT IMPLEMENTED * L4. A character that possesses the mirrored property as specified by * Section 4.7, Mirrored, must be depicted by a mirrored glyph if the * resolved directionality of that character is R. */ /* Note: this is implemented before L2 for efficiency */ for (i=0; i tempType) tempType = levels[i]; i++; } /* maximum level in tempType. */ while (tempType > 0) { /* loop from highest level to the least odd, */ /* which i assume is 1 */ flipThisRun(line, levels, tempType, count); tempType--; } /* Rule (L3) NOT IMPLEMENTED * L3. Combining marks applied to a right-to-left base character will at * this point precede their base character. If the rendering engine * expects them to follow the base characters in the final display * process, then the ordering of the marks and the base character must * be reversed. */ sfree(types); sfree(levels); return R; } /* * Bad, Horrible function * takes a pointer to a character that is checked for * having a mirror glyph. */ void doMirror(unsigned int *ch) { if ((*ch & 0xFF00) == 0) { switch (*ch) { case 0x0028: *ch = 0x0029; break; case 0x0029: *ch = 0x0028; break; case 0x003C: *ch = 0x003E; break; case 0x003E: *ch = 0x003C; break; case 0x005B: *ch = 0x005D; break; case 0x005D: *ch = 0x005B; break; case 0x007B: *ch = 0x007D; break; case 0x007D: *ch = 0x007B; break; case 0x00AB: *ch = 0x00BB; break; case 0x00BB: *ch = 0x00AB; break; } } else if ((*ch & 0xFF00) == 0x2000) { switch (*ch) { case 0x2039: *ch = 0x203A; break; case 0x203A: *ch = 0x2039; break; case 0x2045: *ch = 0x2046; break; case 0x2046: *ch = 0x2045; break; case 0x207D: *ch = 0x207E; break; case 0x207E: *ch = 0x207D; break; case 0x208D: *ch = 0x208E; break; case 0x208E: *ch = 0x208D; break; } } else if ((*ch & 0xFF00) == 0x2200) { switch (*ch) { case 0x2208: *ch = 0x220B; break; case 0x2209: *ch = 0x220C; break; case 0x220A: *ch = 0x220D; break; case 0x220B: *ch = 0x2208; break; case 0x220C: *ch = 0x2209; break; case 0x220D: *ch = 0x220A; break; case 0x2215: *ch = 0x29F5; break; case 0x223C: *ch = 0x223D; break; case 0x223D: *ch = 0x223C; break; case 0x2243: *ch = 0x22CD; break; case 0x2252: *ch = 0x2253; break; case 0x2253: *ch = 0x2252; break; case 0x2254: *ch = 0x2255; break; case 0x2255: *ch = 0x2254; break; case 0x2264: *ch = 0x2265; break; case 0x2265: *ch = 0x2264; break; case 0x2266: *ch = 0x2267; break; case 0x2267: *ch = 0x2266; break; case 0x2268: *ch = 0x2269; break; case 0x2269: *ch = 0x2268; break; case 0x226A: *ch = 0x226B; break; case 0x226B: *ch = 0x226A; break; case 0x226E: *ch = 0x226F; break; case 0x226F: *ch = 0x226E; break; case 0x2270: *ch = 0x2271; break; case 0x2271: *ch = 0x2270; break; case 0x2272: *ch = 0x2273; break; case 0x2273: *ch = 0x2272; break; case 0x2274: *ch = 0x2275; break; case 0x2275: *ch = 0x2274; break; case 0x2276: *ch = 0x2277; break; case 0x2277: *ch = 0x2276; break; case 0x2278: *ch = 0x2279; break; case 0x2279: *ch = 0x2278; break; case 0x227A: *ch = 0x227B; break; case 0x227B: *ch = 0x227A; break; case 0x227C: *ch = 0x227D; break; case 0x227D: *ch = 0x227C; break; case 0x227E: *ch = 0x227F; break; case 0x227F: *ch = 0x227E; break; case 0x2280: *ch = 0x2281; break; case 0x2281: *ch = 0x2280; break; case 0x2282: *ch = 0x2283; break; case 0x2283: *ch = 0x2282; break; case 0x2284: *ch = 0x2285; break; case 0x2285: *ch = 0x2284; break; case 0x2286: *ch = 0x2287; break; case 0x2287: *ch = 0x2286; break; case 0x2288: *ch = 0x2289; break; case 0x2289: *ch = 0x2288; break; case 0x228A: *ch = 0x228B; break; case 0x228B: *ch = 0x228A; break; case 0x228F: *ch = 0x2290; break; case 0x2290: *ch = 0x228F; break; case 0x2291: *ch = 0x2292; break; case 0x2292: *ch = 0x2291; break; case 0x2298: *ch = 0x29B8; break; case 0x22A2: *ch = 0x22A3; break; case 0x22A3: *ch = 0x22A2; break; case 0x22A6: *ch = 0x2ADE; break; case 0x22A8: *ch = 0x2AE4; break; case 0x22A9: *ch = 0x2AE3; break; case 0x22AB: *ch = 0x2AE5; break; case 0x22B0: *ch = 0x22B1; break; case 0x22B1: *ch = 0x22B0; break; case 0x22B2: *ch = 0x22B3; break; case 0x22B3: *ch = 0x22B2; break; case 0x22B4: *ch = 0x22B5; break; case 0x22B5: *ch = 0x22B4; break; case 0x22B6: *ch = 0x22B7; break; case 0x22B7: *ch = 0x22B6; break; case 0x22C9: *ch = 0x22CA; break; case 0x22CA: *ch = 0x22C9; break; case 0x22CB: *ch = 0x22CC; break; case 0x22CC: *ch = 0x22CB; break; case 0x22CD: *ch = 0x2243; break; case 0x22D0: *ch = 0x22D1; break; case 0x22D1: *ch = 0x22D0; break; case 0x22D6: *ch = 0x22D7; break; case 0x22D7: *ch = 0x22D6; break; case 0x22D8: *ch = 0x22D9; break; case 0x22D9: *ch = 0x22D8; break; case 0x22DA: *ch = 0x22DB; break; case 0x22DB: *ch = 0x22DA; break; case 0x22DC: *ch = 0x22DD; break; case 0x22DD: *ch = 0x22DC; break; case 0x22DE: *ch = 0x22DF; break; case 0x22DF: *ch = 0x22DE; break; case 0x22E0: *ch = 0x22E1; break; case 0x22E1: *ch = 0x22E0; break; case 0x22E2: *ch = 0x22E3; break; case 0x22E3: *ch = 0x22E2; break; case 0x22E4: *ch = 0x22E5; break; case 0x22E5: *ch = 0x22E4; break; case 0x22E6: *ch = 0x22E7; break; case 0x22E7: *ch = 0x22E6; break; case 0x22E8: *ch = 0x22E9; break; case 0x22E9: *ch = 0x22E8; break; case 0x22EA: *ch = 0x22EB; break; case 0x22EB: *ch = 0x22EA; break; case 0x22EC: *ch = 0x22ED; break; case 0x22ED: *ch = 0x22EC; break; case 0x22F0: *ch = 0x22F1; break; case 0x22F1: *ch = 0x22F0; break; case 0x22F2: *ch = 0x22FA; break; case 0x22F3: *ch = 0x22FB; break; case 0x22F4: *ch = 0x22FC; break; case 0x22F6: *ch = 0x22FD; break; case 0x22F7: *ch = 0x22FE; break; case 0x22FA: *ch = 0x22F2; break; case 0x22FB: *ch = 0x22F3; break; case 0x22FC: *ch = 0x22F4; break; case 0x22FD: *ch = 0x22F6; break; case 0x22FE: *ch = 0x22F7; break; } } else if ((*ch & 0xFF00) == 0x2300) { switch (*ch) { case 0x2308: *ch = 0x2309; break; case 0x2309: *ch = 0x2308; break; case 0x230A: *ch = 0x230B; break; case 0x230B: *ch = 0x230A; break; case 0x2329: *ch = 0x232A; break; case 0x232A: *ch = 0x2329; break; } } else if ((*ch & 0xFF00) == 0x2700) { switch (*ch) { case 0x2768: *ch = 0x2769; break; case 0x2769: *ch = 0x2768; break; case 0x276A: *ch = 0x276B; break; case 0x276B: *ch = 0x276A; break; case 0x276C: *ch = 0x276D; break; case 0x276D: *ch = 0x276C; break; case 0x276E: *ch = 0x276F; break; case 0x276F: *ch = 0x276E; break; case 0x2770: *ch = 0x2771; break; case 0x2771: *ch = 0x2770; break; case 0x2772: *ch = 0x2773; break; case 0x2773: *ch = 0x2772; break; case 0x2774: *ch = 0x2775; break; case 0x2775: *ch = 0x2774; break; case 0x27D5: *ch = 0x27D6; break; case 0x27D6: *ch = 0x27D5; break; case 0x27DD: *ch = 0x27DE; break; case 0x27DE: *ch = 0x27DD; break; case 0x27E2: *ch = 0x27E3; break; case 0x27E3: *ch = 0x27E2; break; case 0x27E4: *ch = 0x27E5; break; case 0x27E5: *ch = 0x27E4; break; case 0x27E6: *ch = 0x27E7; break; case 0x27E7: *ch = 0x27E6; break; case 0x27E8: *ch = 0x27E9; break; case 0x27E9: *ch = 0x27E8; break; case 0x27EA: *ch = 0x27EB; break; case 0x27EB: *ch = 0x27EA; break; } } else if ((*ch & 0xFF00) == 0x2900) { switch (*ch) { case 0x2983: *ch = 0x2984; break; case 0x2984: *ch = 0x2983; break; case 0x2985: *ch = 0x2986; break; case 0x2986: *ch = 0x2985; break; case 0x2987: *ch = 0x2988; break; case 0x2988: *ch = 0x2987; break; case 0x2989: *ch = 0x298A; break; case 0x298A: *ch = 0x2989; break; case 0x298B: *ch = 0x298C; break; case 0x298C: *ch = 0x298B; break; case 0x298D: *ch = 0x2990; break; case 0x298E: *ch = 0x298F; break; case 0x298F: *ch = 0x298E; break; case 0x2990: *ch = 0x298D; break; case 0x2991: *ch = 0x2992; break; case 0x2992: *ch = 0x2991; break; case 0x2993: *ch = 0x2994; break; case 0x2994: *ch = 0x2993; break; case 0x2995: *ch = 0x2996; break; case 0x2996: *ch = 0x2995; break; case 0x2997: *ch = 0x2998; break; case 0x2998: *ch = 0x2997; break; case 0x29B8: *ch = 0x2298; break; case 0x29C0: *ch = 0x29C1; break; case 0x29C1: *ch = 0x29C0; break; case 0x29C4: *ch = 0x29C5; break; case 0x29C5: *ch = 0x29C4; break; case 0x29CF: *ch = 0x29D0; break; case 0x29D0: *ch = 0x29CF; break; case 0x29D1: *ch = 0x29D2; break; case 0x29D2: *ch = 0x29D1; break; case 0x29D4: *ch = 0x29D5; break; case 0x29D5: *ch = 0x29D4; break; case 0x29D8: *ch = 0x29D9; break; case 0x29D9: *ch = 0x29D8; break; case 0x29DA: *ch = 0x29DB; break; case 0x29DB: *ch = 0x29DA; break; case 0x29F5: *ch = 0x2215; break; case 0x29F8: *ch = 0x29F9; break; case 0x29F9: *ch = 0x29F8; break; case 0x29FC: *ch = 0x29FD; break; case 0x29FD: *ch = 0x29FC; break; } } else if ((*ch & 0xFF00) == 0x2A00) { switch (*ch) { case 0x2A2B: *ch = 0x2A2C; break; case 0x2A2C: *ch = 0x2A2B; break; case 0x2A2D: *ch = 0x2A2C; break; case 0x2A2E: *ch = 0x2A2D; break; case 0x2A34: *ch = 0x2A35; break; case 0x2A35: *ch = 0x2A34; break; case 0x2A3C: *ch = 0x2A3D; break; case 0x2A3D: *ch = 0x2A3C; break; case 0x2A64: *ch = 0x2A65; break; case 0x2A65: *ch = 0x2A64; break; case 0x2A79: *ch = 0x2A7A; break; case 0x2A7A: *ch = 0x2A79; break; case 0x2A7D: *ch = 0x2A7E; break; case 0x2A7E: *ch = 0x2A7D; break; case 0x2A7F: *ch = 0x2A80; break; case 0x2A80: *ch = 0x2A7F; break; case 0x2A81: *ch = 0x2A82; break; case 0x2A82: *ch = 0x2A81; break; case 0x2A83: *ch = 0x2A84; break; case 0x2A84: *ch = 0x2A83; break; case 0x2A8B: *ch = 0x2A8C; break; case 0x2A8C: *ch = 0x2A8B; break; case 0x2A91: *ch = 0x2A92; break; case 0x2A92: *ch = 0x2A91; break; case 0x2A93: *ch = 0x2A94; break; case 0x2A94: *ch = 0x2A93; break; case 0x2A95: *ch = 0x2A96; break; case 0x2A96: *ch = 0x2A95; break; case 0x2A97: *ch = 0x2A98; break; case 0x2A98: *ch = 0x2A97; break; case 0x2A99: *ch = 0x2A9A; break; case 0x2A9A: *ch = 0x2A99; break; case 0x2A9B: *ch = 0x2A9C; break; case 0x2A9C: *ch = 0x2A9B; break; case 0x2AA1: *ch = 0x2AA2; break; case 0x2AA2: *ch = 0x2AA1; break; case 0x2AA6: *ch = 0x2AA7; break; case 0x2AA7: *ch = 0x2AA6; break; case 0x2AA8: *ch = 0x2AA9; break; case 0x2AA9: *ch = 0x2AA8; break; case 0x2AAA: *ch = 0x2AAB; break; case 0x2AAB: *ch = 0x2AAA; break; case 0x2AAC: *ch = 0x2AAD; break; case 0x2AAD: *ch = 0x2AAC; break; case 0x2AAF: *ch = 0x2AB0; break; case 0x2AB0: *ch = 0x2AAF; break; case 0x2AB3: *ch = 0x2AB4; break; case 0x2AB4: *ch = 0x2AB3; break; case 0x2ABB: *ch = 0x2ABC; break; case 0x2ABC: *ch = 0x2ABB; break; case 0x2ABD: *ch = 0x2ABE; break; case 0x2ABE: *ch = 0x2ABD; break; case 0x2ABF: *ch = 0x2AC0; break; case 0x2AC0: *ch = 0x2ABF; break; case 0x2AC1: *ch = 0x2AC2; break; case 0x2AC2: *ch = 0x2AC1; break; case 0x2AC3: *ch = 0x2AC4; break; case 0x2AC4: *ch = 0x2AC3; break; case 0x2AC5: *ch = 0x2AC6; break; case 0x2AC6: *ch = 0x2AC5; break; case 0x2ACD: *ch = 0x2ACE; break; case 0x2ACE: *ch = 0x2ACD; break; case 0x2ACF: *ch = 0x2AD0; break; case 0x2AD0: *ch = 0x2ACF; break; case 0x2AD1: *ch = 0x2AD2; break; case 0x2AD2: *ch = 0x2AD1; break; case 0x2AD3: *ch = 0x2AD4; break; case 0x2AD4: *ch = 0x2AD3; break; case 0x2AD5: *ch = 0x2AD6; break; case 0x2AD6: *ch = 0x2AD5; break; case 0x2ADE: *ch = 0x22A6; break; case 0x2AE3: *ch = 0x22A9; break; case 0x2AE4: *ch = 0x22A8; break; case 0x2AE5: *ch = 0x22AB; break; case 0x2AEC: *ch = 0x2AED; break; case 0x2AED: *ch = 0x2AEC; break; case 0x2AF7: *ch = 0x2AF8; break; case 0x2AF8: *ch = 0x2AF7; break; case 0x2AF9: *ch = 0x2AFA; break; case 0x2AFA: *ch = 0x2AF9; break; } } else if ((*ch & 0xFF00) == 0x3000) { switch (*ch) { case 0x3008: *ch = 0x3009; break; case 0x3009: *ch = 0x3008; break; case 0x300A: *ch = 0x300B; break; case 0x300B: *ch = 0x300A; break; case 0x300C: *ch = 0x300D; break; case 0x300D: *ch = 0x300C; break; case 0x300E: *ch = 0x300F; break; case 0x300F: *ch = 0x300E; break; case 0x3010: *ch = 0x3011; break; case 0x3011: *ch = 0x3010; break; case 0x3014: *ch = 0x3015; break; case 0x3015: *ch = 0x3014; break; case 0x3016: *ch = 0x3017; break; case 0x3017: *ch = 0x3016; break; case 0x3018: *ch = 0x3019; break; case 0x3019: *ch = 0x3018; break; case 0x301A: *ch = 0x301B; break; case 0x301B: *ch = 0x301A; break; } } else if ((*ch & 0xFF00) == 0xFF00) { switch (*ch) { case 0xFF08: *ch = 0xFF09; break; case 0xFF09: *ch = 0xFF08; break; case 0xFF1C: *ch = 0xFF1E; break; case 0xFF1E: *ch = 0xFF1C; break; case 0xFF3B: *ch = 0xFF3D; break; case 0xFF3D: *ch = 0xFF3B; break; case 0xFF5B: *ch = 0xFF5D; break; case 0xFF5D: *ch = 0xFF5B; break; case 0xFF5F: *ch = 0xFF60; break; case 0xFF60: *ch = 0xFF5F; break; case 0xFF62: *ch = 0xFF63; break; case 0xFF63: *ch = 0xFF62; break; } } } #ifdef TEST_GETTYPE #include #include int main(int argc, char **argv) { static const struct { int type; char *name; } typetoname[] = { #define TYPETONAME(X) { X , #X } TYPETONAME(L), TYPETONAME(LRE), TYPETONAME(LRO), TYPETONAME(R), TYPETONAME(AL), TYPETONAME(RLE), TYPETONAME(RLO), TYPETONAME(PDF), TYPETONAME(EN), TYPETONAME(ES), TYPETONAME(ET), TYPETONAME(AN), TYPETONAME(CS), TYPETONAME(NSM), TYPETONAME(BN), TYPETONAME(B), TYPETONAME(S), TYPETONAME(WS), TYPETONAME(ON), #undef TYPETONAME }; int i; for (i = 1; i < argc; i++) { unsigned long chr = strtoul(argv[i], NULL, 0); int type = getType(chr); assert(typetoname[type].type == type); printf("U+%04x: %s\n", (unsigned)chr, typetoname[type].name); } return 0; } #endif its-playback-time-0.2017-08-30.3c40fd3/misc.c000066400000000000000000001003631333656753000177510ustar00rootroot00000000000000/* * Platform-independent routines shared between all PuTTY programs. */ #include #include #include #include #include #include #include "putty.h" #include "misc.h" /* * 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. */ 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; } /* * 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 * specified by number (decimal, 0octal, 0xHEX) * ~ ^ escape */ 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; } /* * 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. * * We provide several variants of this function, with semantics like * various standard string.h functions. */ static const char *host_strchr_internal(const char *s, const char *set, int 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++; } } 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); } 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); } 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); } #ifdef TEST_HOST_STRFOO 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; } /* Stubs to stop the rest of this module causing compile failures. */ void modalfatalbox(const char *fmt, ...) {} int conf_get_int(Conf *conf, int primary) { return 0; } char *conf_get_str(Conf *conf, int primary) { return NULL; } #endif /* TEST_HOST_STRFOO */ /* * Trim square brackets off the outside of an IPv6 address literal. * Leave all other strings unchanged. Returns a fresh dynamically * allocated string. */ 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 == ']' && !p[1] && colons > 1) { /* * This looks like an IPv6 address literal (hex digits and * at least two colons, 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); } prompts_t *new_prompts(void *frontend) { prompts_t *p = snew(prompts_t); p->prompts = NULL; p->n_prompts = 0; p->frontend = frontend; 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, int echo) { prompt_t *pr = snew(prompt_t); pr->prompt = promptstr; pr->echo = echo; pr->result = NULL; pr->resultsize = 0; p->n_prompts++; p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *); p->prompts[p->n_prompts-1] = pr; } void prompt_ensure_result_size(prompt_t *pr, int newlen) { if ((int)pr->resultsize < newlen) { char *newbuf; newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */ /* * We don't use sresize / realloc here, because we will be * storing sensitive stuff like passwords in here, and we want * to make sure that the data doesn't get copied around in * memory without the old copy being destroyed. */ newbuf = snewn(newlen, char); memcpy(newbuf, pr->result, pr->resultsize); smemclr(pr->result, pr->resultsize); sfree(pr->result); pr->result = newbuf; pr->resultsize = newlen; } } void prompt_set_result(prompt_t *pr, const char *newstr) { prompt_ensure_result_size(pr, strlen(newstr) + 1); strcpy(pr->result, newstr); } void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; smemclr(pr->result, pr->resultsize); /* burn the evidence */ sfree(pr->result); sfree(pr->prompt); sfree(pr); } sfree(p->prompts); sfree(p->name); sfree(p->instruction); sfree(p); } /* ---------------------------------------------------------------------- * String handling routines. */ char *dupstr(const char *s) { char *p = NULL; if (s) { int len = strlen(s); p = snewn(len + 1, char); strcpy(p, s); } return p; } /* Allocate the concatenation of N strings. Terminate arg list with NULL. */ char *dupcat(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; } void burnstr(char *string) /* sfree(str), only clear it first */ { if (string) { smemclr(string, strlen(string)); sfree(string); } } int toint(unsigned u) { /* * Convert an unsigned to an int, without running into the * undefined behaviour which happens by the strict C standard if * the value overflows. You'd hope that sensible compilers would * do the sensible thing in response to a cast, but actually I * don't trust modern compilers not to do silly things like * assuming that _obviously_ you wouldn't have caused an overflow * and so they can elide an 'if (i < 0)' test immediately after * the cast. * * Sensible compilers ought of course to optimise this entire * function into 'just return the input value'! */ if (u <= (unsigned)INT_MAX) return (int)u; else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */ return INT_MIN + (int)(u - (unsigned)INT_MIN); else return INT_MIN; /* fallback; should never occur on binary machines */ } /* * 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 , 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. */ static char *dupvprintf_inner(char *buf, int oldlen, int *oldsize, const char *fmt, va_list ap) { int len, size, newsize; assert(*oldsize >= oldlen); size = *oldsize - oldlen; if (size == 0) { size = 512; newsize = oldlen + size; buf = sresize(buf, newsize, char); } else { newsize = *oldsize; } while (1) { #if defined _WINDOWS && !defined __WINE__ && _MSC_VER < 1900 /* 1900 == VS2015 has real snprintf */ #define vsnprintf _vsnprintf #endif #ifdef va_copy /* Use the `va_copy' macro mandated by C99, if present. * XXX some environments may have this as __va_copy() */ va_list aq; va_copy(aq, ap); len = vsnprintf(buf + oldlen, size, fmt, aq); va_end(aq); #else /* Ugh. No va_copy macro, so do something nasty. * Technically, you can't reuse a va_list like this: it is left * unspecified whether advancing a va_list pointer modifies its * value or something it points to, so on some platforms calling * vsnprintf twice on the same va_list might fail hideously * (indeed, it has been observed to). * XXX the autoconf manual suggests that using memcpy() will give * "maximum portability". */ len = vsnprintf(buf + oldlen, size, fmt, ap); #endif if (len >= 0 && len < size) { /* This is the C99-specified criterion for snprintf to have * been completely successful. */ *oldsize = newsize; return buf; } else if (len > 0) { /* This is the C99 error condition: the returned length is * the required buffer size not counting the NUL. */ size = len + 1; } 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. */ size += 512; } newsize = oldlen + size; buf = sresize(buf, newsize, char); } } char *dupvprintf(const char *fmt, va_list ap) { int 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; } struct strbuf { char *s; int len, size; }; strbuf *strbuf_new(void) { strbuf *buf = snew(strbuf); buf->len = 0; buf->size = 512; buf->s = snewn(buf->size, char); *buf->s = '\0'; return buf; } void strbuf_free(strbuf *buf) { sfree(buf->s); sfree(buf); } char *strbuf_str(strbuf *buf) { return buf->s; } char *strbuf_to_str(strbuf *buf) { char *ret = buf->s; sfree(buf); return ret; } void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap) { buf->s = dupvprintf_inner(buf->s, buf->len, &buf->size, fmt, ap); buf->len += strlen(buf->s + buf->len); } void strbuf_catf(strbuf *buf, const char *fmt, ...) { va_list ap; va_start(ap, fmt); strbuf_catfv(buf, fmt, ap); va_end(ap); } /* * Read an entire line of text from a file. Return a buffer * malloced to be as big as necessary (caller must free). */ char *fgetline(FILE *fp) { char *ret = snewn(512, char); int 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 */ size = len + 512; ret = sresize(ret, size, char); } if (len == 0) { /* first fgets returned NULL */ sfree(ret); return NULL; } ret[len] = '\0'; return ret; } /* * 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. */ 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; } /* ---------------------------------------------------------------------- * Core base64 encoding and decoding routines. */ 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] = '='; } 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; } /* ---------------------------------------------------------------------- * 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 */ #define BUFFER_MIN_GRANULE 512 struct bufchain_granule { struct bufchain_granule *next; char *bufpos, *bufend, *bufmax; }; void bufchain_init(bufchain *ch) { ch->head = ch->tail = NULL; ch->buffersize = 0; } void bufchain_clear(bufchain *ch) { struct bufchain_granule *b; while (ch->head) { b = ch->head; ch->head = ch->head->next; sfree(b); } ch->tail = NULL; ch->buffersize = 0; } int bufchain_size(bufchain *ch) { return ch->buffersize; } void bufchain_add(bufchain *ch, const void *data, int 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) { int 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) { int 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; } } } void bufchain_consume(bufchain *ch, int 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; sfree(tmp); } else ch->head->bufpos += remlen; ch->buffersize -= remlen; len -= remlen; } } void bufchain_prefix(bufchain *ch, void **data, int *len) { *len = ch->head->bufend - ch->head->bufpos; *data = ch->head->bufpos; } void bufchain_fetch(bufchain *ch, void *data, int 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; } } /* ---------------------------------------------------------------------- * My own versions of malloc, realloc and free. Because I want * malloc and realloc to bomb out and exit the program if they run * out of memory, realloc to reliably call malloc if passed a NULL * pointer, and free to reliably do nothing if passed a NULL * pointer. We can also put trace printouts in, if we need to; and * we can also replace the allocator with an ElectricFence-like * one. */ #ifdef MINEFIELD void *minefield_c_malloc(size_t size); void minefield_c_free(void *p); void *minefield_c_realloc(void *p, size_t size); #endif #ifdef MALLOC_LOG static FILE *fp = NULL; static char *mlog_file = NULL; static int mlog_line = 0; void mlog(char *file, int line) { mlog_file = file; mlog_line = line; if (!fp) { fp = fopen("putty_mem.log", "w"); setvbuf(fp, NULL, _IONBF, BUFSIZ); } if (fp) fprintf(fp, "%s:%d: ", file, line); } #endif void *safemalloc(size_t n, size_t size) { void *p; if (n > INT_MAX / size) { p = NULL; } else { size *= n; if (size == 0) size = 1; #ifdef MINEFIELD p = minefield_c_malloc(size); #else p = malloc(size); #endif } if (!p) { char str[200]; #ifdef MALLOC_LOG sprintf(str, "Out of memory! (%s:%d, size=%d)", mlog_file, mlog_line, size); fprintf(fp, "*** %s\n", str); fclose(fp); #else strcpy(str, "Out of memory!"); #endif modalfatalbox("%s", str); } #ifdef MALLOC_LOG if (fp) fprintf(fp, "malloc(%d) returns %p\n", size, p); #endif return p; } 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) { char str[200]; #ifdef MALLOC_LOG sprintf(str, "Out of memory! (%s:%d, size=%d)", mlog_file, mlog_line, size); fprintf(fp, "*** %s\n", str); fclose(fp); #else strcpy(str, "Out of memory!"); #endif modalfatalbox("%s", str); } #ifdef MALLOC_LOG if (fp) fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p); #endif return p; } void safefree(void *ptr) { if (ptr) { #ifdef MALLOC_LOG if (fp) fprintf(fp, "free(%p)\n", ptr); #endif #ifdef MINEFIELD minefield_c_free(ptr); #else free(ptr); #endif } #ifdef MALLOC_LOG else if (fp) fprintf(fp, "freeing null pointer - no action taken\n"); #endif } /* ---------------------------------------------------------------------- * Debugging routines. */ #ifdef DEBUG extern void dputs(const char *); /* defined in per-platform *misc.c */ 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, int 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%02.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); } } #endif /* def DEBUG */ /* * Determine whether or not a Conf represents a session which can * sensibly be launched right now. */ int 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; } 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); } #ifndef PLATFORM_HAS_SMEMCLR /* * 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. */ 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++; } } #endif /* * 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. */ int 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 (strlen(q) == 16*3 - 1 && q[strspn(q, "0123456789abcdefABCDEF:")] == 0) { /* * Might be a 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 (q[3*i] == ':' || q[3*i+1] == ':') goto not_fingerprint; /* sorry */ for (i = 0; i < 15; i++) if (q[3*i+2] != ':') goto not_fingerprint; /* sorry */ for (i = 0; i < 16*3 - 1; i++) key[i] = tolower(q[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, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz+/=")] == 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; } int 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; } int match_ssh_id(int stringlen, const void *string, const char *id) { int idlen = strlen(id); return (idlen == stringlen && !memcmp(string, id, idlen)); } void *get_ssh_string(int *datalen, const void **data, int *stringlen) { void *ret; unsigned int len; if (*datalen < 4) return NULL; len = GET_32BIT_MSB_FIRST((const unsigned char *)*data); if (*datalen - 4 < len) return NULL; ret = (void *)((const char *)*data + 4); *datalen -= len + 4; *data = (const char *)*data + len + 4; *stringlen = len; return ret; } int get_ssh_uint32(int *datalen, const void **data, unsigned *ret) { if (*datalen < 4) return FALSE; *ret = GET_32BIT_MSB_FIRST((const unsigned char *)*data); *datalen -= 4; *data = (const char *)*data + 4; return TRUE; } int strstartswith(const char *s, const char *t) { return !memcmp(s, t, strlen(t)); } int strendswith(const char *s, const char *t) { size_t slen = strlen(s), tlen = strlen(t); return slen >= tlen && !strcmp(s + (slen - tlen), t); } char *buildinfo(const char *newline) { strbuf *buf = strbuf_new(); extern const char commitid[]; /* in commitid.c */ 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", newline); #if _MSC_VER == 1900 strbuf_catf(buf, " 2015 / MSVC++ 14.0"); #elif _MSC_VER == 1800 strbuf_catf(buf, " 2013 / MSVC++ 12.0"); #elif _MSC_VER == 1700 strbuf_catf(buf, " 2012 / MSVC++ 11.0"); #elif _MSC_VER == 1600 strbuf_catf(buf, " 2010 / MSVC++ 10.0"); #elif _MSC_VER == 1500 strbuf_catf(buf, " 2008 / MSVC++ 9.0"); #elif _MSC_VER == 1400 strbuf_catf(buf, " 2005 / MSVC++ 8.0"); #elif _MSC_VER == 1310 strbuf_catf(buf, " 2003 / MSVC++ 7.1"); #elif _MSC_VER == 1300 strbuf_catf(buf, " 2003 / MSVC++ 7.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 && defined MINEFIELD strbuf_catf(buf, "%sBuild option: MINEFIELD", newline); #endif #ifdef NO_SECURITY strbuf_catf(buf, "%sBuild option: NO_SECURITY", 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); } its-playback-time-0.2017-08-30.3c40fd3/misc.h000066400000000000000000000156241333656753000177630ustar00rootroot00000000000000/* * Header for misc.c. */ #ifndef PUTTY_MISC_H #define PUTTY_MISC_H #include "puttymem.h" #include /* for FILE * */ #include /* for va_list */ #include /* for struct tm */ #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif typedef struct Filename Filename; typedef struct FontSpec FontSpec; unsigned long parse_blocksize(const char *bs); char ctrlparse(char *s, char **next); size_t host_strcspn(const char *s, const char *set); char *host_strchr(const char *s, int c); char *host_strrchr(const char *s, int c); char *host_strduptrim(const char *s); char *dupstr(const char *s); char *dupcat(const char *s1, ...); char *dupprintf(const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) #endif ; char *dupvprintf(const char *fmt, va_list ap); void burnstr(char *string); typedef struct strbuf strbuf; strbuf *strbuf_new(void); void strbuf_free(strbuf *buf); char *strbuf_str(strbuf *buf); /* does not free buf */ char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */ void strbuf_catf(strbuf *buf, const char *fmt, ...); void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap); /* String-to-Unicode converters that auto-allocate the destination and * work around the rather deficient interface of mb_to_wc. * * These actually live in miscucs.c, not misc.c (the distinction being * that the former is only linked into tools that also have the main * Unicode support). */ wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len); wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string); int toint(unsigned); char *fgetline(FILE *fp); char *chomp(char *str); int strstartswith(const char *s, const char *t); int strendswith(const char *s, const char *t); void base64_encode_atom(const unsigned char *data, int n, char *out); int base64_decode_atom(const char *atom, unsigned char *out); struct bufchain_granule; struct bufchain_tag { struct bufchain_granule *head, *tail; int buffersize; /* current amount of buffered data */ }; #ifndef BUFCHAIN_TYPEDEF typedef struct bufchain_tag bufchain; /* rest of declaration in misc.c */ #define BUFCHAIN_TYPEDEF #endif void bufchain_init(bufchain *ch); void bufchain_clear(bufchain *ch); int bufchain_size(bufchain *ch); void bufchain_add(bufchain *ch, const void *data, int len); void bufchain_prefix(bufchain *ch, void **data, int *len); void bufchain_consume(bufchain *ch, int len); void bufchain_fetch(bufchain *ch, void *data, int len); int validate_manual_hostkey(char *key); struct tm ltime(void); /* Wipe sensitive data out of memory that's about to be freed. Simpler * than memset because we don't need the fill char parameter; also * attempts (by fiddly use of volatile) to inhibit the compiler from * over-cleverly trying to optimise the memset away because it knows * the variable is going out of scope. */ void smemclr(void *b, size_t len); /* Compare two fixed-length chunks of memory for equality, without * data-dependent control flow (so an attacker with a very accurate * stopwatch can't try to guess where the first mismatching byte was). * Returns 0 for mismatch or 1 for equality (unlike memcmp), hinted at * by the 'eq' in the name. */ int smemeq(const void *av, const void *bv, size_t len); /* Extracts an SSH-marshalled string from the start of *data. If * successful (*datalen is not too small), advances data/datalen past * the string and returns a pointer to the string itself and its * length in *stringlen. Otherwise does nothing and returns NULL. * * Like strchr, this function can discard const from its parameter. * Treat it as if it was a family of two functions, one returning a * non-const string given a non-const pointer, and one taking and * returning const. */ void *get_ssh_string(int *datalen, const void **data, int *stringlen); /* Extracts an SSH uint32, similarly. Returns TRUE on success, and * leaves the extracted value in *ret. */ int get_ssh_uint32(int *datalen, const void **data, unsigned *ret); /* Given a not-necessarily-zero-terminated string in (length,data) * form, check if it equals an ordinary C zero-terminated string. */ int match_ssh_id(int stringlen, const void *string, const char *id); char *buildinfo(const char *newline); /* * Debugging functions. * * Output goes to debug.log * * debug(()) (note the double brackets) is like printf(). * * dmemdump() and dmemdumpl() both do memory dumps. The difference * is that dmemdumpl() is more suited for when the memory address is * important (say because you'll be recording pointer values later * on). dmemdump() is more concise. */ #ifdef DEBUG void debug_printf(const char *fmt, ...); void debug_memdump(const void *buf, int len, int L); #define debug(x) (debug_printf x) #define dmemdump(buf,len) debug_memdump (buf, len, 0); #define dmemdumpl(buf,len) debug_memdump (buf, len, 1); #else #define debug(x) #define dmemdump(buf,len) #define dmemdumpl(buf,len) #endif #ifndef lenof #define lenof(x) ( (sizeof((x))) / (sizeof(*(x)))) #endif #ifndef min #define min(x,y) ( (x) < (y) ? (x) : (y) ) #endif #ifndef max #define max(x,y) ( (x) > (y) ? (x) : (y) ) #endif #define GET_32BIT_LSB_FIRST(cp) \ (((unsigned long)(unsigned char)(cp)[0]) | \ ((unsigned long)(unsigned char)(cp)[1] << 8) | \ ((unsigned long)(unsigned char)(cp)[2] << 16) | \ ((unsigned long)(unsigned char)(cp)[3] << 24)) #define PUT_32BIT_LSB_FIRST(cp, value) ( \ (cp)[0] = (unsigned char)(value), \ (cp)[1] = (unsigned char)((value) >> 8), \ (cp)[2] = (unsigned char)((value) >> 16), \ (cp)[3] = (unsigned char)((value) >> 24) ) #define GET_16BIT_LSB_FIRST(cp) \ (((unsigned long)(unsigned char)(cp)[0]) | \ ((unsigned long)(unsigned char)(cp)[1] << 8)) #define PUT_16BIT_LSB_FIRST(cp, value) ( \ (cp)[0] = (unsigned char)(value), \ (cp)[1] = (unsigned char)((value) >> 8) ) #define GET_32BIT_MSB_FIRST(cp) \ (((unsigned long)(unsigned char)(cp)[0] << 24) | \ ((unsigned long)(unsigned char)(cp)[1] << 16) | \ ((unsigned long)(unsigned char)(cp)[2] << 8) | \ ((unsigned long)(unsigned char)(cp)[3])) #define GET_32BIT(cp) GET_32BIT_MSB_FIRST(cp) #define PUT_32BIT_MSB_FIRST(cp, value) ( \ (cp)[0] = (unsigned char)((value) >> 24), \ (cp)[1] = (unsigned char)((value) >> 16), \ (cp)[2] = (unsigned char)((value) >> 8), \ (cp)[3] = (unsigned char)(value) ) #define PUT_32BIT(cp, value) PUT_32BIT_MSB_FIRST(cp, value) #define GET_16BIT_MSB_FIRST(cp) \ (((unsigned long)(unsigned char)(cp)[0] << 8) | \ ((unsigned long)(unsigned char)(cp)[1])) #define PUT_16BIT_MSB_FIRST(cp, value) ( \ (cp)[0] = (unsigned char)((value) >> 8), \ (cp)[1] = (unsigned char)(value) ) /* Replace NULL with the empty string, permitting an idiom in which we * get a string (pointer,length) pair that might be NULL,0 and can * then safely say things like printf("%.*s", length, NULLTOEMPTY(ptr)) */ #define NULLTOEMPTY(s) ((s)?(s):"") #endif its-playback-time-0.2017-08-30.3c40fd3/miscucs.c000066400000000000000000000013511333656753000204610ustar00rootroot00000000000000/* * 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)); } its-playback-time-0.2017-08-30.3c40fd3/mkauto.sh000077500000000000000000000000611333656753000205030ustar00rootroot00000000000000#!/bin/sh autoreconf -i && rm -rf autom4te.cache its-playback-time-0.2017-08-30.3c40fd3/network.h000066400000000000000000000226271333656753000205220ustar00rootroot00000000000000/* * Networking abstraction in PuTTY. * * The way this works is: a back end can choose to open any number * of sockets - including zero, which might be necessary in some. * It can register a bunch of callbacks (most notably for when * data is received) for each socket, and it can call the networking * abstraction to send data without having to worry about blocking. * The stuff behind the abstraction takes care of selects and * nonblocking writes and all that sort of painful gubbins. */ #ifndef PUTTY_NETWORK_H #define PUTTY_NETWORK_H #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif typedef struct SockAddr_tag *SockAddr; /* pay attention to levels of indirection */ typedef struct socket_function_table **Socket; typedef struct plug_function_table **Plug; struct socket_function_table { Plug(*plug) (Socket s, Plug p); /* use a different plug (return the old one) */ /* if p is NULL, it doesn't change the plug */ /* but it does return the one it's using */ void (*close) (Socket s); int (*write) (Socket s, const char *data, int len); int (*write_oob) (Socket s, const char *data, int len); void (*write_eof) (Socket s); void (*flush) (Socket s); void (*set_frozen) (Socket s, int is_frozen); /* ignored by tcp, but vital for ssl */ const char *(*socket_error) (Socket s); char *(*peer_info) (Socket s); }; typedef union { void *p; int i; } accept_ctx_t; typedef Socket (*accept_fn_t)(accept_ctx_t ctx, Plug plug); struct plug_function_table { void (*log)(Plug p, int type, SockAddr addr, int port, const char *error_msg, int error_code); /* * Passes the client progress reports on the process of setting * up the connection. * * - type==0 means we are about to try to connect to address * `addr' (error_msg and error_code are ignored) * - type==1 means we have failed to connect to address `addr' * (error_msg and error_code are supplied). This is not a * fatal error - we may well have other candidate addresses * to fall back to. When it _is_ fatal, the closing() * function will be called. * - type==2 means that error_msg contains a line of generic * logging information about setting up the connection. This * will typically be a wodge of standard-error output from a * proxy command, so the receiver should probably prefix it to * indicate this. */ void (*closing) (Plug p, const char *error_msg, int error_code, int calling_back); /* error_msg is NULL iff it is not an error (ie it closed normally) */ /* calling_back != 0 iff there is a Plug function */ /* currently running (would cure the fixme in try_send()) */ void (*receive) (Plug p, int urgent, char *data, int len); /* * - urgent==0. `data' points to `len' bytes of perfectly * ordinary data. * * - urgent==1. `data' points to `len' bytes of data, * which were read from before an Urgent pointer. * * - urgent==2. `data' points to `len' bytes of data, * the first of which was the one at the Urgent mark. */ void (*sent) (Plug p, int bufsize); /* * The `sent' function is called when the pending send backlog * on a socket is cleared or partially cleared. The new backlog * size is passed in the `bufsize' parameter. */ int (*accepting)(Plug p, accept_fn_t constructor, accept_ctx_t ctx); /* * `accepting' is called only on listener-type sockets, and is * passed a constructor function+context that will create a fresh * Socket describing the connection. It returns nonzero if it * doesn't want the connection for some reason, or 0 on success. */ }; /* proxy indirection layer */ /* NB, control of 'addr' is passed via new_connection, which takes * responsibility for freeing it */ Socket new_connection(SockAddr addr, const char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf); Socket new_listener(const char *srcaddr, int port, Plug plug, int local_host_only, Conf *conf, int addressfamily); SockAddr name_lookup(const char *host, int port, char **canonicalname, Conf *conf, int addressfamily, void *frontend_for_logging, const char *lookup_reason_for_logging); int proxy_for_destination (SockAddr addr, const char *hostname, int port, Conf *conf); /* platform-dependent callback from new_connection() */ /* (same caveat about addr as new_connection()) */ Socket platform_new_connection(SockAddr addr, const char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug plug, Conf *conf); /* socket functions */ void sk_init(void); /* called once at program startup */ void sk_cleanup(void); /* called just before program exit */ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family); SockAddr sk_nonamelookup(const char *host); void sk_getaddr(SockAddr addr, char *buf, int buflen); int sk_addr_needs_port(SockAddr addr); int sk_hostname_is_local(const char *name); int sk_address_is_local(SockAddr addr); int sk_address_is_special_local(SockAddr addr); int sk_addrtype(SockAddr addr); void sk_addrcopy(SockAddr addr, char *buf); void sk_addr_free(SockAddr addr); /* sk_addr_dup generates another SockAddr which contains the same data * as the original one and can be freed independently. May not actually * physically _duplicate_ it: incrementing a reference count so that * one more free is required before it disappears is an acceptable * implementation. */ SockAddr sk_addr_dup(SockAddr addr); /* NB, control of 'addr' is passed via sk_new, which takes responsibility * for freeing it, as for new_connection() */ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, int nodelay, int keepalive, Plug p); Socket sk_newlistener(const char *srcaddr, int port, Plug plug, int local_host_only, int address_family); #define sk_plug(s,p) (((*s)->plug) (s, p)) #define sk_close(s) (((*s)->close) (s)) #define sk_write(s,buf,len) (((*s)->write) (s, buf, len)) #define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len)) #define sk_write_eof(s) (((*s)->write_eof) (s)) #define sk_flush(s) (((*s)->flush) (s)) #ifdef DEFINE_PLUG_METHOD_MACROS #define plug_log(p,type,addr,port,msg,code) (((*p)->log) (p, type, addr, port, msg, code)) #define plug_closing(p,msg,code,callback) (((*p)->closing) (p, msg, code, callback)) #define plug_receive(p,urgent,buf,len) (((*p)->receive) (p, urgent, buf, len)) #define plug_sent(p,bufsize) (((*p)->sent) (p, bufsize)) #define plug_accepting(p, constructor, ctx) (((*p)->accepting)(p, constructor, ctx)) #endif /* * Special error values are returned from sk_namelookup and sk_new * if there's a problem. These functions extract an error message, * or return NULL if there's no problem. */ const char *sk_addr_error(SockAddr addr); #define sk_socket_error(s) (((*s)->socket_error) (s)) /* * Set the `frozen' flag on a socket. A frozen socket is one in * which all READABLE notifications are ignored, so that data is * not accepted from the peer until the socket is unfrozen. This * exists for two purposes: * * - Port forwarding: when a local listening port receives a * connection, we do not want to receive data from the new * socket until we have somewhere to send it. Hence, we freeze * the socket until its associated SSH channel is ready; then we * unfreeze it and pending data is delivered. * * - Socket buffering: if an SSH channel (or the whole connection) * backs up or presents a zero window, we must freeze the * associated local socket in order to avoid unbounded buffer * growth. */ #define sk_set_frozen(s, is_frozen) (((*s)->set_frozen) (s, is_frozen)) /* * Return a (dynamically allocated) string giving some information * about the other end of the socket, suitable for putting in log * files. May be NULL if nothing is available at all. */ #define sk_peer_info(s) (((*s)->peer_info) (s)) /* * Simple wrapper on getservbyname(), needed by ssh.c. Returns the * port number, in host byte order (suitable for printf and so on). * Returns 0 on failure. Any platform not supporting getservbyname * can just return 0 - this function is not required to handle * numeric port specifications. */ int net_service_lookup(char *service); /* * Look up the local hostname; return value needs freeing. * May return NULL. */ char *get_hostname(void); /* * Trivial socket implementation which just stores an error. Found in * errsock.c. */ Socket new_error_socket(const char *errmsg, Plug plug); /* ---------------------------------------------------------------------- * Functions defined outside the network code, which have to be * declared in this header file rather than the main putty.h because * they use types defined here. */ /* * Exports from be_misc.c. */ void backend_socket_log(void *frontend, int type, SockAddr addr, int port, const char *error_msg, int error_code, Conf *conf, int session_started); #ifndef BUFCHAIN_TYPEDEF typedef struct bufchain_tag bufchain; /* rest of declaration in misc.c */ #define BUFCHAIN_TYPEDEF #endif void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len); #endif its-playback-time-0.2017-08-30.3c40fd3/nogss.c000066400000000000000000000005041333656753000201430ustar00rootroot00000000000000/* * Stub definitions of the GSSAPI library list, for Unix pterm and * any other application that needs the symbols defined but has no * use for them. */ #include "putty.h" const int ngsslibs = 0; const char *const gsslibnames[1] = { "dummy" }; const struct keyvalwhere gsslibkeywords[1] = { { "dummy", 0, -1, -1 } }; its-playback-time-0.2017-08-30.3c40fd3/noprint.c000066400000000000000000000011561333656753000205070ustar00rootroot00000000000000/* * Stub implementation of the printing interface for PuTTY, for the * benefit of non-printing terminal applications. */ #include #include #include "putty.h" struct printer_job_tag { int dummy; }; printer_job *printer_start_job(char *printer) { return NULL; } void printer_job_data(printer_job *pj, void *data, int len) { } void printer_finish_job(printer_job *pj) { } printer_enum *printer_start_enum(int *nprinters_ptr) { *nprinters_ptr = 0; return NULL; } char *printer_get_name(printer_enum *pe, int i) { return NULL; } void printer_finish_enum(printer_enum *pe) { } its-playback-time-0.2017-08-30.3c40fd3/notiming.c000066400000000000000000000011211333656753000206320ustar00rootroot00000000000000/* * notiming.c: stub version of timing API. * * Used in any tool which needs a subsystem linked against the * timing API but doesn't want to actually provide timing. For * example, key generation tools need the random number generator, * but they don't want the hassle of calling noise_regular() at * regular intervals - and they don't _need_ it either, since they * have their own rigorous and different means of noise collection. */ #include "putty.h" unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx) { return 0; } void expire_timer_context(void *ctx) { } its-playback-time-0.2017-08-30.3c40fd3/putty.h000066400000000000000000001565761333656753000202310ustar00rootroot00000000000000#ifndef PUTTY_PUTTY_H #define PUTTY_PUTTY_H #include /* for wchar_t */ /* * Global variables. Most modules declare these `extern', but * window.c will do `#define PUTTY_DO_GLOBALS' before including this * module, and so will get them properly defined. */ #ifndef GLOBAL #ifdef PUTTY_DO_GLOBALS #define GLOBAL #else #define GLOBAL extern #endif #endif #ifndef DONE_TYPEDEFS #define DONE_TYPEDEFS typedef struct conf_tag Conf; typedef struct backend_tag Backend; typedef struct terminal_tag Terminal; #endif #include "puttyps.h" #include "network.h" #include "misc.h" /* * Fingerprints of the PGP master keys that can be used to establish a trust * path between an executable and other files. */ #define PGP_MASTER_KEY_FP \ "440D E3B5 B7A1 CA85 B3CC 1718 AB58 5DC6 0467 6F7C" #define PGP_RSA_MASTER_KEY_FP \ "8F 15 97 DA 25 30 AB 0D 88 D1 92 54 11 CF 0C 4C" #define PGP_DSA_MASTER_KEY_FP \ "313C 3E76 4B74 C2C5 F2AE 83A8 4F5E 6DF5 6A93 B34E" /* Three attribute types: * The ATTRs (normal attributes) are stored with the characters in * the main display arrays * * The TATTRs (temporary attributes) are generated on the fly, they * can overlap with characters but not with normal attributes. * * The LATTRs (line attributes) are an entirely disjoint space of * flags. * * The DATTRs (display attributes) are internal to terminal.c (but * defined here because their values have to match the others * here); they reuse the TATTR_* space but are always masked off * before sending to the front end. * * ATTR_INVALID is an illegal colour combination. */ #define TATTR_ACTCURS 0x40000000UL /* active cursor (block) */ #define TATTR_PASCURS 0x20000000UL /* passive cursor (box) */ #define TATTR_RIGHTCURS 0x10000000UL /* cursor-on-RHS */ #define TATTR_COMBINING 0x80000000UL /* combining characters */ #define DATTR_STARTRUN 0x80000000UL /* start of redraw run */ #define TDATTR_MASK 0xF0000000UL #define TATTR_MASK (TDATTR_MASK) #define DATTR_MASK (TDATTR_MASK) #define LATTR_NORM 0x00000000UL #define LATTR_WIDE 0x00000001UL #define LATTR_TOP 0x00000002UL #define LATTR_BOT 0x00000003UL #define LATTR_MODE 0x00000003UL #define LATTR_WRAPPED 0x00000010UL /* this line wraps to next */ #define LATTR_WRAPPED2 0x00000020UL /* with WRAPPED: CJK wide character wrapped to next line, so last single-width cell is empty */ #define ATTR_INVALID 0x03FFFFU /* Like Linux use the F000 page for direct to font. */ #define CSET_OEMCP 0x0000F000UL /* OEM Codepage DTF */ #define CSET_ACP 0x0000F100UL /* Ansi Codepage DTF */ /* These are internal use overlapping with the UTF-16 surrogates */ #define CSET_ASCII 0x0000D800UL /* normal ASCII charset ESC ( B */ #define CSET_LINEDRW 0x0000D900UL /* line drawing charset ESC ( 0 */ #define CSET_SCOACS 0x0000DA00UL /* SCO Alternate charset */ #define CSET_GBCHR 0x0000DB00UL /* UK variant charset ESC ( A */ #define CSET_MASK 0xFFFFFF00UL /* Character set mask */ #define DIRECT_CHAR(c) ((c&0xFFFFFC00)==0xD800) #define DIRECT_FONT(c) ((c&0xFFFFFE00)==0xF000) #define UCSERR (CSET_LINEDRW|'a') /* UCS Format error character. */ /* * UCSWIDE is a special value used in the terminal data to signify * the character cell containing the right-hand half of a CJK wide * character. We use 0xDFFF because it's part of the surrogate * range and hence won't be used for anything else (it's impossible * to input it via UTF-8 because our UTF-8 decoder correctly * rejects surrogates). */ #define UCSWIDE 0xDFFF #define ATTR_NARROW 0x800000U #define ATTR_WIDE 0x400000U #define ATTR_BOLD 0x040000U #define ATTR_UNDER 0x080000U #define ATTR_REVERSE 0x100000U #define ATTR_BLINK 0x200000U #define ATTR_FGMASK 0x0001FFU #define ATTR_BGMASK 0x03FE00U #define ATTR_COLOURS 0x03FFFFU #define ATTR_FGSHIFT 0 #define ATTR_BGSHIFT 9 /* * The definitive list of colour numbers stored in terminal * attribute words is kept here. It is: * * - 0-7 are ANSI colours (KRGYBMCW). * - 8-15 are the bold versions of those colours. * - 16-255 are the remains of the xterm 256-colour mode (a * 216-colour cube with R at most significant and B at least, * followed by a uniform series of grey shades running between * black and white but not including either on grounds of * redundancy). * - 256 is default foreground * - 257 is default bold foreground * - 258 is default background * - 259 is default bold background * - 260 is cursor foreground * - 261 is cursor background */ #define ATTR_DEFFG (256 << ATTR_FGSHIFT) #define ATTR_DEFBG (258 << ATTR_BGSHIFT) #define ATTR_DEFAULT (ATTR_DEFFG | ATTR_DEFBG) struct sesslist { int nsessions; const char **sessions; char *buffer; /* so memory can be freed later */ }; struct unicode_data { char **uni_tbl; int dbcs_screenfont; int font_codepage; int line_codepage; wchar_t unitab_scoacs[256]; wchar_t unitab_line[256]; wchar_t unitab_font[256]; wchar_t unitab_xterm[256]; wchar_t unitab_oemcp[256]; unsigned char unitab_ctrl[256]; }; #define LGXF_OVR 1 /* existing logfile overwrite */ #define LGXF_APN 0 /* existing logfile append */ #define LGXF_ASK -1 /* existing logfile ask */ #define LGTYP_NONE 0 /* logmode: no logging */ #define LGTYP_ASCII 1 /* logmode: pure ascii */ #define LGTYP_DEBUG 2 /* logmode: all chars of traffic */ #define LGTYP_PACKETS 3 /* logmode: SSH data packets */ #define LGTYP_SSHRAW 4 /* logmode: SSH raw data */ typedef enum { /* Actual special commands. Originally Telnet, but some codes have * been re-used for similar specials in other protocols. */ TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT, TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING, TS_EOL, /* Special command for SSH. */ TS_REKEY, /* POSIX-style signals. (not Telnet) */ TS_SIGABRT, TS_SIGALRM, TS_SIGFPE, TS_SIGHUP, TS_SIGILL, TS_SIGINT, TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV, TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2, /* Pseudo-specials used for constructing the specials menu. */ TS_SEP, /* Separator */ TS_SUBMENU, /* Start a new submenu with specified name */ TS_EXITMENU, /* Exit current submenu or end of specials */ /* Starting point for protocols to invent special-action codes * that can't live in this enum at all, e.g. because they change * with every session. * * Of course, this must remain the last value in this * enumeration. */ TS_LOCALSTART } Telnet_Special; struct telnet_special { const char *name; int code; }; typedef enum { MBT_NOTHING, MBT_LEFT, MBT_MIDDLE, MBT_RIGHT, /* `raw' button designations */ MBT_SELECT, MBT_EXTEND, MBT_PASTE, /* `cooked' button designations */ MBT_WHEEL_UP, MBT_WHEEL_DOWN /* mouse wheel */ } Mouse_Button; typedef enum { MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE } Mouse_Action; /* Keyboard modifiers -- keys the user is actually holding down */ #define PKM_SHIFT 0x01 #define PKM_CONTROL 0x02 #define PKM_META 0x04 #define PKM_ALT 0x08 /* Keyboard flags that aren't really modifiers */ #define PKF_CAPSLOCK 0x10 #define PKF_NUMLOCK 0x20 #define PKF_REPEAT 0x40 /* Stand-alone keysyms for function keys */ typedef enum { PK_NULL, /* No symbol for this key */ /* Main keypad keys */ PK_ESCAPE, PK_TAB, PK_BACKSPACE, PK_RETURN, PK_COMPOSE, /* Editing keys */ PK_HOME, PK_INSERT, PK_DELETE, PK_END, PK_PAGEUP, PK_PAGEDOWN, /* Cursor keys */ PK_UP, PK_DOWN, PK_RIGHT, PK_LEFT, PK_REST, /* Numeric keypad */ /* Real one looks like: */ PK_PF1, PK_PF2, PK_PF3, PK_PF4, /* PF1 PF2 PF3 PF4 */ PK_KPCOMMA, PK_KPMINUS, PK_KPDECIMAL, /* 7 8 9 - */ PK_KP0, PK_KP1, PK_KP2, PK_KP3, PK_KP4, /* 4 5 6 , */ PK_KP5, PK_KP6, PK_KP7, PK_KP8, PK_KP9, /* 1 2 3 en- */ PK_KPBIGPLUS, PK_KPENTER, /* 0 . ter */ /* Top row */ PK_F1, PK_F2, PK_F3, PK_F4, PK_F5, PK_F6, PK_F7, PK_F8, PK_F9, PK_F10, PK_F11, PK_F12, PK_F13, PK_F14, PK_F15, PK_F16, PK_F17, PK_F18, PK_F19, PK_F20, PK_PAUSE } Key_Sym; #define PK_ISEDITING(k) ((k) >= PK_HOME && (k) <= PK_PAGEDOWN) #define PK_ISCURSOR(k) ((k) >= PK_UP && (k) <= PK_REST) #define PK_ISKEYPAD(k) ((k) >= PK_PF1 && (k) <= PK_KPENTER) #define PK_ISFKEY(k) ((k) >= PK_F1 && (k) <= PK_F20) enum { VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN, VT_UNICODE }; enum { /* * SSH-2 key exchange algorithms */ KEX_WARN, KEX_DHGROUP1, KEX_DHGROUP14, KEX_DHGEX, KEX_RSA, KEX_ECDH, KEX_MAX }; enum { /* * SSH-2 host key algorithms */ HK_WARN, HK_RSA, HK_DSA, HK_ECDSA, HK_ED25519, HK_MAX }; enum { /* * SSH ciphers (both SSH-1 and SSH-2) */ CIPHER_WARN, /* pseudo 'cipher' */ CIPHER_3DES, CIPHER_BLOWFISH, CIPHER_AES, /* (SSH-2 only) */ CIPHER_DES, CIPHER_ARCFOUR, CIPHER_CHACHA20, CIPHER_MAX /* no. ciphers (inc warn) */ }; enum { /* * Several different bits of the PuTTY configuration seem to be * three-way settings whose values are `always yes', `always * no', and `decide by some more complex automated means'. This * is true of line discipline options (local echo and line * editing), proxy DNS, proxy terminal logging, Close On Exit, and * SSH server bug workarounds. Accordingly I supply a single enum * here to deal with them all. */ FORCE_ON, FORCE_OFF, AUTO }; enum { /* * Proxy types. */ PROXY_NONE, PROXY_SOCKS4, PROXY_SOCKS5, PROXY_HTTP, PROXY_TELNET, PROXY_CMD, PROXY_FUZZ }; enum { /* * Line discipline options which the backend might try to control. */ LD_EDIT, /* local line editing */ LD_ECHO /* local echo */ }; enum { /* Actions on remote window title query */ TITLE_NONE, TITLE_EMPTY, TITLE_REAL }; enum { /* Protocol back ends. (CONF_protocol) */ PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, /* PROT_SERIAL is supported on a subset of platforms, but it doesn't * hurt to define it globally. */ PROT_SERIAL }; enum { /* Bell settings (CONF_beep) */ BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER }; enum { /* Taskbar flashing indication on bell (CONF_beep_ind) */ B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY }; enum { /* Resize actions (CONF_resize_action) */ RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER }; enum { /* Function key types (CONF_funky_type) */ FUNKY_TILDE, FUNKY_LINUX, FUNKY_XTERM, FUNKY_VT400, FUNKY_VT100P, FUNKY_SCO }; enum { FQ_DEFAULT, FQ_ANTIALIASED, FQ_NONANTIALIASED, FQ_CLEARTYPE }; enum { SER_PAR_NONE, SER_PAR_ODD, SER_PAR_EVEN, SER_PAR_MARK, SER_PAR_SPACE }; enum { SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR }; /* * Tables of string <-> enum value mappings used in settings.c. * Defined here so that backends can export their GSS library tables * to the cross-platform settings code. */ struct keyvalwhere { /* * Two fields which define a string and enum value to be * equivalent to each other. */ const char *s; int v; /* * The next pair of fields are used by gprefs() in settings.c to * arrange that when it reads a list of strings representing a * preference list and translates it into the corresponding list * of integers, strings not appearing in the list are entered in a * configurable position rather than uniformly at the end. */ /* * 'vrel' indicates which other value in the list to place this * element relative to. It should be a value that has occurred in * a 'v' field of some other element of the array, or -1 to * indicate that we simply place relative to one or other end of * the list. * * gprefs will try to process the elements in an order which makes * this field work (i.e. so that the element referenced has been * added before processing this one). */ int vrel; /* * 'where' indicates whether to place the new value before or * after the one referred to by vrel. -1 means before; +1 means * after. * * When vrel is -1, this also implicitly indicates which end of * the array to use. So vrel=-1, where=-1 means to place _before_ * some end of the list (hence, at the last element); vrel=-1, * where=+1 means to place _after_ an end (hence, at the first). */ int where; }; #ifndef NO_GSSAPI extern const int ngsslibs; extern const char *const gsslibnames[]; /* for displaying in configuration */ extern const struct keyvalwhere gsslibkeywords[]; /* for settings.c */ #endif extern const char *const ttymodes[]; enum { /* * Network address types. Used for specifying choice of IPv4/v6 * in config; also used in proxy.c to indicate whether a given * host name has already been resolved or will be resolved at * the proxy end. */ ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME }; struct backend_tag { const char *(*init) (void *frontend_handle, void **backend_handle, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive); void (*free) (void *handle); /* back->reconfig() passes in a replacement configuration. */ void (*reconfig) (void *handle, Conf *conf); /* back->send() returns the current amount of buffered data. */ int (*send) (void *handle, const char *buf, int len); /* back->sendbuffer() does the same thing but without attempting a send */ int (*sendbuffer) (void *handle); void (*size) (void *handle, int width, int height); void (*special) (void *handle, Telnet_Special code); const struct telnet_special *(*get_specials) (void *handle); int (*connected) (void *handle); int (*exitcode) (void *handle); /* If back->sendok() returns FALSE, data sent to it from the frontend * may be lost. */ int (*sendok) (void *handle); int (*ldisc) (void *handle, int); void (*provide_ldisc) (void *handle, void *ldisc); void (*provide_logctx) (void *handle, void *logctx); /* * back->unthrottle() tells the back end that the front end * buffer is clearing. */ void (*unthrottle) (void *handle, int); int (*cfg_info) (void *handle); /* Only implemented in the SSH protocol: check whether a * connection-sharing upstream exists for a given configuration. */ int (*test_for_upstream)(const char *host, int port, Conf *conf); const char *name; int protocol; int default_port; }; extern Backend *backends[]; /* * Suggested default protocol provided by the backend link module. * The application is free to ignore this. */ extern const int be_default_protocol; /* * Name of this particular application, for use in the config box * and other pieces of text. */ extern const char *const appname; /* * Some global flags denoting the type of application. * * FLAG_VERBOSE is set when the user requests verbose details. * * FLAG_STDERR is set in command-line applications (which have a * functioning stderr that it makes sense to write to) and not in * GUI applications (which don't). * * FLAG_INTERACTIVE is set when a full interactive shell session is * being run, _either_ because no remote command has been provided * _or_ because the application is GUI and can't run non- * interactively. * * These flags describe the type of _application_ - they wouldn't * vary between individual sessions - and so it's OK to have this * variable be GLOBAL. * * Note that additional flags may be defined in platform-specific * headers. It's probably best if those ones start from 0x1000, to * avoid collision. */ #define FLAG_VERBOSE 0x0001 #define FLAG_STDERR 0x0002 #define FLAG_INTERACTIVE 0x0004 GLOBAL int flags; /* * Likewise, these two variables are set up when the application * initialises, and inform all default-settings accesses after * that. */ GLOBAL int default_protocol; GLOBAL int default_port; /* * This is set TRUE by cmdline.c iff a session is loaded with "-load". */ GLOBAL int loaded_session; /* * This is set to the name of the loaded session. */ GLOBAL char *cmdline_session_name; struct RSAKey; /* be a little careful of scope */ /* * Mechanism for getting text strings such as usernames and passwords * from the front-end. * The fields are mostly modelled after SSH's keyboard-interactive auth. * FIXME We should probably mandate a character set/encoding (probably UTF-8). * * Since many of the pieces of text involved may be chosen by the server, * the caller must take care to ensure that the server can't spoof locally- * generated prompts such as key passphrase prompts. Some ground rules: * - If the front-end needs to truncate a string, it should lop off the * end. * - The front-end should filter out any dangerous characters and * generally not trust the strings. (But \n is required to behave * vaguely sensibly, at least in `instruction', and ideally in * `prompt[]' too.) */ typedef struct { char *prompt; int echo; /* * 'result' must be a dynamically allocated array of exactly * 'resultsize' chars. The code for actually reading input may * realloc it bigger (and adjust resultsize accordingly) if it has * to. The caller should free it again when finished with it. * * If resultsize==0, then result may be NULL. When setting up a * prompt_t, it's therefore easiest to initialise them this way, * which means all actual allocation is done by the callee. This * is what add_prompt does. */ char *result; size_t resultsize; } prompt_t; typedef struct { /* * Indicates whether the information entered is to be used locally * (for instance a key passphrase prompt), or is destined for the wire. * This is a hint only; the front-end is at liberty not to use this * information (so the caller should ensure that the supplied text is * sufficient). */ int to_server; char *name; /* Short description, perhaps for dialog box title */ int name_reqd; /* Display of `name' required or optional? */ char *instruction; /* Long description, maybe with embedded newlines */ int instr_reqd; /* Display of `instruction' required or optional? */ size_t n_prompts; /* May be zero (in which case display the foregoing, * if any, and return success) */ prompt_t **prompts; void *frontend; void *data; /* slot for housekeeping data, managed by * get_userpass_input(); initially NULL */ } prompts_t; prompts_t *new_prompts(void *frontend); void add_prompt(prompts_t *p, char *promptstr, int echo); void prompt_set_result(prompt_t *pr, const char *newstr); void prompt_ensure_result_size(prompt_t *pr, int len); /* Burn the evidence. (Assumes _all_ strings want free()ing.) */ void free_prompts(prompts_t *p); /* * Exports from the front end. */ void request_resize(void *frontend, int, int); void do_text(Context, int, int, wchar_t *, int, unsigned long, int); void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int); int char_width(Context ctx, int uc); #ifdef OPTIMISE_SCROLL void do_scroll(Context, int, int, int); #endif void set_title(void *frontend, char *); void set_icon(void *frontend, char *); void set_sbar(void *frontend, int, int, int); Context get_ctx(void *frontend); void free_ctx(Context); void palette_set(void *frontend, int, int, int, int); void palette_reset(void *frontend); void write_aclip(void *frontend, char *, int, int); void write_clip(void *frontend, wchar_t *, int *, int, int); void get_clip(void *frontend, wchar_t **, int *); void optimised_move(void *frontend, int, int, int); void set_raw_mouse_mode(void *frontend, int); void connection_fatal(void *frontend, const char *, ...); void nonfatal(const char *, ...); void fatalbox(const char *, ...); void modalfatalbox(const char *, ...); #ifdef macintosh #pragma noreturn(fatalbox) #pragma noreturn(modalfatalbox) #endif void do_beep(void *frontend, int); void begin_session(void *frontend); void sys_cursor(void *frontend, int x, int y); void request_paste(void *frontend); void frontend_keypress(void *frontend); void frontend_echoedit_update(void *frontend, int echo, int edit); /* It's the backend's responsibility to invoke this at the start of a * connection, if necessary; it can also invoke it later if the set of * special commands changes. It does not need to invoke it at session * shutdown. */ void update_specials_menu(void *frontend); int from_backend(void *frontend, int is_stderr, const char *data, int len); int from_backend_untrusted(void *frontend, const char *data, int len); /* Called when the back end wants to indicate that EOF has arrived on * the server-to-client stream. Returns FALSE to indicate that we * intend to keep the session open in the other direction, or TRUE to * indicate that if they're closing so are we. */ int from_backend_eof(void *frontend); void notify_remote_exit(void *frontend); /* Get a sensible value for a tty mode. NULL return = don't set. * Otherwise, returned value should be freed by caller. */ char *get_ttymode(void *frontend, const char *mode); /* * >0 = `got all results, carry on' * 0 = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?) * <0 = `please call back later with more in/inlen' */ int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen); #define OPTIMISE_IS_SCROLL 1 void set_iconic(void *frontend, int iconic); void move_window(void *frontend, int x, int y); void set_zorder(void *frontend, int top); void refresh_window(void *frontend); void set_zoomed(void *frontend, int zoomed); int is_iconic(void *frontend); void get_window_pos(void *frontend, int *x, int *y); void get_window_pixels(void *frontend, int *x, int *y); char *get_window_title(void *frontend, int icon); /* Hint from backend to frontend about time-consuming operations. * Initial state is assumed to be BUSY_NOT. */ enum { BUSY_NOT, /* Not busy, all user interaction OK */ BUSY_WAITING, /* Waiting for something; local event loops still running so some local interaction (e.g. menus) OK, but network stuff is suspended */ BUSY_CPU /* Locally busy (e.g. crypto); user interaction suspended */ }; void set_busy_status(void *frontend, int status); int frontend_is_utf8(void *frontend); void cleanup_exit(int); /* * Exports from conf.c, and a big enum (via parametric macro) of * configuration option keys. */ #define CONFIG_OPTIONS(X) \ /* X(value-type, subkey-type, keyword) */ \ X(STR, NONE, host) \ X(INT, NONE, port) \ X(INT, NONE, protocol) \ X(INT, NONE, addressfamily) \ X(INT, NONE, close_on_exit) \ X(INT, NONE, warn_on_close) \ X(INT, NONE, ping_interval) /* in seconds */ \ X(INT, NONE, tcp_nodelay) \ X(INT, NONE, tcp_keepalives) \ X(STR, NONE, loghost) /* logical host being contacted, for host key check */ \ /* Proxy options */ \ X(STR, NONE, proxy_exclude_list) \ X(INT, NONE, proxy_dns) \ X(INT, NONE, even_proxy_localhost) \ X(INT, NONE, proxy_type) \ X(STR, NONE, proxy_host) \ X(INT, NONE, proxy_port) \ X(STR, NONE, proxy_username) \ X(STR, NONE, proxy_password) \ X(STR, NONE, proxy_telnet_command) \ X(INT, NONE, proxy_log_to_term) \ /* SSH options */ \ X(STR, NONE, remote_cmd) \ X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \ X(INT, NONE, nopty) \ X(INT, NONE, compression) \ X(INT, INT, ssh_kexlist) \ X(INT, INT, ssh_hklist) \ X(INT, NONE, ssh_rekey_time) /* in minutes */ \ X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \ X(INT, NONE, tryagent) \ X(INT, NONE, agentfwd) \ X(INT, NONE, change_username) /* allow username switching in SSH-2 */ \ X(INT, INT, ssh_cipherlist) \ X(FILENAME, NONE, keyfile) \ /* \ * Which SSH protocol to use. \ * For historical reasons, the current legal values for CONF_sshprot \ * are: \ * 0 = SSH-1 only \ * 3 = SSH-2 only \ * We used to also support \ * 1 = SSH-1 with fallback to SSH-2 \ * 2 = SSH-2 with fallback to SSH-1 \ * and we continue to use 0/3 in storage formats rather than the more \ * obvious 1/2 to avoid surprises if someone saves a session and later \ * downgrades PuTTY. So it's easier to use these numbers internally too. \ */ \ X(INT, NONE, sshprot) \ X(INT, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \ X(INT, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \ X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \ X(INT, NONE, try_tis_auth) \ X(INT, NONE, try_ki_auth) \ X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth */ \ X(INT, NONE, gssapifwd) /* forward tgt via gss */ \ X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \ X(FILENAME, NONE, ssh_gss_custom) \ X(INT, NONE, ssh_subsys) /* run a subsystem rather than a command */ \ X(INT, NONE, ssh_subsys2) /* fallback to go with remote_cmd_ptr2 */ \ X(INT, NONE, ssh_no_shell) /* avoid running a shell */ \ X(STR, NONE, ssh_nc_host) /* host to connect to in `nc' mode */ \ X(INT, NONE, ssh_nc_port) /* port to connect to in `nc' mode */ \ /* Telnet options */ \ X(STR, NONE, termtype) \ X(STR, NONE, termspeed) \ X(STR, STR, ttymodes) /* values are "Vvalue" or "A" */ \ X(STR, STR, environmt) \ X(STR, NONE, username) \ X(INT, NONE, username_from_env) \ X(STR, NONE, localusername) \ X(INT, NONE, rfc_environ) \ X(INT, NONE, passive_telnet) \ /* Serial port options */ \ X(STR, NONE, serline) \ X(INT, NONE, serspeed) \ X(INT, NONE, serdatabits) \ X(INT, NONE, serstopbits) \ X(INT, NONE, serparity) \ X(INT, NONE, serflow) \ /* Keyboard options */ \ X(INT, NONE, bksp_is_delete) \ X(INT, NONE, rxvt_homeend) \ X(INT, NONE, funky_type) \ X(INT, NONE, no_applic_c) /* totally disable app cursor keys */ \ X(INT, NONE, no_applic_k) /* totally disable app keypad */ \ X(INT, NONE, no_mouse_rep) /* totally disable mouse reporting */ \ X(INT, NONE, no_remote_resize) /* disable remote resizing */ \ X(INT, NONE, no_alt_screen) /* disable alternate screen */ \ X(INT, NONE, no_remote_wintitle) /* disable remote retitling */ \ X(INT, NONE, no_remote_clearscroll) /* disable ESC[3J */ \ X(INT, NONE, no_dbackspace) /* disable destructive backspace */ \ X(INT, NONE, no_remote_charset) /* disable remote charset config */ \ X(INT, NONE, remote_qtitle_action) /* remote win title query action */ \ X(INT, NONE, app_cursor) \ X(INT, NONE, app_keypad) \ X(INT, NONE, nethack_keypad) \ X(INT, NONE, telnet_keyboard) \ X(INT, NONE, telnet_newline) \ X(INT, NONE, alt_f4) /* is it special? */ \ X(INT, NONE, alt_space) /* is it special? */ \ X(INT, NONE, alt_only) /* is it special? */ \ X(INT, NONE, localecho) \ X(INT, NONE, localedit) \ X(INT, NONE, alwaysontop) \ X(INT, NONE, fullscreenonaltenter) \ X(INT, NONE, scroll_on_key) \ X(INT, NONE, scroll_on_disp) \ X(INT, NONE, erase_to_scrollback) \ X(INT, NONE, compose_key) \ X(INT, NONE, ctrlaltkeys) \ X(INT, NONE, osx_option_meta) \ X(INT, NONE, osx_command_meta) \ X(STR, NONE, wintitle) /* initial window title */ \ /* Terminal options */ \ X(INT, NONE, savelines) \ X(INT, NONE, dec_om) \ X(INT, NONE, wrap_mode) \ X(INT, NONE, lfhascr) \ X(INT, NONE, cursor_type) /* 0=block 1=underline 2=vertical */ \ X(INT, NONE, blink_cur) \ X(INT, NONE, beep) \ X(INT, NONE, beep_ind) \ X(INT, NONE, bellovl) /* bell overload protection active? */ \ X(INT, NONE, bellovl_n) /* number of bells to cause overload */ \ X(INT, NONE, bellovl_t) /* time interval for overload (seconds) */ \ X(INT, NONE, bellovl_s) /* period of silence to re-enable bell (s) */ \ X(FILENAME, NONE, bell_wavefile) \ X(INT, NONE, scrollbar) \ X(INT, NONE, scrollbar_in_fullscreen) \ X(INT, NONE, resize_action) \ X(INT, NONE, bce) \ X(INT, NONE, blinktext) \ X(INT, NONE, win_name_always) \ X(INT, NONE, width) \ X(INT, NONE, height) \ X(FONT, NONE, font) \ X(INT, NONE, font_quality) \ X(FILENAME, NONE, logfilename) \ X(INT, NONE, logtype) \ X(INT, NONE, logxfovr) \ X(INT, NONE, logflush) \ X(INT, NONE, logomitpass) \ X(INT, NONE, logomitdata) \ X(INT, NONE, hide_mouseptr) \ X(INT, NONE, sunken_edge) \ X(INT, NONE, window_border) \ X(STR, NONE, answerback) \ X(STR, NONE, printer) \ X(INT, NONE, arabicshaping) \ X(INT, NONE, bidi) \ /* Colour options */ \ X(INT, NONE, ansi_colour) \ X(INT, NONE, xterm_256_colour) \ X(INT, NONE, system_colour) \ X(INT, NONE, try_palette) \ X(INT, NONE, bold_style) \ X(INT, INT, colours) \ /* Selection options */ \ X(INT, NONE, mouse_is_xterm) \ X(INT, NONE, rect_select) \ X(INT, NONE, rawcnp) \ X(INT, NONE, rtf_paste) \ X(INT, NONE, mouse_override) \ X(INT, INT, wordness) \ /* translations */ \ X(INT, NONE, vtmode) \ X(STR, NONE, line_codepage) \ X(INT, NONE, cjk_ambig_wide) \ X(INT, NONE, utf8_override) \ X(INT, NONE, xlat_capslockcyr) \ /* X11 forwarding */ \ X(INT, NONE, x11_forward) \ X(STR, NONE, x11_display) \ X(INT, NONE, x11_auth) \ X(FILENAME, NONE, xauthfile) \ /* port forwarding */ \ X(INT, NONE, lport_acceptall) /* accept conns from hosts other than localhost */ \ X(INT, NONE, rport_acceptall) /* same for remote forwarded ports (SSH-2 only) */ \ /* \ * Subkeys for 'portfwd' can have the following forms: \ * \ * [LR]localport \ * [LR]localaddr:localport \ * \ * Dynamic forwardings are indicated by an 'L' key, and the \ * special value "D". For all other forwardings, the value \ * should be of the form 'host:port'. \ */ \ X(STR, STR, portfwd) \ /* SSH bug compatibility modes */ \ X(INT, NONE, sshbug_ignore1) \ X(INT, NONE, sshbug_plainpw1) \ X(INT, NONE, sshbug_rsa1) \ X(INT, NONE, sshbug_hmac2) \ X(INT, NONE, sshbug_derivekey2) \ X(INT, NONE, sshbug_rsapad2) \ X(INT, NONE, sshbug_pksessid2) \ X(INT, NONE, sshbug_rekey2) \ X(INT, NONE, sshbug_maxpkt2) \ X(INT, NONE, sshbug_ignore2) \ X(INT, NONE, sshbug_oldgex2) \ X(INT, NONE, sshbug_winadj) \ X(INT, NONE, sshbug_chanreq) \ /* \ * ssh_simple means that we promise never to open any channel \ * other than the main one, which means it can safely use a very \ * large window in SSH-2. \ */ \ X(INT, NONE, ssh_simple) \ X(INT, NONE, ssh_connection_sharing) \ X(INT, NONE, ssh_connection_sharing_upstream) \ X(INT, NONE, ssh_connection_sharing_downstream) \ /* * ssh_manual_hostkeys is conceptually a set rather than a * dictionary: the string subkeys are the important thing, and the * actual values to which those subkeys map are all "". */ \ X(STR, STR, ssh_manual_hostkeys) \ /* Options for pterm. Should split out into platform-dependent part. */ \ X(INT, NONE, stamp_utmp) \ X(INT, NONE, login_shell) \ X(INT, NONE, scrollbar_on_left) \ X(INT, NONE, shadowbold) \ X(FONT, NONE, boldfont) \ X(FONT, NONE, widefont) \ X(FONT, NONE, wideboldfont) \ X(INT, NONE, shadowboldoffset) \ X(INT, NONE, crhaslf) \ X(STR, NONE, winclass) \ /* Now define the actual enum of option keywords using that macro. */ #define CONF_ENUM_DEF(valtype, keytype, keyword) CONF_ ## keyword, enum config_primary_key { CONFIG_OPTIONS(CONF_ENUM_DEF) N_CONFIG_OPTIONS }; #undef CONF_ENUM_DEF #define NCFGCOLOURS 22 /* number of colours in CONF_colours above */ /* Functions handling configuration structures. */ Conf *conf_new(void); /* create an empty configuration */ void conf_free(Conf *conf); Conf *conf_copy(Conf *oldconf); void conf_copy_into(Conf *dest, Conf *src); /* Mandatory accessor functions: enforce by assertion that keys exist. */ int conf_get_int(Conf *conf, int key); int conf_get_int_int(Conf *conf, int key, int subkey); char *conf_get_str(Conf *conf, int key); /* result still owned by conf */ char *conf_get_str_str(Conf *conf, int key, const char *subkey); Filename *conf_get_filename(Conf *conf, int key); FontSpec *conf_get_fontspec(Conf *conf, int key); /* still owned by conf */ /* Optional accessor function: return NULL if key does not exist. */ char *conf_get_str_str_opt(Conf *conf, int key, const char *subkey); /* Accessor function to step through a string-subkeyed list. * Returns the next subkey after the provided one, or the first if NULL. * Returns NULL if there are none left. * Both the return value and *subkeyout are still owned by conf. */ char *conf_get_str_strs(Conf *conf, int key, char *subkeyin, char **subkeyout); /* Return the nth string subkey in a list. Owned by conf. NULL if beyond end */ char *conf_get_str_nthstrkey(Conf *conf, int key, int n); /* Functions to set entries in configuration. Always copy their inputs. */ void conf_set_int(Conf *conf, int key, int value); void conf_set_int_int(Conf *conf, int key, int subkey, int value); void conf_set_str(Conf *conf, int key, const char *value); void conf_set_str_str(Conf *conf, int key, const char *subkey, const char *val); void conf_del_str_str(Conf *conf, int key, const char *subkey); void conf_set_filename(Conf *conf, int key, const Filename *val); void conf_set_fontspec(Conf *conf, int key, const FontSpec *val); /* Serialisation functions for Duplicate Session */ int conf_serialised_size(Conf *conf); void conf_serialise(Conf *conf, void *data); int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/ /* * Functions to copy, free, serialise and deserialise FontSpecs. * Provided per-platform, to go with the platform's idea of a * FontSpec's contents. * * fontspec_serialise returns the number of bytes written, and can * handle data==NULL without crashing. So you can call it once to find * out a size, then again once you've allocated a buffer. */ FontSpec *fontspec_copy(const FontSpec *f); void fontspec_free(FontSpec *f); int fontspec_serialise(FontSpec *f, void *data); FontSpec *fontspec_deserialise(void *data, int maxsize, int *used); /* * Exports from noise.c. */ void noise_get_heavy(void (*func) (void *, int)); void noise_get_light(void (*func) (void *, int)); void noise_regular(void); void noise_ultralight(unsigned long data); void random_save_seed(void); void random_destroy_seed(void); /* * Exports from settings.c. */ Backend *backend_from_name(const char *name); Backend *backend_from_proto(int proto); char *get_remote_username(Conf *conf); /* dynamically allocated */ char *save_settings(const char *section, Conf *conf); void save_open_settings(void *sesskey, Conf *conf); void load_settings(const char *section, Conf *conf); void load_open_settings(void *sesskey, Conf *conf); void get_sesslist(struct sesslist *, int allocate); void do_defaults(const char *, Conf *); void registry_cleanup(void); /* * Functions used by settings.c to provide platform-specific * default settings. * * (The integer one is expected to return `def' if it has no clear * opinion of its own. This is because there's no integer value * which I can reliably set aside to indicate `nil'. The string * function is perfectly all right returning NULL, of course. The * Filename and FontSpec functions are _not allowed_ to fail to * return, since these defaults _must_ be per-platform.) * * The 'Filename *' returned by platform_default_filename, and the * 'FontSpec *' returned by platform_default_fontspec, have ownership * transferred to the caller, and must be freed. */ char *platform_default_s(const char *name); int platform_default_i(const char *name, int def); Filename *platform_default_filename(const char *name); FontSpec *platform_default_fontspec(const char *name); /* * Exports from terminal.c. */ Terminal *term_init(Conf *, struct unicode_data *, void *); void term_free(Terminal *); void term_size(Terminal *, int, int, int); void term_paint(Terminal *, Context, int, int, int, int, int); void term_scroll(Terminal *, int, int); void term_scroll_to_selection(Terminal *, int); void term_pwron(Terminal *, int); void term_clrsb(Terminal *); void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action, int,int,int,int,int); void term_key(Terminal *, Key_Sym, wchar_t *, size_t, unsigned int, unsigned int); void term_deselect(Terminal *); void term_update(Terminal *); void term_invalidate(Terminal *); void term_blink(Terminal *, int set_cursor); void term_do_paste(Terminal *); void term_nopaste(Terminal *); int term_ldisc(Terminal *, int option); void term_copyall(Terminal *); void term_reconfig(Terminal *, Conf *); void term_seen_key_event(Terminal *); int term_data(Terminal *, int is_stderr, const char *data, int len); int term_data_untrusted(Terminal *, const char *data, int len); void term_provide_resize_fn(Terminal *term, void (*resize_fn)(void *, int, int), void *resize_ctx); void term_provide_logctx(Terminal *term, void *logctx); void term_set_focus(Terminal *term, int has_focus); char *term_get_ttymode(Terminal *term, const char *mode); int term_get_userpass_input(Terminal *term, prompts_t *p, const unsigned char *in, int inlen); int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl); /* * Exports from logging.c. */ void *log_init(void *frontend, Conf *conf); void log_free(void *logctx); void log_reconfig(void *logctx, Conf *conf); void logfopen(void *logctx); void logfclose(void *logctx); void logtraffic(void *logctx, unsigned char c, int logmode); void logflush(void *logctx); void log_eventlog(void *logctx, const char *string); enum { PKT_INCOMING, PKT_OUTGOING }; enum { PKTLOG_EMIT, PKTLOG_BLANK, PKTLOG_OMIT }; struct logblank_t { int offset; int len; int type; }; void log_packet(void *logctx, int direction, int type, const char *texttype, const void *data, int len, int n_blanks, const struct logblank_t *blanks, const unsigned long *sequence, unsigned downstream_id, const char *additional_log_text); /* * Exports from testback.c */ extern Backend null_backend; extern Backend loop_backend; /* * Exports from raw.c. */ extern Backend raw_backend; /* * Exports from rlogin.c. */ extern Backend rlogin_backend; /* * Exports from telnet.c. */ extern Backend telnet_backend; /* * Exports from ssh.c. */ extern Backend ssh_backend; /* * Exports from ldisc.c. */ void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *); void ldisc_configure(void *, Conf *); void ldisc_free(void *); void ldisc_send(void *handle, const char *buf, int len, int interactive); void ldisc_echoedit_update(void *handle); /* * Exports from ldiscucs.c. */ void lpage_send(void *, int codepage, const char *buf, int len, int interactive); void luni_send(void *, const wchar_t * widebuf, int len, int interactive); /* * Exports from sshrand.c. */ void random_add_noise(void *noise, int length); int random_byte(void); void random_get_savedata(void **data, int *len); extern int random_active; /* The random number subsystem is activated if at least one other entity * within the program expresses an interest in it. So each SSH session * calls random_ref on startup and random_unref on shutdown. */ void random_ref(void); void random_unref(void); /* * Exports from pinger.c. */ typedef struct pinger_tag *Pinger; Pinger pinger_new(Conf *conf, Backend *back, void *backhandle); void pinger_reconfig(Pinger, Conf *oldconf, Conf *newconf); void pinger_free(Pinger); /* * Exports from misc.c. */ #include "misc.h" int conf_launchable(Conf *conf); char const *conf_dest(Conf *conf); /* * Exports from sercfg.c. */ void ser_setup_config_box(struct controlbox *b, int midsession, int parity_mask, int flow_mask); /* * Exports from version.c. */ extern const char ver[]; /* * Exports from unicode.c. */ #ifndef CP_UTF8 #define CP_UTF8 65001 #endif /* void init_ucs(void); -- this is now in platform-specific headers */ int is_dbcs_leadbyte(int codepage, char byte); int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen); int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, const char *defchr, int *defused, struct unicode_data *ucsdata); wchar_t xlat_uskbd2cyrllic(int ch); int check_compose(int first, int second); int decode_codepage(char *cp_name); const char *cp_enumerate (int index); const char *cp_name(int codepage); void get_unitab(int codepage, wchar_t * unitab, int ftype); /* * Exports from wcwidth.c */ int mk_wcwidth(unsigned int ucs); int mk_wcswidth(const unsigned int *pwcs, size_t n); int mk_wcwidth_cjk(unsigned int ucs); int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n); /* * Exports from mscrypto.c */ #ifdef MSCRYPTOAPI int crypto_startup(); void crypto_wrapup(); #endif /* * Exports from pageantc.c. * * agent_query returns NULL for here's-a-response, and non-NULL for * query-in- progress. In the latter case there will be a call to * `callback' at some future point, passing callback_ctx as the first * parameter and the actual reply data as the second and third. * * The response may be a NULL pointer (in either of the synchronous * or asynchronous cases), which indicates failure to receive a * response. * * When the return from agent_query is not NULL, it identifies the * in-progress query in case it needs to be cancelled. If * agent_cancel_query is called, then the pending query is destroyed * and the callback will not be called. (E.g. if you're going to throw * away the thing you were using as callback_ctx.) * * Passing a null pointer as callback forces agent_query to behave * synchronously, i.e. it will block if necessary, and guarantee to * return NULL. The wrapper function agent_query_synchronous() makes * this easier. */ typedef struct agent_pending_query agent_pending_query; agent_pending_query *agent_query( void *in, int inlen, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx); void agent_cancel_query(agent_pending_query *); void agent_query_synchronous(void *in, int inlen, void **out, int *outlen); int agent_exists(void); /* * Exports from wildcard.c */ const char *wc_error(int value); int wc_match(const char *wildcard, const char *target); int wc_unescape(char *output, const char *wildcard); /* * Exports from frontend (windlg.c etc) */ void logevent(void *frontend, const char *); void pgp_fingerprints(void); /* * verify_ssh_host_key() can return one of three values: * * - +1 means `key was OK' (either already known or the user just * approved it) `so continue with the connection' * * - 0 means `key was not OK, abandon the connection' * * - -1 means `I've initiated enquiries, please wait to be called * back via the provided function with a result that's either 0 * or +1'. */ int verify_ssh_host_key(void *frontend, char *host, int port, const char *keytype, char *keystr, char *fingerprint, void (*callback)(void *ctx, int result), void *ctx); /* * have_ssh_host_key() just returns true if a key of that type is * already cached and false otherwise. */ int have_ssh_host_key(const char *host, int port, const char *keytype); /* * askalg and askhk have the same set of return values as * verify_ssh_host_key. * * (askhk is used in the case where we're using a host key below the * warning threshold because that's all we have cached, but at least * one acceptable algorithm is available that we don't have cached.) */ int askalg(void *frontend, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx); int askhk(void *frontend, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx); /* * askappend can return four values: * * - 2 means overwrite the log file * - 1 means append to the log file * - 0 means cancel logging for this session * - -1 means please wait. */ int askappend(void *frontend, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); /* * Exports from console frontends (wincons.c, uxcons.c) * that aren't equivalents to things in windlg.c et al. */ extern int console_batch_mode; int console_get_userpass_input(prompts_t *p, const unsigned char *in, int inlen); void console_provide_logctx(void *logctx); int is_interactive(void); /* * Exports from printing.c. */ typedef struct printer_enum_tag printer_enum; typedef struct printer_job_tag printer_job; printer_enum *printer_start_enum(int *nprinters); char *printer_get_name(printer_enum *, int); void printer_finish_enum(printer_enum *); printer_job *printer_start_job(char *printer); void printer_job_data(printer_job *, void *, int); void printer_finish_job(printer_job *); /* * Exports from cmdline.c (and also cmdline_error(), which is * defined differently in various places and required _by_ * cmdline.c). * * Note that cmdline_process_param takes a const option string, but a * writable argument string. That's not a mistake - that's so it can * zero out password arguments in the hope of not having them show up * avoidably in Unix 'ps'. */ int cmdline_process_param(const char *, char *, int, Conf *); void cmdline_run_saved(Conf *); void cmdline_cleanup(void); int cmdline_get_passwd_input(prompts_t *p, const unsigned char *in, int inlen); #define TOOLTYPE_FILETRANSFER 1 #define TOOLTYPE_NONNETWORK 2 extern int cmdline_tooltype; void cmdline_error(const char *, ...); /* * Exports from config.c. */ struct controlbox; union control; void conf_radiobutton_handler(union control *ctrl, void *dlg, void *data, int event); #define CHECKBOX_INVERT (1<<30) void conf_checkbox_handler(union control *ctrl, void *dlg, void *data, int event); void conf_editbox_handler(union control *ctrl, void *dlg, void *data, int event); void conf_filesel_handler(union control *ctrl, void *dlg, void *data, int event); void conf_fontsel_handler(union control *ctrl, void *dlg, void *data, int event); void setup_config_box(struct controlbox *b, int midsession, int protocol, int protcfginfo); /* * Exports from minibidi.c. */ typedef struct bidi_char { unsigned int origwc, wc; unsigned short index; } bidi_char; int do_bidi(bidi_char *line, int count); int do_shape(bidi_char *line, bidi_char *to, int count); int is_rtl(int c); /* * X11 auth mechanisms we know about. */ enum { X11_NO_AUTH, X11_MIT, /* MIT-MAGIC-COOKIE-1 */ X11_XDM, /* XDM-AUTHORIZATION-1 */ X11_NAUTHS }; extern const char *const x11_authnames[]; /* declared in x11fwd.c */ /* * Miscellaneous exports from the platform-specific code. * * filename_serialise and filename_deserialise have the same semantics * as fontspec_serialise and fontspec_deserialise above. */ Filename *filename_from_str(const char *string); const char *filename_to_str(const Filename *fn); int filename_equal(const Filename *f1, const Filename *f2); int filename_is_null(const Filename *fn); Filename *filename_copy(const Filename *fn); void filename_free(Filename *fn); int filename_serialise(const Filename *f, void *data); Filename *filename_deserialise(void *data, int maxsize, int *used); char *get_username(void); /* return value needs freeing */ char *get_random_data(int bytes, const char *device); /* used in cmdgen.c */ char filename_char_sanitise(char c); /* rewrite special pathname chars */ /* * Exports and imports from timing.c. * * schedule_timer() asks the front end to schedule a callback to a * timer function in a given number of ticks. The returned value is * the time (in ticks since an arbitrary offset) at which the * callback can be expected. This value will also be passed as the * `now' parameter to the callback function. Hence, you can (for * example) schedule an event at a particular time by calling * schedule_timer() and storing the return value in your context * structure as the time when that event is due. The first time a * callback function gives you that value or more as `now', you do * the thing. * * expire_timer_context() drops all current timers associated with * a given value of ctx (for when you're about to free ctx). * * run_timers() is called from the front end when it has reason to * think some timers have reached their moment, or when it simply * needs to know how long to wait next. We pass it the time we * think it is. It returns TRUE and places the time when the next * timer needs to go off in `next', or alternatively it returns * FALSE if there are no timers at all pending. * * timer_change_notify() must be supplied by the front end; it * notifies the front end that a new timer has been added to the * list which is sooner than any existing ones. It provides the * time when that timer needs to go off. * * *** FRONT END IMPLEMENTORS NOTE: * * There's an important subtlety in the front-end implementation of * the timer interface. When a front end is given a `next' value, * either returned from run_timers() or via timer_change_notify(), * it should ensure that it really passes _that value_ as the `now' * parameter to its next run_timers call. It should _not_ simply * call GETTICKCOUNT() to get the `now' parameter when invoking * run_timers(). * * The reason for this is that an OS's system clock might not agree * exactly with the timing mechanisms it supplies to wait for a * given interval. I'll illustrate this by the simple example of * Unix Plink, which uses timeouts to select() in a way which for * these purposes can simply be considered to be a wait() function. * Suppose, for the sake of argument, that this wait() function * tends to return early by 1%. Then a possible sequence of actions * is: * * - run_timers() tells the front end that the next timer firing * is 10000ms from now. * - Front end calls wait(10000ms), but according to * GETTICKCOUNT() it has only waited for 9900ms. * - Front end calls run_timers() again, passing time T-100ms as * `now'. * - run_timers() does nothing, and says the next timer firing is * still 100ms from now. * - Front end calls wait(100ms), which only waits for 99ms. * - Front end calls run_timers() yet again, passing time T-1ms. * - run_timers() says there's still 1ms to wait. * - Front end calls wait(1ms). * * If you're _lucky_ at this point, wait(1ms) will actually wait * for 1ms and you'll only have woken the program up three times. * If you're unlucky, wait(1ms) might do nothing at all due to * being below some minimum threshold, and you might find your * program spends the whole of the last millisecond tight-looping * between wait() and run_timers(). * * Instead, what you should do is to _save_ the precise `next' * value provided by run_timers() or via timer_change_notify(), and * use that precise value as the input to the next run_timers() * call. So: * * - run_timers() tells the front end that the next timer firing * is at time T, 10000ms from now. * - Front end calls wait(10000ms). * - Front end then immediately calls run_timers() and passes it * time T, without stopping to check GETTICKCOUNT() at all. * * This guarantees that the program wakes up only as many times as * there are actual timer actions to be taken, and that the timing * mechanism will never send it into a tight loop. * * (It does also mean that the timer action in the above example * will occur 100ms early, but this is not generally critical. And * the hypothetical 1% error in wait() will be partially corrected * for anyway when, _after_ run_timers() returns, you call * GETTICKCOUNT() and compare the result with the returned `next' * value to find out how long you have to make your next wait().) */ typedef void (*timer_fn_t)(void *ctx, unsigned long now); unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx); void expire_timer_context(void *ctx); int run_timers(unsigned long now, unsigned long *next); void timer_change_notify(unsigned long next); unsigned long timing_last_clock(void); /* * Exports from callback.c. * * This provides a method of queuing function calls to be run at the * earliest convenience from the top-level event loop. Use it if * you're deep in a nested chain of calls and want to trigger an * action which will probably lead to your function being re-entered * recursively if you just call the initiating function the normal * way. * * Most front ends run the queued callbacks by simply calling * run_toplevel_callbacks() after handling each event in their * top-level event loop. However, if a front end doesn't have control * over its own event loop (e.g. because it's using GTK) then it can * instead request notifications when a callback is available, so that * it knows to ask its delegate event loop to do the same thing. Also, * if a front end needs to know whether a callback is pending without * actually running it (e.g. so as to put a zero timeout on a select() * call) then it can call toplevel_callback_pending(), which will * return true if at least one callback is in the queue. */ typedef void (*toplevel_callback_fn_t)(void *ctx); void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx); void run_toplevel_callbacks(void); int toplevel_callback_pending(void); typedef void (*toplevel_callback_notify_fn_t)(void *frontend); void request_callback_notifications(toplevel_callback_notify_fn_t notify, void *frontend); /* * Define no-op macros for the jump list functions, on platforms that * don't support them. (This is a bit of a hack, and it'd be nicer to * localise even the calls to those functions into the Windows front * end, but it'll do for the moment.) */ #ifndef JUMPLIST_SUPPORTED #define add_session_to_jumplist(x) ((void)0) #define remove_session_from_jumplist(x) ((void)0) #endif /* SURROGATE PAIR */ #define HIGH_SURROGATE_START 0xd800 #define HIGH_SURROGATE_END 0xdbff #define LOW_SURROGATE_START 0xdc00 #define LOW_SURROGATE_END 0xdfff /* These macros exist in the Windows API, so the environment may * provide them. If not, define them in terms of the above. */ #ifndef IS_HIGH_SURROGATE #define IS_HIGH_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ ((wch) <= HIGH_SURROGATE_END)) #define IS_LOW_SURROGATE(wch) (((wch) >= LOW_SURROGATE_START) && \ ((wch) <= LOW_SURROGATE_END)) #define IS_SURROGATE_PAIR(hs, ls) (IS_HIGH_SURROGATE(hs) && \ IS_LOW_SURROGATE(ls)) #endif #define IS_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && \ ((wch) <= LOW_SURROGATE_END)) #define HIGH_SURROGATE_OF(codept) \ (HIGH_SURROGATE_START + (((codept) - 0x10000) >> 10)) #define LOW_SURROGATE_OF(codept) \ (LOW_SURROGATE_START + (((codept) - 0x10000) & 0x3FF)) #define FROM_SURROGATES(wch1, wch2) \ (0x10000 + (((wch1) & 0x3FF) << 10) + ((wch2) & 0x3FF)) #endif its-playback-time-0.2017-08-30.3c40fd3/puttymem.h000066400000000000000000000036271333656753000207140ustar00rootroot00000000000000/* * PuTTY memory-handling header. */ #ifndef PUTTY_PUTTYMEM_H #define PUTTY_PUTTYMEM_H #include /* for size_t */ #include /* for memcpy() */ /* #define MALLOC_LOG do this if you suspect putty of leaking memory */ #ifdef MALLOC_LOG #define smalloc(z) (mlog(__FILE__,__LINE__), safemalloc(z,1)) #define snmalloc(z,s) (mlog(__FILE__,__LINE__), safemalloc(z,s)) #define srealloc(y,z) (mlog(__FILE__,__LINE__), saferealloc(y,z,1)) #define snrealloc(y,z,s) (mlog(__FILE__,__LINE__), saferealloc(y,z,s)) #define sfree(z) (mlog(__FILE__,__LINE__), safefree(z)) void mlog(char *, int); #else #define smalloc(z) safemalloc(z,1) #define snmalloc safemalloc #define srealloc(y,z) saferealloc(y,z,1) #define snrealloc saferealloc #define sfree safefree #endif void *safemalloc(size_t, size_t); void *saferealloc(void *, size_t, size_t); void safefree(void *); /* * Direct use of smalloc within the code should be avoided where * possible, in favour of these type-casting macros which ensure * you don't mistakenly allocate enough space for one sort of * structure and assign it to a different sort of pointer. * * The nasty trick in sresize with sizeof arranges for the compiler, * in passing, to type-check the expression ((type *)0 == (ptr)), i.e. * to type-check that the input pointer is a pointer to the correct * type. The construction sizeof(stuff) ? (b) : (b) looks like a * violation of the first principle of safe macros, but in fact it's * OK - although it _expands_ the macro parameter more than once, it * only _evaluates_ it once, so it's still side-effect safe. */ #define snew(type) ((type *)snmalloc(1, sizeof(type))) #define snewn(n, type) ((type *)snmalloc((n), sizeof(type))) #define sresize(ptr, n, type) \ ((type *)snrealloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ (n), sizeof(type))) #endif its-playback-time-0.2017-08-30.3c40fd3/puttyps.h000066400000000000000000000003021333656753000205430ustar00rootroot00000000000000/* * Find the platform-specific header for this platform. */ #ifndef PUTTY_PUTTYPS_H #define PUTTY_PUTTYPS_H #ifdef _WINDOWS #include "winstuff.h" #else #include "unix.h" #endif #endif its-playback-time-0.2017-08-30.3c40fd3/sbcs.c000066400000000000000000000022461333656753000177510ustar00rootroot00000000000000/* * sbcs.c - routines to handle single-byte character sets. */ #include "charset.h" #include "internal.h" /* * The charset_spec for any single-byte character set should * provide read_sbcs() as its read function, and its `data' field * should be a wchar_t string constant containing the 256 entries * of the translation table. */ void read_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { const struct sbcs_data *sd = charset->data; UNUSEDARG(state); emit(emitctx, sd->sbcs2ucs[input_chr]); } void write_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { const struct sbcs_data *sd = charset->data; int i, j, k, c; UNUSEDARG(state); /* * Binary-search in the ucs2sbcs table. */ i = -1; j = sd->nvalid; while (i+1 < j) { k = (i+j)/2; c = sd->ucs2sbcs[k]; if (input_chr < sd->sbcs2ucs[c]) j = k; else if (input_chr > sd->sbcs2ucs[c]) i = k; else { emit(emitctx, c); return; } } emit(emitctx, ERROR); } its-playback-time-0.2017-08-30.3c40fd3/sbcs.dat000066400000000000000000002262741333656753000203100ustar00rootroot00000000000000 Data file defining single-byte character sets. All lines which begin with whitespace are considered comments. To generate an SBCS table from a unicode.org mapping table: gensbcs() { wget -q -O - "$1" | tr '\r' '\n' | \ perl -ne '/^(0x.*)\s+(0x.*)\s+/ and $a[hex $1]=sprintf "%04x", hex $2;' \ -e 'BEGIN{for($i=0;$i<256;$i++){$a[$i]="XXXX";' \ -e ' if ($i < 32 or $i == 127) {$a[$i]=sprintf "%04x", $i}}}' \ -e 'END{for($i=0;$i<256;$i++){printf"%s%s",$a[$i],$i%16==15?"\n":" "}}' } (A couple of noteworthy ickinesses here. For a start, any undefined characters in the control-code regions (00-1F and 7F) are assumed to be the Unicode code point corresponding to their index, since the Mac Roman mapping table declines to define them but realistically you don't want to be messing with that sort of thing. Secondly, the Mac mapping tables are shipped with Mac line endings, so note the `tr' to turn them into something legible to Perl...) Here are the ISO-8859-x tables, generated by this piece of Bourne shell: for i in 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16; do echo charset CS_ISO8859_$i gensbcs http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-$i.TXT echo done charset CS_ISO8859_1 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff charset CS_ISO8859_2 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 02d8 0141 00a4 013d 015a 00a7 00a8 0160 015e 0164 0179 00ad 017d 017b 00b0 0105 02db 0142 00b4 013e 015b 02c7 00b8 0161 015f 0165 017a 02dd 017e 017c 0154 00c1 00c2 0102 00c4 0139 0106 00c7 010c 00c9 0118 00cb 011a 00cd 00ce 010e 0110 0143 0147 00d3 00d4 0150 00d6 00d7 0158 016e 00da 0170 00dc 00dd 0162 00df 0155 00e1 00e2 0103 00e4 013a 0107 00e7 010d 00e9 0119 00eb 011b 00ed 00ee 010f 0111 0144 0148 00f3 00f4 0151 00f6 00f7 0159 016f 00fa 0171 00fc 00fd 0163 02d9 charset CS_ISO8859_3 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0126 02d8 00a3 00a4 XXXX 0124 00a7 00a8 0130 015e 011e 0134 00ad XXXX 017b 00b0 0127 00b2 00b3 00b4 00b5 0125 00b7 00b8 0131 015f 011f 0135 00bd XXXX 017c 00c0 00c1 00c2 XXXX 00c4 010a 0108 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf XXXX 00d1 00d2 00d3 00d4 0120 00d6 00d7 011c 00d9 00da 00db 00dc 016c 015c 00df 00e0 00e1 00e2 XXXX 00e4 010b 0109 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef XXXX 00f1 00f2 00f3 00f4 0121 00f6 00f7 011d 00f9 00fa 00fb 00fc 016d 015d 02d9 charset CS_ISO8859_4 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 0138 0156 00a4 0128 013b 00a7 00a8 0160 0112 0122 0166 00ad 017d 00af 00b0 0105 02db 0157 00b4 0129 013c 02c7 00b8 0161 0113 0123 0167 014a 017e 014b 0100 00c1 00c2 00c3 00c4 00c5 00c6 012e 010c 00c9 0118 00cb 0116 00cd 00ce 012a 0110 0145 014c 0136 00d4 00d5 00d6 00d7 00d8 0172 00da 00db 00dc 0168 016a 00df 0101 00e1 00e2 00e3 00e4 00e5 00e6 012f 010d 00e9 0119 00eb 0117 00ed 00ee 012b 0111 0146 014d 0137 00f4 00f5 00f6 00f7 00f8 0173 00fa 00fb 00fc 0169 016b 02d9 charset CS_ISO8859_5 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0401 0402 0403 0404 0405 0406 0407 0408 0409 040a 040b 040c 00ad 040e 040f 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f 2116 0451 0452 0453 0454 0455 0456 0457 0458 0459 045a 045b 045c 00a7 045e 045f charset CS_ISO8859_6 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 XXXX XXXX XXXX 00a4 XXXX XXXX XXXX XXXX XXXX XXXX XXXX 060c 00ad XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 061b XXXX XXXX XXXX 061f XXXX 0621 0622 0623 0624 0625 0626 0627 0628 0629 062a 062b 062c 062d 062e 062f 0630 0631 0632 0633 0634 0635 0636 0637 0638 0639 063a XXXX XXXX XXXX XXXX XXXX 0640 0641 0642 0643 0644 0645 0646 0647 0648 0649 064a 064b 064c 064d 064e 064f 0650 0651 0652 XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX charset CS_ISO8859_7 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 2018 2019 00a3 XXXX XXXX 00a6 00a7 00a8 00a9 XXXX 00ab 00ac 00ad XXXX 2015 00b0 00b1 00b2 00b3 0384 0385 0386 00b7 0388 0389 038a 00bb 038c 00bd 038e 038f 0390 0391 0392 0393 0394 0395 0396 0397 0398 0399 039a 039b 039c 039d 039e 039f 03a0 03a1 XXXX 03a3 03a4 03a5 03a6 03a7 03a8 03a9 03aa 03ab 03ac 03ad 03ae 03af 03b0 03b1 03b2 03b3 03b4 03b5 03b6 03b7 03b8 03b9 03ba 03bb 03bc 03bd 03be 03bf 03c0 03c1 03c2 03c3 03c4 03c5 03c6 03c7 03c8 03c9 03ca 03cb 03cc 03cd 03ce XXXX charset CS_ISO8859_8 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 XXXX 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00d7 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00f7 00bb 00bc 00bd 00be XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 2017 05d0 05d1 05d2 05d3 05d4 05d5 05d6 05d7 05d8 05d9 05da 05db 05dc 05dd 05de 05df 05e0 05e1 05e2 05e3 05e4 05e5 05e6 05e7 05e8 05e9 05ea XXXX XXXX 200e 200f XXXX charset CS_ISO8859_9 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 011e 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 0130 015e 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 011f 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 0131 015f 00ff charset CS_ISO8859_10 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 0112 0122 012a 0128 0136 00a7 013b 0110 0160 0166 017d 00ad 016a 014a 00b0 0105 0113 0123 012b 0129 0137 00b7 013c 0111 0161 0167 017e 2015 016b 014b 0100 00c1 00c2 00c3 00c4 00c5 00c6 012e 010c 00c9 0118 00cb 0116 00cd 00ce 00cf 00d0 0145 014c 00d3 00d4 00d5 00d6 0168 00d8 0172 00da 00db 00dc 00dd 00de 00df 0101 00e1 00e2 00e3 00e4 00e5 00e6 012f 010d 00e9 0119 00eb 0117 00ed 00ee 00ef 00f0 0146 014d 00f3 00f4 00f5 00f6 0169 00f8 0173 00fa 00fb 00fc 00fd 00fe 0138 charset CS_ISO8859_11 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0e01 0e02 0e03 0e04 0e05 0e06 0e07 0e08 0e09 0e0a 0e0b 0e0c 0e0d 0e0e 0e0f 0e10 0e11 0e12 0e13 0e14 0e15 0e16 0e17 0e18 0e19 0e1a 0e1b 0e1c 0e1d 0e1e 0e1f 0e20 0e21 0e22 0e23 0e24 0e25 0e26 0e27 0e28 0e29 0e2a 0e2b 0e2c 0e2d 0e2e 0e2f 0e30 0e31 0e32 0e33 0e34 0e35 0e36 0e37 0e38 0e39 0e3a XXXX XXXX XXXX XXXX 0e3f 0e40 0e41 0e42 0e43 0e44 0e45 0e46 0e47 0e48 0e49 0e4a 0e4b 0e4c 0e4d 0e4e 0e4f 0e50 0e51 0e52 0e53 0e54 0e55 0e56 0e57 0e58 0e59 0e5a 0e5b XXXX XXXX XXXX XXXX charset CS_ISO8859_13 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 201d 00a2 00a3 00a4 201e 00a6 00a7 00d8 00a9 0156 00ab 00ac 00ad 00ae 00c6 00b0 00b1 00b2 00b3 201c 00b5 00b6 00b7 00f8 00b9 0157 00bb 00bc 00bd 00be 00e6 0104 012e 0100 0106 00c4 00c5 0118 0112 010c 00c9 0179 0116 0122 0136 012a 013b 0160 0143 0145 00d3 014c 00d5 00d6 00d7 0172 0141 015a 016a 00dc 017b 017d 00df 0105 012f 0101 0107 00e4 00e5 0119 0113 010d 00e9 017a 0117 0123 0137 012b 013c 0161 0144 0146 00f3 014d 00f5 00f6 00f7 0173 0142 015b 016b 00fc 017c 017e 2019 charset CS_ISO8859_14 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 1e02 1e03 00a3 010a 010b 1e0a 00a7 1e80 00a9 1e82 1e0b 1ef2 00ad 00ae 0178 1e1e 1e1f 0120 0121 1e40 1e41 00b6 1e56 1e81 1e57 1e83 1e60 1ef3 1e84 1e85 1e61 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 0174 00d1 00d2 00d3 00d4 00d5 00d6 1e6a 00d8 00d9 00da 00db 00dc 00dd 0176 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 0175 00f1 00f2 00f3 00f4 00f5 00f6 1e6b 00f8 00f9 00fa 00fb 00fc 00fd 0177 00ff charset CS_ISO8859_15 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 20ac 00a5 0160 00a7 0161 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 017d 00b5 00b6 00b7 017e 00b9 00ba 00bb 0152 0153 0178 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff charset CS_ISO8859_16 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 0105 0141 20ac 201e 0160 00a7 0161 00a9 0218 00ab 0179 00ad 017a 017b 00b0 00b1 010c 0142 017d 201d 00b6 00b7 017e 010d 0219 00bb 0152 0153 0178 017c 00c0 00c1 00c2 0102 00c4 0106 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 0110 0143 00d2 00d3 00d4 0150 00d6 015a 0170 00d9 00da 00db 00dc 0118 021a 00df 00e0 00e1 00e2 0103 00e4 0107 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 0111 0144 00f2 00f3 00f4 0151 00f6 015b 0171 00f9 00fa 00fb 00fc 0119 021b 00ff Some X fonts are encoded in a variant form of ISO8859-1: everything above 0x20 (space) is as normal, but the first 32 characters contain the VT100 line drawing glyphs as they would appear from positions 0x5F to 0x7E inclusive. Here is the modified ISO8859-1 code table. Since this table contains a few duplicated positions, we use the `sortpriority' hint to indicate that things in the main part of the code table (0x20-0xFF) should be generated preferentially when converting _from_ Unicode. Hence, U+00b0 (for example) will yield 0xb0 rather than 0x07. charset CS_ISO8859_1_X11 sortpriority 00-1F -1 0020 2666 2592 2409 240c 240d 240a 00b0 00b1 2424 240b 2518 2510 250c 2514 253c 23ba 23bb 2500 23bc 23bd 251c 2524 2534 252c 2502 2264 2265 03c0 2260 00a3 00b7 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff Here are some PC (old DOS) code pages, generated by this piece of Bourne shell: for i in 437 850 866; do echo charset CS_CP$i gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP$i.TXT echo done charset CS_CP437 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c7 00fc 00e9 00e2 00e4 00e0 00e5 00e7 00ea 00eb 00e8 00ef 00ee 00ec 00c4 00c5 00c9 00e6 00c6 00f4 00f6 00f2 00fb 00f9 00ff 00d6 00dc 00a2 00a3 00a5 20a7 0192 00e1 00ed 00f3 00fa 00f1 00d1 00aa 00ba 00bf 2310 00ac 00bd 00bc 00a1 00ab 00bb 2591 2592 2593 2502 2524 2561 2562 2556 2555 2563 2551 2557 255d 255c 255b 2510 2514 2534 252c 251c 2500 253c 255e 255f 255a 2554 2569 2566 2560 2550 256c 2567 2568 2564 2565 2559 2558 2552 2553 256b 256a 2518 250c 2588 2584 258c 2590 2580 03b1 00df 0393 03c0 03a3 03c3 00b5 03c4 03a6 0398 03a9 03b4 221e 03c6 03b5 2229 2261 00b1 2265 2264 2320 2321 00f7 2248 00b0 2219 00b7 221a 207f 00b2 25a0 00a0 charset CS_CP850 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c7 00fc 00e9 00e2 00e4 00e0 00e5 00e7 00ea 00eb 00e8 00ef 00ee 00ec 00c4 00c5 00c9 00e6 00c6 00f4 00f6 00f2 00fb 00f9 00ff 00d6 00dc 00f8 00a3 00d8 00d7 0192 00e1 00ed 00f3 00fa 00f1 00d1 00aa 00ba 00bf 00ae 00ac 00bd 00bc 00a1 00ab 00bb 2591 2592 2593 2502 2524 00c1 00c2 00c0 00a9 2563 2551 2557 255d 00a2 00a5 2510 2514 2534 252c 251c 2500 253c 00e3 00c3 255a 2554 2569 2566 2560 2550 256c 00a4 00f0 00d0 00ca 00cb 00c8 0131 00cd 00ce 00cf 2518 250c 2588 2584 00a6 00cc 2580 00d3 00df 00d4 00d2 00f5 00d5 00b5 00fe 00de 00da 00db 00d9 00fd 00dd 00af 00b4 00ad 00b1 2017 00be 00b6 00a7 00f7 00b8 00b0 00a8 00b7 00b9 00b3 00b2 25a0 00a0 charset CS_CP866 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 2591 2592 2593 2502 2524 2561 2562 2556 2555 2563 2551 2557 255d 255c 255b 2510 2514 2534 252c 251c 2500 253c 255e 255f 255a 2554 2569 2566 2560 2550 256c 2567 2568 2564 2565 2559 2558 2552 2553 256b 256a 2518 250c 2588 2584 258c 2590 2580 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f 0401 0451 0404 0454 0407 0457 040e 045e 00b0 2219 00b7 221a 2116 00a4 25a0 00a0 Another old DOS code page, submitted by a user and checked against the translation table at http://msdn.microsoft.com/en-us/goglobal/cc305161.aspx . charset CS_CP852 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c7 00fc 00e9 00e2 00e4 016f 0107 00e7 0142 00eb 0150 0151 00ee 0179 00c4 0106 00c9 0139 013a 00f4 00f6 013d 013e 015a 015b 00d6 00dc 0164 0165 0141 00d7 010d 00e1 00ed 00f3 00fa 0104 0105 017d 017e 0118 0119 00ac 017a 010c 015f 00ab 00bb 2591 2592 2593 2502 2524 00c1 00c2 011a 015e 2563 2551 2557 255d 017b 017c 2510 2514 2534 252c 251c 2500 253c 0102 0103 255a 2554 2569 2566 2560 2550 256c 00a4 0111 0110 010e 00cb 010f 0147 00cd 00ce 011b 2518 250c 2588 2584 0162 016e 2580 00d3 00df 00d4 0143 0144 0148 0160 0161 0154 00da 0155 0170 00fd 00dd 0163 00b4 00ad 02dd 02db 02c7 02d8 00a7 00f7 00b8 00b0 00a8 02d9 0171 0158 0159 25a0 00a0 Here are some Windows code pages, generated by this piece of Bourne shell: for i in 1250 1251 1252 1253 1254 1255 1256 1257 1258; do echo charset CS_CP$i gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP$i.TXT echo done charset CS_CP1250 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac XXXX 201a XXXX 201e 2026 2020 2021 XXXX 2030 0160 2039 015a 0164 017d 0179 XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 0161 203a 015b 0165 017e 017a 00a0 02c7 02d8 0141 00a4 0104 00a6 00a7 00a8 00a9 015e 00ab 00ac 00ad 00ae 017b 00b0 00b1 02db 0142 00b4 00b5 00b6 00b7 00b8 0105 015f 00bb 013d 02dd 013e 017c 0154 00c1 00c2 0102 00c4 0139 0106 00c7 010c 00c9 0118 00cb 011a 00cd 00ce 010e 0110 0143 0147 00d3 00d4 0150 00d6 00d7 0158 016e 00da 0170 00dc 00dd 0162 00df 0155 00e1 00e2 0103 00e4 013a 0107 00e7 010d 00e9 0119 00eb 011b 00ed 00ee 010f 0111 0144 0148 00f3 00f4 0151 00f6 00f7 0159 016f 00fa 0171 00fc 00fd 0163 02d9 charset CS_CP1251 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0402 0403 201a 0453 201e 2026 2020 2021 20ac 2030 0409 2039 040a 040c 040b 040f 0452 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 0459 203a 045a 045c 045b 045f 00a0 040e 045e 0408 00a4 0490 00a6 00a7 0401 00a9 0404 00ab 00ac 00ad 00ae 0407 00b0 00b1 0406 0456 0491 00b5 00b6 00b7 0451 2116 0454 00bb 0458 0405 0455 0457 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f charset CS_CP1252 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 0160 2039 0152 XXXX 017d XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 0161 203a 0153 XXXX 017e 0178 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff charset CS_CP1253 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac XXXX 201a 0192 201e 2026 2020 2021 XXXX 2030 XXXX 2039 XXXX XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 XXXX 203a XXXX XXXX XXXX XXXX 00a0 0385 0386 00a3 00a4 00a5 00a6 00a7 00a8 00a9 XXXX 00ab 00ac 00ad 00ae 2015 00b0 00b1 00b2 00b3 0384 00b5 00b6 00b7 0388 0389 038a 00bb 038c 00bd 038e 038f 0390 0391 0392 0393 0394 0395 0396 0397 0398 0399 039a 039b 039c 039d 039e 039f 03a0 03a1 XXXX 03a3 03a4 03a5 03a6 03a7 03a8 03a9 03aa 03ab 03ac 03ad 03ae 03af 03b0 03b1 03b2 03b3 03b4 03b5 03b6 03b7 03b8 03b9 03ba 03bb 03bc 03bd 03be 03bf 03c0 03c1 03c2 03c3 03c4 03c5 03c6 03c7 03c8 03c9 03ca 03cb 03cc 03cd 03ce XXXX charset CS_CP1254 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 0160 2039 0152 XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 0161 203a 0153 XXXX XXXX 0178 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 011e 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 0130 015e 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 011f 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 0131 015f 00ff charset CS_CP1255 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 XXXX 2039 XXXX XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 XXXX 203a XXXX XXXX XXXX XXXX 00a0 00a1 00a2 00a3 20aa 00a5 00a6 00a7 00a8 00a9 00d7 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00f7 00bb 00bc 00bd 00be 00bf 05b0 05b1 05b2 05b3 05b4 05b5 05b6 05b7 05b8 05b9 XXXX 05bb 05bc 05bd 05be 05bf 05c0 05c1 05c2 05c3 05f0 05f1 05f2 05f3 05f4 XXXX XXXX XXXX XXXX XXXX XXXX XXXX 05d0 05d1 05d2 05d3 05d4 05d5 05d6 05d7 05d8 05d9 05da 05db 05dc 05dd 05de 05df 05e0 05e1 05e2 05e3 05e4 05e5 05e6 05e7 05e8 05e9 05ea XXXX XXXX 200e 200f XXXX charset CS_CP1256 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac 067e 201a 0192 201e 2026 2020 2021 02c6 2030 0679 2039 0152 0686 0698 0688 06af 2018 2019 201c 201d 2022 2013 2014 06a9 2122 0691 203a 0153 200c 200d 06ba 00a0 060c 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 06be 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 061b 00bb 00bc 00bd 00be 061f 06c1 0621 0622 0623 0624 0625 0626 0627 0628 0629 062a 062b 062c 062d 062e 062f 0630 0631 0632 0633 0634 0635 0636 00d7 0637 0638 0639 063a 0640 0641 0642 0643 00e0 0644 00e2 0645 0646 0647 0648 00e7 00e8 00e9 00ea 00eb 0649 064a 00ee 00ef 064b 064c 064d 064e 00f4 064f 0650 00f7 0651 00f9 0652 00fb 00fc 200e 200f 06d2 charset CS_CP1257 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac XXXX 201a XXXX 201e 2026 2020 2021 XXXX 2030 XXXX 2039 XXXX 00a8 02c7 00b8 XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 XXXX 203a XXXX 00af 02db XXXX 00a0 XXXX 00a2 00a3 00a4 XXXX 00a6 00a7 00d8 00a9 0156 00ab 00ac 00ad 00ae 00c6 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00f8 00b9 0157 00bb 00bc 00bd 00be 00e6 0104 012e 0100 0106 00c4 00c5 0118 0112 010c 00c9 0179 0116 0122 0136 012a 013b 0160 0143 0145 00d3 014c 00d5 00d6 00d7 0172 0141 015a 016a 00dc 017b 017d 00df 0105 012f 0101 0107 00e4 00e5 0119 0113 010d 00e9 017a 0117 0123 0137 012b 013c 0161 0144 0146 00f3 014d 00f5 00f6 00f7 0173 0142 015b 016b 00fc 017c 017e 02d9 charset CS_CP1258 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 XXXX 2039 0152 XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 XXXX 203a 0153 XXXX XXXX 0178 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 0102 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 0300 00cd 00ce 00cf 0110 00d1 0309 00d3 00d4 01a0 00d6 00d7 00d8 00d9 00da 00db 00dc 01af 0303 00df 00e0 00e1 00e2 0103 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 0301 00ed 00ee 00ef 0111 00f1 0323 00f3 00f4 01a1 00f6 00f7 00f8 00f9 00fa 00fb 00fc 01b0 20ab 00ff KOI8-R, generated by this code: { echo charset CS_KOI8_R; gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT; } charset CS_KOI8_R 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 2500 2502 250c 2510 2514 2518 251c 2524 252c 2534 253c 2580 2584 2588 258c 2590 2591 2592 2593 2320 25a0 2219 221a 2248 2264 2265 00a0 2321 00b0 00b2 00b7 00f7 2550 2551 2552 0451 2553 2554 2555 2556 2557 2558 2559 255a 255b 255c 255d 255e 255f 2560 2561 0401 2562 2563 2564 2565 2566 2567 2568 2569 256a 256b 256c 00a9 044e 0430 0431 0446 0434 0435 0444 0433 0445 0438 0439 043a 043b 043c 043d 043e 043f 044f 0440 0441 0442 0443 0436 0432 044c 044b 0437 0448 044d 0449 0447 044a 042e 0410 0411 0426 0414 0415 0424 0413 0425 0418 0419 041a 041b 041c 041d 041e 041f 042f 0420 0421 0422 0423 0416 0412 042c 042b 0417 0428 042d 0429 0427 042a KOI8-U: I can't find an easily machine-processable mapping table for this one, so I've created it by hand-editing the KOI8-R mapping table in accordance with the list of differences specified in RFC2319. Note that RFC2319 has an apparent error: position B4 is listed as U+0404 in the main character set list, but as U+0403 in Appendix A (differences from KOI8-R). Both agree that it should be CYRILLIC CAPITAL LETTER UKRAINIAN IE, however, and the Unicode character database says that therefore U+0404 is the correct value. charset CS_KOI8_U 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 2500 2502 250c 2510 2514 2518 251c 2524 252c 2534 253c 2580 2584 2588 258c 2590 2591 2592 2593 2320 25a0 2219 221a 2248 2264 2265 00a0 2321 00b0 00b2 00b7 00f7 2550 2551 2552 0451 0454 2554 0456 0457 2557 2558 2559 255a 255b 0491 255d 255e 255f 2560 2561 0401 0404 2563 0406 0407 2566 2567 2568 2569 256a 0490 256c 00a9 044e 0430 0431 0446 0434 0435 0444 0433 0445 0438 0439 043a 043b 043c 043d 043e 043f 044f 0440 0441 0442 0443 0436 0432 044c 044b 0437 0448 044d 0449 0447 044a 042e 0410 0411 0426 0414 0415 0424 0413 0425 0418 0419 041a 041b 041c 041d 041e 041f 042f 0420 0421 0422 0423 0416 0412 042c 042b 0417 0428 042d 0429 0427 042a Various Mac character sets, generated by: for i in ROMAN TURKISH CROATIAN ICELAND ROMANIAN GREEK CYRILLIC THAI \ CENTEURO SYMBOL DINGBATS; do echo charset CS_MAC_$i gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/$i.TXT | \ sed s/f8a0/XXXX/ echo done The code point F8FF at position F0 in Mac OS Roman an interesting one. In Unicode, it's the last of the Private Use section. The mapping table states that it should be an Apple logo. I suppose we should just leave it as it is; there's bound to be some software out there that understands U+F8FF to be an Apple logo! Code point F8A0 at position F5 in Mac OS Turkish is actually just an undefined character, so we make it properly undefined. Many of the positions 80-9F in Mac OS Thai are for presentation forms of other characters. When converting from Unicode, we use `sortpriority' to avoid them. Positions E2-E4 in Mac OS Symbol are for sans-serif variants of other characters. Similarly, we avoid them. charset CS_MAC_ROMAN 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 2039 203a fb01 fb02 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_TURKISH 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 011e 011f 0130 0131 015e 015f 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 XXXX 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_CROATIAN 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 0160 2122 00b4 00a8 2260 017d 00d8 221e 00b1 2264 2265 2206 00b5 2202 2211 220f 0161 222b 00aa 00ba 03a9 017e 00f8 00bf 00a1 00ac 221a 0192 2248 0106 00ab 010c 2026 00a0 00c0 00c3 00d5 0152 0153 0110 2014 201c 201d 2018 2019 00f7 25ca f8ff 00a9 2044 20ac 2039 203a 00c6 00bb 2013 00b7 201a 201e 2030 00c2 0107 00c1 010d 00c8 00cd 00ce 00cf 00cc 00d3 00d4 0111 00d2 00da 00db 00d9 0131 02c6 02dc 00af 03c0 00cb 02da 00b8 00ca 00e6 02c7 charset CS_MAC_ICELAND 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 00d0 00f0 00de 00fe 00fd 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_ROMANIAN 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 0102 0218 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 0103 0219 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 2039 203a 021a 021b 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_GREEK 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00b9 00b2 00c9 00b3 00d6 00dc 0385 00e0 00e2 00e4 0384 00a8 00e7 00e9 00e8 00ea 00eb 00a3 2122 00ee 00ef 2022 00bd 2030 00f4 00f6 00a6 20ac 00f9 00fb 00fc 2020 0393 0394 0398 039b 039e 03a0 00df 00ae 00a9 03a3 03aa 00a7 2260 00b0 00b7 0391 00b1 2264 2265 00a5 0392 0395 0396 0397 0399 039a 039c 03a6 03ab 03a8 03a9 03ac 039d 00ac 039f 03a1 2248 03a4 00ab 00bb 2026 00a0 03a5 03a7 0386 0388 0153 2013 2015 201c 201d 2018 2019 00f7 0389 038a 038c 038e 03ad 03ae 03af 03cc 038f 03cd 03b1 03b2 03c8 03b4 03b5 03c6 03b3 03b7 03b9 03be 03ba 03bb 03bc 03bd 03bf 03c0 03ce 03c1 03c3 03c4 03b8 03c9 03c2 03c7 03c5 03b6 03ca 03cb 0390 03b0 00ad charset CS_MAC_CYRILLIC 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 2020 00b0 0490 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453 221e 00b1 2264 2265 0456 00b5 0491 0408 0404 0454 0407 0457 0409 0459 040a 045a 0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455 2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 20ac charset CS_MAC_THAI sortpriority 83-8C -1 sortpriority 8F-8F -1 sortpriority 92-9C -1 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00ab 00bb 2026 0e48 0e49 0e4a 0e4b 0e4c 0e48 0e49 0e4a 0e4b 0e4c 201c 201d 0e4d XXXX 2022 0e31 0e47 0e34 0e35 0e36 0e37 0e48 0e49 0e4a 0e4b 0e4c 2018 2019 XXXX 00a0 0e01 0e02 0e03 0e04 0e05 0e06 0e07 0e08 0e09 0e0a 0e0b 0e0c 0e0d 0e0e 0e0f 0e10 0e11 0e12 0e13 0e14 0e15 0e16 0e17 0e18 0e19 0e1a 0e1b 0e1c 0e1d 0e1e 0e1f 0e20 0e21 0e22 0e23 0e24 0e25 0e26 0e27 0e28 0e29 0e2a 0e2b 0e2c 0e2d 0e2e 0e2f 0e30 0e31 0e32 0e33 0e34 0e35 0e36 0e37 0e38 0e39 0e3a 2060 200b 2013 2014 0e3f 0e40 0e41 0e42 0e43 0e44 0e45 0e46 0e47 0e48 0e49 0e4a 0e4b 0e4c 0e4d 2122 0e4f 0e50 0e51 0e52 0e53 0e54 0e55 0e56 0e57 0e58 0e59 00ae 00a9 XXXX XXXX XXXX XXXX charset CS_MAC_CENTEURO 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 0100 0101 00c9 0104 00d6 00dc 00e1 0105 010c 00e4 010d 0106 0107 00e9 0179 017a 010e 00ed 010f 0112 0113 0116 00f3 0117 00f4 00f6 00f5 00fa 011a 011b 00fc 2020 00b0 0118 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 0119 00a8 2260 0123 012e 012f 012a 2264 2265 012b 0136 2202 2211 0142 013b 013c 013d 013e 0139 013a 0145 0146 0143 00ac 221a 0144 0147 2206 00ab 00bb 2026 00a0 0148 0150 00d5 0151 014c 2013 2014 201c 201d 2018 2019 00f7 25ca 014d 0154 0155 0158 2039 203a 0159 0156 0157 0160 201a 201e 0161 015a 015b 00c1 0164 0165 00cd 017d 017e 016a 00d3 00d4 016b 016e 00da 016f 0170 0171 0172 0173 00dd 00fd 0137 017b 0141 017c 0122 02c7 charset CS_MAC_SYMBOL sortpriority E2-E4 -1 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 2200 0023 2203 0025 0026 220d 0028 0029 2217 002b 002c 2212 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 2245 0391 0392 03a7 0394 0395 03a6 0393 0397 0399 03d1 039a 039b 039c 039d 039f 03a0 0398 03a1 03a3 03a4 03a5 03c2 03a9 039e 03a8 0396 005b 2234 005d 22a5 005f f8e5 03b1 03b2 03c7 03b4 03b5 03c6 03b3 03b7 03b9 03d5 03ba 03bb 03bc 03bd 03bf 03c0 03b8 03c1 03c3 03c4 03c5 03d6 03c9 03be 03c8 03b6 007b 007c 007d 223c 007f XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 20ac 03d2 2032 2264 2044 221e 0192 2663 2666 2665 2660 2194 2190 2191 2192 2193 00b0 00b1 2033 2265 00d7 221d 2202 2022 00f7 2260 2261 2248 2026 f8e6 23af 21b5 2135 2111 211c 2118 2297 2295 2205 2229 222a 2283 2287 2284 2282 2286 2208 2209 2220 2207 00ae 00a9 2122 220f 221a 22c5 00ac 2227 2228 21d4 21d0 21d1 21d2 21d3 22c4 3008 00ae 00a9 2122 2211 239b 239c 239d 23a1 23a2 23a3 23a7 23a8 23a9 23aa f8ff 3009 222b 2320 23ae 2321 239e 239f 23a0 23a4 23a5 23a6 23ab 23ac 23ad XXXX charset CS_MAC_DINGBATS 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 2701 2702 2703 2704 260e 2706 2707 2708 2709 261b 261e 270c 270d 270e 270f 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 271a 271b 271c 271d 271e 271f 2720 2721 2722 2723 2724 2725 2726 2727 2605 2729 272a 272b 272c 272d 272e 272f 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 273a 273b 273c 273d 273e 273f 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 274a 274b 25cf 274d 25a0 274f 2750 2751 2752 25b2 25bc 25c6 2756 25d7 2758 2759 275a 275b 275c 275d 275e 007f 2768 2769 276a 276b 276c 276d 276e 276f 2770 2771 2772 2773 2774 2775 XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 2761 2762 2763 2764 2765 2766 2767 2663 2666 2665 2660 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2776 2777 2778 2779 277a 277b 277c 277d 277e 277f 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 278a 278b 278c 278d 278e 278f 2790 2791 2792 2793 2794 2192 2194 2195 2798 2799 279a 279b 279c 279d 279e 279f 27a0 27a1 27a2 27a3 27a4 27a5 27a6 27a7 27a8 27a9 27aa 27ab 27ac 27ad 27ae 27af XXXX 27b1 27b2 27b3 27b4 27b5 27b6 27b7 27b8 27b9 27ba 27bb 27bc 27bd 27be XXXX Various Mac character sets have older (usually pre-Euro) variants which are documented in the comments in their mapping tables. I've manually applied these changes below. Mac OS Roman variants before Mac OS 8.5 (CURRENCY SIGN rather than EURO SIGN): charset CS_MAC_ROMAN_OLD 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 2039 203a fb01 fb02 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_CROATIAN_OLD 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 0160 2122 00b4 00a8 2260 017d 00d8 221e 00b1 2264 2265 2206 00b5 2202 2211 220f 0161 222b 00aa 00ba 03a9 017e 00f8 00bf 00a1 00ac 221a 0192 2248 0106 00ab 010c 2026 00a0 00c0 00c3 00d5 0152 0153 0110 2014 201c 201d 2018 2019 00f7 25ca f8ff 00a9 2044 00a4 2039 203a 00c6 00bb 2013 00b7 201a 201e 2030 00c2 0107 00c1 010d 00c8 00cd 00ce 00cf 00cc 00d3 00d4 0111 00d2 00da 00db 00d9 0131 02c6 02dc 00af 03c0 00cb 02da 00b8 00ca 00e6 02c7 charset CS_MAC_ICELAND_OLD 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 00d0 00f0 00de 00fe 00fd 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_ROMANIAN_OLD 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 0102 0218 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 0103 0219 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 2039 203a 021a 021b 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 Mac OS Greek before Mac OS 9.2.2 (SOFT HYPHEN instead of EURO SIGN, and undefined instead of SOFT HYPHEN). charset CS_MAC_GREEK_OLD 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 00c4 00b9 00b2 00c9 00b3 00d6 00dc 0385 00e0 00e2 00e4 0384 00a8 00e7 00e9 00e8 00ea 00eb 00a3 2122 00ee 00ef 2022 00bd 2030 00f4 00f6 00a6 00ad 00f9 00fb 00fc 2020 0393 0394 0398 039b 039e 03a0 00df 00ae 00a9 03a3 03aa 00a7 2260 00b0 00b7 0391 00b1 2264 2265 00a5 0392 0395 0396 0397 0399 039a 039c 03a6 03ab 03a8 03a9 03ac 039d 00ac 039f 03a1 2248 03a4 00ab 00bb 2026 00a0 03a5 03a7 0386 0388 0153 2013 2015 201c 201d 2018 2019 00f7 0389 038a 038c 038e 03ad 03ae 03af 03cc 038f 03cd 03b1 03b2 03c8 03b4 03b5 03c6 03b3 03b7 03b9 03be 03ba 03bb 03bc 03bd 03bf 03c0 03ce 03c1 03c3 03c4 03b8 03c9 03c2 03c7 03c5 03b6 03ca 03cb 0390 03b0 XXXX Mac OS Cyrillic before Mac OS 9.0 (CENT SIGN instead of CYRILLIC CAPITAL LETTER GHE WITH UPTURN, PARTIAL DIFFERENTIAL instead of CYRILLIC SMALL LETTER GHE WITH UPTURN, CURRENCY SIGN instead of EURO SIGN): charset CS_MAC_CYRILLIC_OLD 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 2020 00b0 00a2 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453 221e 00b1 2264 2265 0456 00b5 2022 0408 0404 0454 0407 0457 0409 0459 040a 045a 0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455 2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 00a4 Mac OS Ukrainian (now Cyrillic) before Mac OS 9.0 (CURRENCY SIGN instead of EURO SIGN): charset CS_MAC_UKRAINE 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 2020 00b0 0490 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453 221e 00b1 2264 2265 0456 00b5 0491 0408 0404 0454 0407 0457 0409 0459 040a 045a 0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455 2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 00a4 Mac OS VT100 character set, as used by the "VT100" font. Basically Mac OS Roman hacked about to give it an almost-Latin1 repertoire and most of the VT100 line-drawing set too. Point CA is the backward question-mark used for silo overflows. This table was derived by pasting the relevant part of 'utom' 140 from the "Western Language Encodings" file shipped with TEC 1.5 and then manually fixing up the scan line characters to use the Unicode 3.2 HORIZONTAL SCAN LINE characters rather than UPPER ONE EIGHTH BLOCK and LOWER ONE EIGHTH BLOCK with transcoding hints. charset CS_MAC_VT100 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 240a 240b 240c 240d 240e 240f 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 241a 241b 241c 241d 241e 241f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 2421 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 00b8 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 00d7 00b1 2264 2265 00a5 00b5 00b9 00b2 00b3 03c0 00a6 00aa 00ba 2592 00e6 00f8 00bf 00a1 00ac 00bd 0192 00bc 00be 00ab 00bb 2026 XXXX 00c0 00c3 00d5 0152 0153 2013 2014 2518 2510 250c 2514 00f7 2022 00ff 0178 253c 20ac 00d0 00f0 00fe 00de 00fd 00b7 23ba 23bb 2500 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 XXXX 00d2 00da 00db 00d9 23bc 23bd 251c 2524 2534 252c 2502 XXXX XXXX XXXX XXXX As with so many others, before Mac OS 8.5 this font had CURRENCY SIGN rather than EURO SIGN. charset CS_MAC_VT100_OLD 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 240a 240b 240c 240d 240e 240f 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 241a 241b 241c 241d 241e 241f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 2421 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 00b8 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 00d7 00b1 2264 2265 00a5 00b5 00b9 00b2 00b3 03c0 00a6 00aa 00ba 2592 00e6 00f8 00bf 00a1 00ac 00bd 0192 00bc 00be 00ab 00bb 2026 XXXX 00c0 00c3 00d5 0152 0153 2013 2014 2518 2510 250c 2514 00f7 2022 00ff 0178 253c 00a4 00d0 00f0 00fe 00de 00fd 00b7 23ba 23bb 2500 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 XXXX 00d2 00da 00db 00d9 23bc 23bd 251c 2524 2534 252c 2502 XXXX XXXX XXXX XXXX Roman Czyborra's web site (http://czyborra.com/) has a variety of other useful mapping tables, in a slightly different format (and gzipped). Here's a shell/Perl function to generate an SBCS table from a Czyborra mapping table: gensbcs_c() { wget -q -O - "$1" | gzip -d | \ perl -ne '/^=(.*)\s+U\+(.*)\s+/ and $a[hex $1]=sprintf "%04x", hex $2;' \ -e 'BEGIN{for($i=0;$i<256;$i++){$a[$i]="XXXX";' \ -e 'if ($i < 32 or ($i >=127 and $i < 160)) {$a[$i]=sprintf "%04x", $i}}}' \ -e 'END{for($i=0;$i<256;$i++){printf"%s%s",$a[$i],$i%16==15?"\n":" "}}' } So here we have some character sets generated from Czyborra mapping tables: VISCII, HP-Roman8, and the DEC Multinational Character Set. { echo charset CS_VISCII; gensbcs_c http://czyborra.com/charsets/viscii.txt.gz; echo; echo charset CS_HP_ROMAN8; gensbcs_c http://czyborra.com/charsets/hp-roman8.txt.gz; echo; echo charset CS_DEC_MCS; gensbcs_c http://czyborra.com/charsets/dec-mcs.txt.gz; echo; } charset CS_VISCII 0000 0001 1eb2 0003 0004 1eb4 1eaa 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 1ef6 0015 0016 0017 0018 1ef8 001a 001b 001c 001d 1ef4 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 1ea0 1eae 1eb0 1eb6 1ea4 1ea6 1ea8 1eac 1ebc 1eb8 1ebe 1ec0 1ec2 1ec4 1ec6 1ed0 1ed2 1ed4 1ed6 1ed8 1ee2 1eda 1edc 1ede 1eca 1ece 1ecc 1ec8 1ee6 0168 1ee4 1ef2 00d5 1eaf 1eb1 1eb7 1ea5 1ea7 1ea8 1ead 1ebd 1eb9 1ebf 1ec1 1ec3 1ec5 1ec7 1ed1 1ed3 1ed5 1ed7 1ee0 01a0 1ed9 1edd 1edf 1ecb 1ef0 1ee8 1eea 1eec 01a1 1edb 01af 00c0 00c1 00c2 00c3 1ea2 0102 1eb3 1eb5 00c8 00c9 00ca 1eba 00cc 00cd 0128 1ef3 0110 1ee9 00d2 00d3 00d4 1ea1 1ef7 1eeb 1eed 00d9 00da 1ef9 1ef5 00dd 1ee1 01b0 00e0 00e1 00e2 00e3 1ea3 0103 1eef 1eab 00e8 00e9 00ea 1ebb 00ec 00ed 0129 1ec9 0111 1ef1 00f2 00f3 00f4 00f5 1ecf 1ecd 1ee5 00f9 00fa 0169 1ee7 00fd 1ee3 1eee charset CS_HP_ROMAN8 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00c0 00c2 00c8 00ca 00cb 00ce 00cf 00b4 02cb 02c6 00a8 02dc 00d9 00db 20a4 00af 00dd 00fd 00b0 00c7 00e7 00d1 00f1 00a1 00bf 00a4 00a3 00a5 00a7 0192 00a2 00e2 00ea 00f4 00fb 00e1 00e9 00f3 00fa 00e0 00e8 00f2 00f9 00e4 00eb 00f6 00fc 00c5 00ee 00d8 00c6 00e5 00ed 00f8 00e6 00c4 00ec 00d6 00dc 00c9 00ef 00df 00d4 00c1 00c3 00e3 00d0 00f0 00cd 00cc 00d3 00d2 00d5 00f5 0160 0161 00da 0178 00ff 00de 00fe 00b7 00b5 00b6 00be 2014 00bc 00bd 00aa 00ba 00ab 25a0 00bb 00b1 XXXX charset CS_DEC_MCS 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001a 001b 001c 001d 001e 001f 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002a 002b 002c 002d 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004a 004b 004c 004d 004e 004f 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005a 005b 005c 005d 005e 005f 0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006a 006b 006c 006d 006e 006f 0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007a 007b 007c 007d 007e 007f 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f XXXX 00a1 00a2 00a3 XXXX 00a5 XXXX 00a7 00a4 00a9 00aa 00ab XXXX XXXX XXXX XXXX 00b0 00b1 00b2 00b3 XXXX 00b5 00b6 00b7 XXXX 00b9 00ba 00bb 00bc 00bd XXXX 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf XXXX 00d1 00d2 00d3 00d4 00d5 00d6 0152 00d8 00d9 00da 00db 00dc 0178 XXXX 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef XXXX 00f1 00f2 00f3 00f4 00f5 00f6 0153 00f8 00f9 00fa 00fb 00fc 00ff XXXX XXXX its-playback-time-0.2017-08-30.3c40fd3/sbcsgen.pl000066400000000000000000000054301333656753000206320ustar00rootroot00000000000000#!/usr/bin/env perl -w # This script generates sbcsdat.c (the data for all the SBCSes) from its # source form sbcs.dat. $infile = shift @ARGV; $infile = "sbcs.dat" unless defined $infile; $outfile = "sbcsdat.c"; open FOO, $infile; open BAR, ">$outfile"; select BAR; print "/*\n"; print " * sbcsdat.c - data definitions for single-byte character sets.\n"; print " *\n"; print " * Generated by sbcsgen.pl from sbcs.dat.\n"; print " * You should edit those files rather than editing this one.\n"; print " */\n"; print "\n"; print "#ifndef ENUM_CHARSETS\n"; print "\n"; print "#include \"charset.h\"\n"; print "#include \"internal.h\"\n"; print "\n"; my $charsetname = undef; my @vals = (); my @charsetnames = (); my @sortpriority = (); while () { chomp; if (/^charset (.*)$/) { $charsetname = $1; @vals = (); @sortpriority = map { 0 } 0..255; } elsif (/^sortpriority ([^-]*)-([^-]*) (.*)$/) { for ($i = hex $1; $i <= hex $2; $i++) { $sortpriority[$i] += $3; } } elsif (/^[0-9a-fA-FX]/) { push @vals, map { $_ eq "XXXX" ? -1 : hex $_ } split / +/, $_; if (scalar @vals > 256) { die "$infile:$.: charset $charsetname has more than 256 values\n"; } elsif (scalar @vals == 256) { &outcharset($charsetname, \@vals, \@sortpriority); push @charsetnames, $charsetname; $charsetname = undef; @vals = (); @sortpriority = map { 0 } 0..255; } } } print "#else /* ENUM_CHARSETS */\n"; print "\n"; foreach $i (@charsetnames) { print "ENUM_CHARSET($i)\n"; } print "\n"; print "#endif /* ENUM_CHARSETS */\n"; sub outcharset($$$) { my ($name, $vals, $sortpriority) = @_; my ($prefix, $i, @sorted); print "static const sbcs_data data_$name = {\n"; print " {\n"; $prefix = " "; @sorted = (); for ($i = 0; $i < 256; $i++) { if ($vals->[$i] < 0) { printf "%sERROR ", $prefix; } else { printf "%s0x%04x", $prefix, $vals->[$i]; die "ooh? $i\n" unless defined $sortpriority->[$i]; push @sorted, [$i, $vals->[$i], 0+$sortpriority->[$i]]; } if ($i % 8 == 7) { $prefix = ",\n "; } else { $prefix = ", "; } } print "\n },\n {\n"; @sorted = sort { ($a->[1] == $b->[1] ? $b->[2] <=> $a->[2] : $a->[1] <=> $b->[1]) || $a->[0] <=> $b->[0] } @sorted; $prefix = " "; $uval = -1; for ($i = $j = 0; $i < scalar @sorted; $i++) { next if ($uval == $sorted[$i]->[1]); # low-priority alternative $uval = $sorted[$i]->[1]; printf "%s0x%02x", $prefix, $sorted[$i]->[0]; if ($j % 8 == 7) { $prefix = ",\n "; } else { $prefix = ", "; } $j++; } printf "\n },\n %d\n", $j; print "};\n"; print "const charset_spec charset_$name = {\n" . " $name, read_sbcs, write_sbcs, &data_$name\n};\n\n"; } its-playback-time-0.2017-08-30.3c40fd3/settings.c000066400000000000000000001460671333656753000206710ustar00rootroot00000000000000/* * settings.c: read and write saved sessions. (platform-independent) */ #include #include #include #include "putty.h" #include "storage.h" /* The cipher order given here is the default order. */ static const struct keyvalwhere ciphernames[] = { { "aes", CIPHER_AES, -1, -1 }, { "chacha20", CIPHER_CHACHA20, CIPHER_AES, +1 }, { "blowfish", CIPHER_BLOWFISH, -1, -1 }, { "3des", CIPHER_3DES, -1, -1 }, { "WARN", CIPHER_WARN, -1, -1 }, { "arcfour", CIPHER_ARCFOUR, -1, -1 }, { "des", CIPHER_DES, -1, -1 } }; /* The default order here is sometimes overridden by the backward- * compatibility warts in load_open_settings(), and should be kept * in sync with those. */ static const struct keyvalwhere kexnames[] = { { "ecdh", KEX_ECDH, -1, +1 }, /* This name is misleading: it covers both SHA-256 and SHA-1 variants */ { "dh-gex-sha1", KEX_DHGEX, -1, -1 }, { "dh-group14-sha1", KEX_DHGROUP14, -1, -1 }, { "dh-group1-sha1", KEX_DHGROUP1, KEX_WARN, +1 }, { "rsa", KEX_RSA, KEX_WARN, -1 }, { "WARN", KEX_WARN, -1, -1 } }; static const struct keyvalwhere hknames[] = { { "ed25519", HK_ED25519, -1, +1 }, { "ecdsa", HK_ECDSA, -1, -1 }, { "dsa", HK_DSA, -1, -1 }, { "rsa", HK_RSA, -1, -1 }, { "WARN", HK_WARN, -1, -1 }, }; /* * All the terminal modes that we know about for the "TerminalModes" * setting. (Also used by config.c for the drop-down list.) * This is currently precisely the same as the set in ssh.c, but could * in principle differ if other backends started to support tty modes * (e.g., the pty backend). * The set of modes in in this array is currently significant for * settings migration from old versions; if they change, review the * gppmap() invocation for "TerminalModes". */ const char *const ttymodes[] = { "INTR", "QUIT", "ERASE", "KILL", "EOF", "EOL", "EOL2", "START", "STOP", "SUSP", "DSUSP", "REPRINT", "WERASE", "LNEXT", "FLUSH", "SWTCH", "STATUS", "DISCARD", "IGNPAR", "PARMRK", "INPCK", "ISTRIP", "INLCR", "IGNCR", "ICRNL", "IUCLC", "IXON", "IXANY", "IXOFF", "IMAXBEL", "IUTF8", "ISIG", "ICANON", "XCASE", "ECHO", "ECHOE", "ECHOK", "ECHONL", "NOFLSH", "TOSTOP", "IEXTEN", "ECHOCTL", "ECHOKE", "PENDIN", "OPOST", "OLCUC", "ONLCR", "OCRNL", "ONOCR", "ONLRET", "CS7", "CS8", "PARENB", "PARODD", NULL }; /* * Convenience functions to access the backends[] array * (which is only present in tools that manage settings). */ Backend *backend_from_name(const char *name) { Backend **p; for (p = backends; *p != NULL; p++) if (!strcmp((*p)->name, name)) return *p; return NULL; } Backend *backend_from_proto(int proto) { Backend **p; for (p = backends; *p != NULL; p++) if ((*p)->protocol == proto) return *p; return NULL; } char *get_remote_username(Conf *conf) { char *username = conf_get_str(conf, CONF_username); if (*username) { return dupstr(username); } else if (conf_get_int(conf, CONF_username_from_env)) { /* Use local username. */ return get_username(); /* might still be NULL */ } else { return NULL; } } static char *gpps_raw(void *handle, const char *name, const char *def) { char *ret = read_setting_s(handle, name); if (!ret) ret = platform_default_s(name); if (!ret) ret = def ? dupstr(def) : NULL; /* permit NULL as final fallback */ return ret; } static void gpps(void *handle, const char *name, const char *def, Conf *conf, int primary) { char *val = gpps_raw(handle, name, def); conf_set_str(conf, primary, val); sfree(val); } /* * gppfont and gppfile cannot have local defaults, since the very * format of a Filename or FontSpec is platform-dependent. So the * platform-dependent functions MUST return some sort of value. */ static void gppfont(void *handle, const char *name, Conf *conf, int primary) { FontSpec *result = read_setting_fontspec(handle, name); if (!result) result = platform_default_fontspec(name); conf_set_fontspec(conf, primary, result); fontspec_free(result); } static void gppfile(void *handle, const char *name, Conf *conf, int primary) { Filename *result = read_setting_filename(handle, name); if (!result) result = platform_default_filename(name); conf_set_filename(conf, primary, result); filename_free(result); } static int gppi_raw(void *handle, const char *name, int def) { def = platform_default_i(name, def); return read_setting_i(handle, name, def); } static void gppi(void *handle, const char *name, int def, Conf *conf, int primary) { conf_set_int(conf, primary, gppi_raw(handle, name, def)); } /* * Read a set of name-value pairs in the format we occasionally use: * NAME\tVALUE\0NAME\tVALUE\0\0 in memory * NAME=VALUE,NAME=VALUE, in storage * If there's no "=VALUE" (e.g. just NAME,NAME,NAME) then those keys * are mapped to the empty string. */ static int gppmap(void *handle, const char *name, Conf *conf, int primary) { char *buf, *p, *q, *key, *val; /* * Start by clearing any existing subkeys of this key from conf. */ while ((key = conf_get_str_nthstrkey(conf, primary, 0)) != NULL) conf_del_str_str(conf, primary, key); /* * Now read a serialised list from the settings and unmarshal it * into its components. */ buf = gpps_raw(handle, name, NULL); if (!buf) return FALSE; p = buf; while (*p) { q = buf; val = NULL; while (*p && *p != ',') { int c = *p++; if (c == '=') c = '\0'; if (c == '\\') c = *p++; *q++ = c; if (!c) val = q; } if (*p == ',') p++; if (!val) val = q; *q = '\0'; if (primary == CONF_portfwd && strchr(buf, 'D') != NULL) { /* * Backwards-compatibility hack: dynamic forwardings are * indexed in the data store as a third type letter in the * key, 'D' alongside 'L' and 'R' - but really, they * should be filed under 'L' with a special _value_, * because local and dynamic forwardings both involve * _listening_ on a local port, and are hence mutually * exclusive on the same port number. So here we translate * the legacy storage format into the sensible internal * form, by finding the D and turning it into a L. */ char *newkey = dupstr(buf); *strchr(newkey, 'D') = 'L'; conf_set_str_str(conf, primary, newkey, "D"); sfree(newkey); } else { conf_set_str_str(conf, primary, buf, val); } } sfree(buf); return TRUE; } /* * Write a set of name/value pairs in the above format, or just the * names if include_values is FALSE. */ static void wmap(void *handle, char const *outkey, Conf *conf, int primary, int include_values) { char *buf, *p, *key, *realkey; const char *val, *q; int len; len = 1; /* allow for NUL */ for (val = conf_get_str_strs(conf, primary, NULL, &key); val != NULL; val = conf_get_str_strs(conf, primary, key, &key)) len += 2 + 2 * (strlen(key) + strlen(val)); /* allow for escaping */ buf = snewn(len, char); p = buf; for (val = conf_get_str_strs(conf, primary, NULL, &key); val != NULL; val = conf_get_str_strs(conf, primary, key, &key)) { if (primary == CONF_portfwd && !strcmp(val, "D")) { /* * Backwards-compatibility hack, as above: translate from * the sensible internal representation of dynamic * forwardings (key "L", value "D") to the * conceptually incoherent legacy storage format (key * "D", value empty). */ char *L; realkey = key; /* restore it at end of loop */ val = ""; key = dupstr(key); L = strchr(key, 'L'); if (L) *L = 'D'; } else { realkey = NULL; } if (p != buf) *p++ = ','; for (q = key; *q; q++) { if (*q == '=' || *q == ',' || *q == '\\') *p++ = '\\'; *p++ = *q; } if (include_values) { *p++ = '='; for (q = val; *q; q++) { if (*q == '=' || *q == ',' || *q == '\\') *p++ = '\\'; *p++ = *q; } } if (realkey) { free(key); key = realkey; } } *p = '\0'; write_setting_s(handle, outkey, buf); sfree(buf); } static int key2val(const struct keyvalwhere *mapping, int nmaps, char *key) { int i; for (i = 0; i < nmaps; i++) if (!strcmp(mapping[i].s, key)) return mapping[i].v; return -1; } static const char *val2key(const struct keyvalwhere *mapping, int nmaps, int val) { int i; for (i = 0; i < nmaps; i++) if (mapping[i].v == val) return mapping[i].s; return NULL; } /* * Helper function to parse a comma-separated list of strings into * a preference list array of values. Any missing values are added * to the end and duplicates are weeded. * XXX: assumes vals in 'mapping' are small +ve integers */ static void gprefs_from_str(const char *str, const struct keyvalwhere *mapping, int nvals, Conf *conf, int primary) { char *commalist = dupstr(str); char *p, *q; int i, j, n, v, pos; unsigned long seen = 0; /* bitmap for weeding dups etc */ /* * Go through that list and convert it into values. */ n = 0; p = commalist; while (1) { while (*p && *p == ',') p++; if (!*p) break; /* no more words */ q = p; while (*p && *p != ',') p++; if (*p) *p++ = '\0'; v = key2val(mapping, nvals, q); if (v != -1 && !(seen & (1 << v))) { seen |= (1 << v); conf_set_int_int(conf, primary, n, v); n++; } } sfree(commalist); /* * Now go through 'mapping' and add values that weren't mentioned * in the list we fetched. We may have to loop over it multiple * times so that we add values before other values whose default * positions depend on them. */ while (n < nvals) { for (i = 0; i < nvals; i++) { assert(mapping[i].v < 32); if (!(seen & (1 << mapping[i].v))) { /* * This element needs adding. But can we add it yet? */ if (mapping[i].vrel != -1 && !(seen & (1 << mapping[i].vrel))) continue; /* nope */ /* * OK, we can work out where to add this element, so * do so. */ if (mapping[i].vrel == -1) { pos = (mapping[i].where < 0 ? n : 0); } else { for (j = 0; j < n; j++) if (conf_get_int_int(conf, primary, j) == mapping[i].vrel) break; assert(j < n); /* implied by (seen & (1<= pos; j--) conf_set_int_int(conf, primary, j+1, conf_get_int_int(conf, primary, j)); conf_set_int_int(conf, primary, pos, mapping[i].v); seen |= (1 << mapping[i].v); n++; } } } } /* * Read a preference list. */ static void gprefs(void *sesskey, const char *name, const char *def, const struct keyvalwhere *mapping, int nvals, Conf *conf, int primary) { /* * Fetch the string which we'll parse as a comma-separated list. */ char *value = gpps_raw(sesskey, name, def); gprefs_from_str(value, mapping, nvals, conf, primary); sfree(value); } /* * Write out a preference list. */ static void wprefs(void *sesskey, const char *name, const struct keyvalwhere *mapping, int nvals, Conf *conf, int primary) { char *buf, *p; int i, maxlen; for (maxlen = i = 0; i < nvals; i++) { const char *s = val2key(mapping, nvals, conf_get_int_int(conf, primary, i)); if (s) { maxlen += (maxlen > 0 ? 1 : 0) + strlen(s); } } buf = snewn(maxlen + 1, char); p = buf; for (i = 0; i < nvals; i++) { const char *s = val2key(mapping, nvals, conf_get_int_int(conf, primary, i)); if (s) { p += sprintf(p, "%s%s", (p > buf ? "," : ""), s); } } assert(p - buf == maxlen); *p = '\0'; write_setting_s(sesskey, name, buf); sfree(buf); } char *save_settings(const char *section, Conf *conf) { void *sesskey; char *errmsg; sesskey = open_settings_w(section, &errmsg); if (!sesskey) return errmsg; save_open_settings(sesskey, conf); close_settings_w(sesskey); return NULL; } void save_open_settings(void *sesskey, Conf *conf) { int i; const char *p; write_setting_i(sesskey, "Present", 1); write_setting_s(sesskey, "HostName", conf_get_str(conf, CONF_host)); write_setting_filename(sesskey, "LogFileName", conf_get_filename(conf, CONF_logfilename)); write_setting_i(sesskey, "LogType", conf_get_int(conf, CONF_logtype)); write_setting_i(sesskey, "LogFileClash", conf_get_int(conf, CONF_logxfovr)); write_setting_i(sesskey, "LogFlush", conf_get_int(conf, CONF_logflush)); write_setting_i(sesskey, "SSHLogOmitPasswords", conf_get_int(conf, CONF_logomitpass)); write_setting_i(sesskey, "SSHLogOmitData", conf_get_int(conf, CONF_logomitdata)); p = "raw"; { const Backend *b = backend_from_proto(conf_get_int(conf, CONF_protocol)); if (b) p = b->name; } write_setting_s(sesskey, "Protocol", p); write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port)); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ write_setting_i(sesskey, "CloseOnExit", (conf_get_int(conf, CONF_close_on_exit)+2)%3); write_setting_i(sesskey, "WarnOnClose", !!conf_get_int(conf, CONF_warn_on_close)); write_setting_i(sesskey, "PingInterval", conf_get_int(conf, CONF_ping_interval) / 60); /* minutes */ write_setting_i(sesskey, "PingIntervalSecs", conf_get_int(conf, CONF_ping_interval) % 60); /* seconds */ write_setting_i(sesskey, "TCPNoDelay", conf_get_int(conf, CONF_tcp_nodelay)); write_setting_i(sesskey, "TCPKeepalives", conf_get_int(conf, CONF_tcp_keepalives)); write_setting_s(sesskey, "TerminalType", conf_get_str(conf, CONF_termtype)); write_setting_s(sesskey, "TerminalSpeed", conf_get_str(conf, CONF_termspeed)); wmap(sesskey, "TerminalModes", conf, CONF_ttymodes, TRUE); /* Address family selection */ write_setting_i(sesskey, "AddressFamily", conf_get_int(conf, CONF_addressfamily)); /* proxy settings */ write_setting_s(sesskey, "ProxyExcludeList", conf_get_str(conf, CONF_proxy_exclude_list)); write_setting_i(sesskey, "ProxyDNS", (conf_get_int(conf, CONF_proxy_dns)+2)%3); write_setting_i(sesskey, "ProxyLocalhost", conf_get_int(conf, CONF_even_proxy_localhost)); write_setting_i(sesskey, "ProxyMethod", conf_get_int(conf, CONF_proxy_type)); write_setting_s(sesskey, "ProxyHost", conf_get_str(conf, CONF_proxy_host)); write_setting_i(sesskey, "ProxyPort", conf_get_int(conf, CONF_proxy_port)); write_setting_s(sesskey, "ProxyUsername", conf_get_str(conf, CONF_proxy_username)); write_setting_s(sesskey, "ProxyPassword", conf_get_str(conf, CONF_proxy_password)); write_setting_s(sesskey, "ProxyTelnetCommand", conf_get_str(conf, CONF_proxy_telnet_command)); write_setting_i(sesskey, "ProxyLogToTerm", conf_get_int(conf, CONF_proxy_log_to_term)); wmap(sesskey, "Environment", conf, CONF_environmt, TRUE); write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username)); write_setting_i(sesskey, "UserNameFromEnvironment", conf_get_int(conf, CONF_username_from_env)); write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername)); write_setting_i(sesskey, "NoPTY", conf_get_int(conf, CONF_nopty)); write_setting_i(sesskey, "Compression", conf_get_int(conf, CONF_compression)); write_setting_i(sesskey, "TryAgent", conf_get_int(conf, CONF_tryagent)); write_setting_i(sesskey, "AgentFwd", conf_get_int(conf, CONF_agentfwd)); write_setting_i(sesskey, "GssapiFwd", conf_get_int(conf, CONF_gssapifwd)); write_setting_i(sesskey, "ChangeUsername", conf_get_int(conf, CONF_change_username)); wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist); wprefs(sesskey, "HostKey", hknames, HK_MAX, conf, CONF_ssh_hklist); write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time)); write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data)); write_setting_i(sesskey, "SshNoAuth", conf_get_int(conf, CONF_ssh_no_userauth)); write_setting_i(sesskey, "SshBanner", conf_get_int(conf, CONF_ssh_show_banner)); write_setting_i(sesskey, "AuthTIS", conf_get_int(conf, CONF_try_tis_auth)); write_setting_i(sesskey, "AuthKI", conf_get_int(conf, CONF_try_ki_auth)); write_setting_i(sesskey, "AuthGSSAPI", conf_get_int(conf, CONF_try_gssapi_auth)); #ifndef NO_GSSAPI wprefs(sesskey, "GSSLibs", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); write_setting_filename(sesskey, "GSSCustom", conf_get_filename(conf, CONF_ssh_gss_custom)); #endif write_setting_i(sesskey, "SshNoShell", conf_get_int(conf, CONF_ssh_no_shell)); write_setting_i(sesskey, "SshProt", conf_get_int(conf, CONF_sshprot)); write_setting_s(sesskey, "LogHost", conf_get_str(conf, CONF_loghost)); write_setting_i(sesskey, "SSH2DES", conf_get_int(conf, CONF_ssh2_des_cbc)); write_setting_filename(sesskey, "PublicKeyFile", conf_get_filename(conf, CONF_keyfile)); write_setting_s(sesskey, "RemoteCommand", conf_get_str(conf, CONF_remote_cmd)); write_setting_i(sesskey, "RFCEnviron", conf_get_int(conf, CONF_rfc_environ)); write_setting_i(sesskey, "PassiveTelnet", conf_get_int(conf, CONF_passive_telnet)); write_setting_i(sesskey, "BackspaceIsDelete", conf_get_int(conf, CONF_bksp_is_delete)); write_setting_i(sesskey, "RXVTHomeEnd", conf_get_int(conf, CONF_rxvt_homeend)); write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type)); write_setting_i(sesskey, "NoApplicationKeys", conf_get_int(conf, CONF_no_applic_k)); write_setting_i(sesskey, "NoApplicationCursors", conf_get_int(conf, CONF_no_applic_c)); write_setting_i(sesskey, "NoMouseReporting", conf_get_int(conf, CONF_no_mouse_rep)); write_setting_i(sesskey, "NoRemoteResize", conf_get_int(conf, CONF_no_remote_resize)); write_setting_i(sesskey, "NoAltScreen", conf_get_int(conf, CONF_no_alt_screen)); write_setting_i(sesskey, "NoRemoteWinTitle", conf_get_int(conf, CONF_no_remote_wintitle)); write_setting_i(sesskey, "NoRemoteClearScroll", conf_get_int(conf, CONF_no_remote_clearscroll)); write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action)); write_setting_i(sesskey, "NoDBackspace", conf_get_int(conf, CONF_no_dbackspace)); write_setting_i(sesskey, "NoRemoteCharset", conf_get_int(conf, CONF_no_remote_charset)); write_setting_i(sesskey, "ApplicationCursorKeys", conf_get_int(conf, CONF_app_cursor)); write_setting_i(sesskey, "ApplicationKeypad", conf_get_int(conf, CONF_app_keypad)); write_setting_i(sesskey, "NetHackKeypad", conf_get_int(conf, CONF_nethack_keypad)); write_setting_i(sesskey, "AltF4", conf_get_int(conf, CONF_alt_f4)); write_setting_i(sesskey, "AltSpace", conf_get_int(conf, CONF_alt_space)); write_setting_i(sesskey, "AltOnly", conf_get_int(conf, CONF_alt_only)); write_setting_i(sesskey, "ComposeKey", conf_get_int(conf, CONF_compose_key)); write_setting_i(sesskey, "CtrlAltKeys", conf_get_int(conf, CONF_ctrlaltkeys)); #ifdef OSX_META_KEY_CONFIG write_setting_i(sesskey, "OSXOptionMeta", conf_get_int(conf, CONF_osx_option_meta)); write_setting_i(sesskey, "OSXCommandMeta", conf_get_int(conf, CONF_osx_command_meta)); #endif write_setting_i(sesskey, "TelnetKey", conf_get_int(conf, CONF_telnet_keyboard)); write_setting_i(sesskey, "TelnetRet", conf_get_int(conf, CONF_telnet_newline)); write_setting_i(sesskey, "LocalEcho", conf_get_int(conf, CONF_localecho)); write_setting_i(sesskey, "LocalEdit", conf_get_int(conf, CONF_localedit)); write_setting_s(sesskey, "Answerback", conf_get_str(conf, CONF_answerback)); write_setting_i(sesskey, "AlwaysOnTop", conf_get_int(conf, CONF_alwaysontop)); write_setting_i(sesskey, "FullScreenOnAltEnter", conf_get_int(conf, CONF_fullscreenonaltenter)); write_setting_i(sesskey, "HideMousePtr", conf_get_int(conf, CONF_hide_mouseptr)); write_setting_i(sesskey, "SunkenEdge", conf_get_int(conf, CONF_sunken_edge)); write_setting_i(sesskey, "WindowBorder", conf_get_int(conf, CONF_window_border)); write_setting_i(sesskey, "CurType", conf_get_int(conf, CONF_cursor_type)); write_setting_i(sesskey, "BlinkCur", conf_get_int(conf, CONF_blink_cur)); write_setting_i(sesskey, "Beep", conf_get_int(conf, CONF_beep)); write_setting_i(sesskey, "BeepInd", conf_get_int(conf, CONF_beep_ind)); write_setting_filename(sesskey, "BellWaveFile", conf_get_filename(conf, CONF_bell_wavefile)); write_setting_i(sesskey, "BellOverload", conf_get_int(conf, CONF_bellovl)); write_setting_i(sesskey, "BellOverloadN", conf_get_int(conf, CONF_bellovl_n)); write_setting_i(sesskey, "BellOverloadT", conf_get_int(conf, CONF_bellovl_t) #ifdef PUTTY_UNIX_H * 1000 #endif ); write_setting_i(sesskey, "BellOverloadS", conf_get_int(conf, CONF_bellovl_s) #ifdef PUTTY_UNIX_H * 1000 #endif ); write_setting_i(sesskey, "ScrollbackLines", conf_get_int(conf, CONF_savelines)); write_setting_i(sesskey, "DECOriginMode", conf_get_int(conf, CONF_dec_om)); write_setting_i(sesskey, "AutoWrapMode", conf_get_int(conf, CONF_wrap_mode)); write_setting_i(sesskey, "LFImpliesCR", conf_get_int(conf, CONF_lfhascr)); write_setting_i(sesskey, "CRImpliesLF", conf_get_int(conf, CONF_crhaslf)); write_setting_i(sesskey, "DisableArabicShaping", conf_get_int(conf, CONF_arabicshaping)); write_setting_i(sesskey, "DisableBidi", conf_get_int(conf, CONF_bidi)); write_setting_i(sesskey, "WinNameAlways", conf_get_int(conf, CONF_win_name_always)); write_setting_s(sesskey, "WinTitle", conf_get_str(conf, CONF_wintitle)); write_setting_i(sesskey, "TermWidth", conf_get_int(conf, CONF_width)); write_setting_i(sesskey, "TermHeight", conf_get_int(conf, CONF_height)); write_setting_fontspec(sesskey, "Font", conf_get_fontspec(conf, CONF_font)); write_setting_i(sesskey, "FontQuality", conf_get_int(conf, CONF_font_quality)); write_setting_i(sesskey, "FontVTMode", conf_get_int(conf, CONF_vtmode)); write_setting_i(sesskey, "UseSystemColours", conf_get_int(conf, CONF_system_colour)); write_setting_i(sesskey, "TryPalette", conf_get_int(conf, CONF_try_palette)); write_setting_i(sesskey, "ANSIColour", conf_get_int(conf, CONF_ansi_colour)); write_setting_i(sesskey, "Xterm256Colour", conf_get_int(conf, CONF_xterm_256_colour)); write_setting_i(sesskey, "BoldAsColour", conf_get_int(conf, CONF_bold_style)-1); for (i = 0; i < 22; i++) { char buf[20], buf2[30]; sprintf(buf, "Colour%d", i); sprintf(buf2, "%d,%d,%d", conf_get_int_int(conf, CONF_colours, i*3+0), conf_get_int_int(conf, CONF_colours, i*3+1), conf_get_int_int(conf, CONF_colours, i*3+2)); write_setting_s(sesskey, buf, buf2); } write_setting_i(sesskey, "RawCNP", conf_get_int(conf, CONF_rawcnp)); write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste)); write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm)); write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select)); write_setting_i(sesskey, "MouseOverride", conf_get_int(conf, CONF_mouse_override)); for (i = 0; i < 256; i += 32) { char buf[20], buf2[256]; int j; sprintf(buf, "Wordness%d", i); *buf2 = '\0'; for (j = i; j < i + 32; j++) { sprintf(buf2 + strlen(buf2), "%s%d", (*buf2 ? "," : ""), conf_get_int_int(conf, CONF_wordness, j)); } write_setting_s(sesskey, buf, buf2); } write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage)); write_setting_i(sesskey, "CJKAmbigWide", conf_get_int(conf, CONF_cjk_ambig_wide)); write_setting_i(sesskey, "UTF8Override", conf_get_int(conf, CONF_utf8_override)); write_setting_s(sesskey, "Printer", conf_get_str(conf, CONF_printer)); write_setting_i(sesskey, "CapsLockCyr", conf_get_int(conf, CONF_xlat_capslockcyr)); write_setting_i(sesskey, "ScrollBar", conf_get_int(conf, CONF_scrollbar)); write_setting_i(sesskey, "ScrollBarFullScreen", conf_get_int(conf, CONF_scrollbar_in_fullscreen)); write_setting_i(sesskey, "ScrollOnKey", conf_get_int(conf, CONF_scroll_on_key)); write_setting_i(sesskey, "ScrollOnDisp", conf_get_int(conf, CONF_scroll_on_disp)); write_setting_i(sesskey, "EraseToScrollback", conf_get_int(conf, CONF_erase_to_scrollback)); write_setting_i(sesskey, "LockSize", conf_get_int(conf, CONF_resize_action)); write_setting_i(sesskey, "BCE", conf_get_int(conf, CONF_bce)); write_setting_i(sesskey, "BlinkText", conf_get_int(conf, CONF_blinktext)); write_setting_i(sesskey, "X11Forward", conf_get_int(conf, CONF_x11_forward)); write_setting_s(sesskey, "X11Display", conf_get_str(conf, CONF_x11_display)); write_setting_i(sesskey, "X11AuthType", conf_get_int(conf, CONF_x11_auth)); write_setting_filename(sesskey, "X11AuthFile", conf_get_filename(conf, CONF_xauthfile)); write_setting_i(sesskey, "LocalPortAcceptAll", conf_get_int(conf, CONF_lport_acceptall)); write_setting_i(sesskey, "RemotePortAcceptAll", conf_get_int(conf, CONF_rport_acceptall)); wmap(sesskey, "PortForwardings", conf, CONF_portfwd, TRUE); write_setting_i(sesskey, "BugIgnore1", 2-conf_get_int(conf, CONF_sshbug_ignore1)); write_setting_i(sesskey, "BugPlainPW1", 2-conf_get_int(conf, CONF_sshbug_plainpw1)); write_setting_i(sesskey, "BugRSA1", 2-conf_get_int(conf, CONF_sshbug_rsa1)); write_setting_i(sesskey, "BugIgnore2", 2-conf_get_int(conf, CONF_sshbug_ignore2)); write_setting_i(sesskey, "BugHMAC2", 2-conf_get_int(conf, CONF_sshbug_hmac2)); write_setting_i(sesskey, "BugDeriveKey2", 2-conf_get_int(conf, CONF_sshbug_derivekey2)); write_setting_i(sesskey, "BugRSAPad2", 2-conf_get_int(conf, CONF_sshbug_rsapad2)); write_setting_i(sesskey, "BugPKSessID2", 2-conf_get_int(conf, CONF_sshbug_pksessid2)); write_setting_i(sesskey, "BugRekey2", 2-conf_get_int(conf, CONF_sshbug_rekey2)); write_setting_i(sesskey, "BugMaxPkt2", 2-conf_get_int(conf, CONF_sshbug_maxpkt2)); write_setting_i(sesskey, "BugOldGex2", 2-conf_get_int(conf, CONF_sshbug_oldgex2)); write_setting_i(sesskey, "BugWinadj", 2-conf_get_int(conf, CONF_sshbug_winadj)); write_setting_i(sesskey, "BugChanReq", 2-conf_get_int(conf, CONF_sshbug_chanreq)); write_setting_i(sesskey, "StampUtmp", conf_get_int(conf, CONF_stamp_utmp)); write_setting_i(sesskey, "LoginShell", conf_get_int(conf, CONF_login_shell)); write_setting_i(sesskey, "ScrollbarOnLeft", conf_get_int(conf, CONF_scrollbar_on_left)); write_setting_fontspec(sesskey, "BoldFont", conf_get_fontspec(conf, CONF_boldfont)); write_setting_fontspec(sesskey, "WideFont", conf_get_fontspec(conf, CONF_widefont)); write_setting_fontspec(sesskey, "WideBoldFont", conf_get_fontspec(conf, CONF_wideboldfont)); write_setting_i(sesskey, "ShadowBold", conf_get_int(conf, CONF_shadowbold)); write_setting_i(sesskey, "ShadowBoldOffset", conf_get_int(conf, CONF_shadowboldoffset)); write_setting_s(sesskey, "SerialLine", conf_get_str(conf, CONF_serline)); write_setting_i(sesskey, "SerialSpeed", conf_get_int(conf, CONF_serspeed)); write_setting_i(sesskey, "SerialDataBits", conf_get_int(conf, CONF_serdatabits)); write_setting_i(sesskey, "SerialStopHalfbits", conf_get_int(conf, CONF_serstopbits)); write_setting_i(sesskey, "SerialParity", conf_get_int(conf, CONF_serparity)); write_setting_i(sesskey, "SerialFlowControl", conf_get_int(conf, CONF_serflow)); write_setting_s(sesskey, "WindowClass", conf_get_str(conf, CONF_winclass)); write_setting_i(sesskey, "ConnectionSharing", conf_get_int(conf, CONF_ssh_connection_sharing)); write_setting_i(sesskey, "ConnectionSharingUpstream", conf_get_int(conf, CONF_ssh_connection_sharing_upstream)); write_setting_i(sesskey, "ConnectionSharingDownstream", conf_get_int(conf, CONF_ssh_connection_sharing_downstream)); wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, FALSE); } void load_settings(const char *section, Conf *conf) { void *sesskey; sesskey = open_settings_r(section); load_open_settings(sesskey, conf); close_settings_r(sesskey); if (conf_launchable(conf)) add_session_to_jumplist(section); } void load_open_settings(void *sesskey, Conf *conf) { int i; char *prot; conf_set_int(conf, CONF_ssh_subsys, 0); /* FIXME: load this properly */ conf_set_str(conf, CONF_remote_cmd, ""); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_str(conf, CONF_ssh_nc_host, ""); gpps(sesskey, "HostName", "", conf, CONF_host); gppfile(sesskey, "LogFileName", conf, CONF_logfilename); gppi(sesskey, "LogType", 0, conf, CONF_logtype); gppi(sesskey, "LogFileClash", LGXF_ASK, conf, CONF_logxfovr); gppi(sesskey, "LogFlush", 1, conf, CONF_logflush); gppi(sesskey, "SSHLogOmitPasswords", 1, conf, CONF_logomitpass); gppi(sesskey, "SSHLogOmitData", 0, conf, CONF_logomitdata); prot = gpps_raw(sesskey, "Protocol", "default"); conf_set_int(conf, CONF_protocol, default_protocol); conf_set_int(conf, CONF_port, default_port); { const Backend *b = backend_from_name(prot); if (b) { conf_set_int(conf, CONF_protocol, b->protocol); gppi(sesskey, "PortNumber", default_port, conf, CONF_port); } } sfree(prot); /* Address family selection */ gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, conf, CONF_addressfamily); /* The CloseOnExit numbers are arranged in a different order from * the standard FORCE_ON / FORCE_OFF / AUTO. */ i = gppi_raw(sesskey, "CloseOnExit", 1); conf_set_int(conf, CONF_close_on_exit, (i+1)%3); gppi(sesskey, "WarnOnClose", 1, conf, CONF_warn_on_close); { /* This is two values for backward compatibility with 0.50/0.51 */ int pingmin, pingsec; pingmin = gppi_raw(sesskey, "PingInterval", 0); pingsec = gppi_raw(sesskey, "PingIntervalSecs", 0); conf_set_int(conf, CONF_ping_interval, pingmin * 60 + pingsec); } gppi(sesskey, "TCPNoDelay", 1, conf, CONF_tcp_nodelay); gppi(sesskey, "TCPKeepalives", 0, conf, CONF_tcp_keepalives); gpps(sesskey, "TerminalType", "xterm", conf, CONF_termtype); gpps(sesskey, "TerminalSpeed", "38400,38400", conf, CONF_termspeed); if (gppmap(sesskey, "TerminalModes", conf, CONF_ttymodes)) { /* * Backwards compatibility with old saved settings. * * From the invention of this setting through 0.67, the set of * terminal modes was fixed, and absence of a mode from this * setting meant the user had explicitly removed it from the * UI and we shouldn't send it. * * In 0.68, the IUTF8 mode was added, and in handling old * settings we inadvertently removed the ability to not send * a mode. Any mode not mentioned was treated as if it was * set to 'auto' (A). * * After 0.68, we added explicit notation to the setting format * when the user removes a known terminal mode from the list. * * So: if any of the modes from the original set is missing, we * assume this was an intentional removal by the user and add * an explicit removal ('N'); but if IUTF8 (or any other mode * added after 0.67) is missing, we assume that its absence is * due to the setting being old rather than intentional, and * add it with its default setting. * * (This does mean that if a 0.68 user explicitly removed IUTF8, * we add it back; but removing IUTF8 had no effect in 0.68, so * we're preserving behaviour, which is the best we can do.) */ for (i = 0; ttymodes[i]; i++) { if (!conf_get_str_str_opt(conf, CONF_ttymodes, ttymodes[i])) { /* Mode not mentioned in setting. */ const char *def; if (!strcmp(ttymodes[i], "IUTF8")) { /* Any new modes we add in future should be treated * this way too. */ def = "A"; /* same as new-setting default below */ } else { /* One of the original modes. Absence is probably * deliberate. */ def = "N"; /* don't send */ } conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], def); } } } else { /* This hardcodes a big set of defaults in any new saved * sessions. Let's hope we don't change our mind. */ for (i = 0; ttymodes[i]; i++) conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], "A"); } /* proxy settings */ gpps(sesskey, "ProxyExcludeList", "", conf, CONF_proxy_exclude_list); i = gppi_raw(sesskey, "ProxyDNS", 1); conf_set_int(conf, CONF_proxy_dns, (i+1)%3); gppi(sesskey, "ProxyLocalhost", 0, conf, CONF_even_proxy_localhost); gppi(sesskey, "ProxyMethod", -1, conf, CONF_proxy_type); if (conf_get_int(conf, CONF_proxy_type) == -1) { int i; i = gppi_raw(sesskey, "ProxyType", 0); if (i == 0) conf_set_int(conf, CONF_proxy_type, PROXY_NONE); else if (i == 1) conf_set_int(conf, CONF_proxy_type, PROXY_HTTP); else if (i == 3) conf_set_int(conf, CONF_proxy_type, PROXY_TELNET); else if (i == 4) conf_set_int(conf, CONF_proxy_type, PROXY_CMD); else { i = gppi_raw(sesskey, "ProxySOCKSVersion", 5); if (i == 5) conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS5); else conf_set_int(conf, CONF_proxy_type, PROXY_SOCKS4); } } gpps(sesskey, "ProxyHost", "proxy", conf, CONF_proxy_host); gppi(sesskey, "ProxyPort", 80, conf, CONF_proxy_port); gpps(sesskey, "ProxyUsername", "", conf, CONF_proxy_username); gpps(sesskey, "ProxyPassword", "", conf, CONF_proxy_password); gpps(sesskey, "ProxyTelnetCommand", "connect %host %port\\n", conf, CONF_proxy_telnet_command); gppi(sesskey, "ProxyLogToTerm", FORCE_OFF, conf, CONF_proxy_log_to_term); gppmap(sesskey, "Environment", conf, CONF_environmt); gpps(sesskey, "UserName", "", conf, CONF_username); gppi(sesskey, "UserNameFromEnvironment", 0, conf, CONF_username_from_env); gpps(sesskey, "LocalUserName", "", conf, CONF_localusername); gppi(sesskey, "NoPTY", 0, conf, CONF_nopty); gppi(sesskey, "Compression", 0, conf, CONF_compression); gppi(sesskey, "TryAgent", 1, conf, CONF_tryagent); gppi(sesskey, "AgentFwd", 0, conf, CONF_agentfwd); gppi(sesskey, "ChangeUsername", 0, conf, CONF_change_username); gppi(sesskey, "GssapiFwd", 0, conf, CONF_gssapifwd); gprefs(sesskey, "Cipher", "\0", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); { /* Backward-compatibility: before 0.58 (when the "KEX" * preference was first added), we had an option to * disable gex under the "bugs" panel after one report of * a server which offered it then choked, but we never got * a server version string or any other reports. */ const char *default_kexes, *normal_default = "ecdh,dh-gex-sha1,dh-group14-sha1,rsa," "WARN,dh-group1-sha1", *bugdhgex2_default = "ecdh,dh-group14-sha1,rsa," "WARN,dh-group1-sha1,dh-gex-sha1"; char *raw; i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0); if (i == FORCE_ON) default_kexes = bugdhgex2_default; else default_kexes = normal_default; /* Migration: after 0.67 we decided we didn't like * dh-group1-sha1. If it looks like the user never changed * the defaults, quietly upgrade their settings to demote it. * (If they did, they're on their own.) */ raw = gpps_raw(sesskey, "KEX", default_kexes); assert(raw != NULL); /* Lack of 'ecdh' tells us this was saved by 0.58-0.67 * inclusive. If it was saved by a later version, we need * to leave it alone. */ if (strcmp(raw, "dh-group14-sha1,dh-group1-sha1,rsa," "WARN,dh-gex-sha1") == 0) { /* Previously migrated from BugDHGEx2. */ sfree(raw); raw = dupstr(bugdhgex2_default); } else if (strcmp(raw, "dh-gex-sha1,dh-group14-sha1," "dh-group1-sha1,rsa,WARN") == 0) { /* Untouched old default setting. */ sfree(raw); raw = dupstr(normal_default); } gprefs_from_str(raw, kexnames, KEX_MAX, conf, CONF_ssh_kexlist); sfree(raw); } gprefs(sesskey, "HostKey", "ed25519,ecdsa,rsa,dsa,WARN", hknames, HK_MAX, conf, CONF_ssh_hklist); gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data); { /* SSH-2 only by default */ int sshprot = gppi_raw(sesskey, "SshProt", 3); /* Old sessions may contain the values corresponding to the fallbacks * we used to allow; migrate them */ if (sshprot == 1) sshprot = 0; /* => "SSH-1 only" */ else if (sshprot == 2) sshprot = 3; /* => "SSH-2 only" */ conf_set_int(conf, CONF_sshprot, sshprot); } gpps(sesskey, "LogHost", "", conf, CONF_loghost); gppi(sesskey, "SSH2DES", 0, conf, CONF_ssh2_des_cbc); gppi(sesskey, "SshNoAuth", 0, conf, CONF_ssh_no_userauth); gppi(sesskey, "SshBanner", 1, conf, CONF_ssh_show_banner); gppi(sesskey, "AuthTIS", 0, conf, CONF_try_tis_auth); gppi(sesskey, "AuthKI", 1, conf, CONF_try_ki_auth); gppi(sesskey, "AuthGSSAPI", 1, conf, CONF_try_gssapi_auth); #ifndef NO_GSSAPI gprefs(sesskey, "GSSLibs", "\0", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom); #endif gppi(sesskey, "SshNoShell", 0, conf, CONF_ssh_no_shell); gppfile(sesskey, "PublicKeyFile", conf, CONF_keyfile); gpps(sesskey, "RemoteCommand", "", conf, CONF_remote_cmd); gppi(sesskey, "RFCEnviron", 0, conf, CONF_rfc_environ); gppi(sesskey, "PassiveTelnet", 0, conf, CONF_passive_telnet); gppi(sesskey, "BackspaceIsDelete", 1, conf, CONF_bksp_is_delete); gppi(sesskey, "RXVTHomeEnd", 0, conf, CONF_rxvt_homeend); gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type); gppi(sesskey, "NoApplicationKeys", 0, conf, CONF_no_applic_k); gppi(sesskey, "NoApplicationCursors", 0, conf, CONF_no_applic_c); gppi(sesskey, "NoMouseReporting", 0, conf, CONF_no_mouse_rep); gppi(sesskey, "NoRemoteResize", 0, conf, CONF_no_remote_resize); gppi(sesskey, "NoAltScreen", 0, conf, CONF_no_alt_screen); gppi(sesskey, "NoRemoteWinTitle", 0, conf, CONF_no_remote_wintitle); gppi(sesskey, "NoRemoteClearScroll", 0, conf, CONF_no_remote_clearscroll); { /* Backward compatibility */ int no_remote_qtitle = gppi_raw(sesskey, "NoRemoteQTitle", 1); /* We deliberately interpret the old setting of "no response" as * "empty string". This changes the behaviour, but hopefully for * the better; the user can always recover the old behaviour. */ gppi(sesskey, "RemoteQTitleAction", no_remote_qtitle ? TITLE_EMPTY : TITLE_REAL, conf, CONF_remote_qtitle_action); } gppi(sesskey, "NoDBackspace", 0, conf, CONF_no_dbackspace); gppi(sesskey, "NoRemoteCharset", 0, conf, CONF_no_remote_charset); gppi(sesskey, "ApplicationCursorKeys", 0, conf, CONF_app_cursor); gppi(sesskey, "ApplicationKeypad", 0, conf, CONF_app_keypad); gppi(sesskey, "NetHackKeypad", 0, conf, CONF_nethack_keypad); gppi(sesskey, "AltF4", 1, conf, CONF_alt_f4); gppi(sesskey, "AltSpace", 0, conf, CONF_alt_space); gppi(sesskey, "AltOnly", 0, conf, CONF_alt_only); gppi(sesskey, "ComposeKey", 0, conf, CONF_compose_key); gppi(sesskey, "CtrlAltKeys", 1, conf, CONF_ctrlaltkeys); #ifdef OSX_META_KEY_CONFIG gppi(sesskey, "OSXOptionMeta", 1, conf, CONF_osx_option_meta); gppi(sesskey, "OSXCommandMeta", 0, conf, CONF_osx_command_meta); #endif gppi(sesskey, "TelnetKey", 0, conf, CONF_telnet_keyboard); gppi(sesskey, "TelnetRet", 1, conf, CONF_telnet_newline); gppi(sesskey, "LocalEcho", AUTO, conf, CONF_localecho); gppi(sesskey, "LocalEdit", AUTO, conf, CONF_localedit); gpps(sesskey, "Answerback", "PuTTY", conf, CONF_answerback); gppi(sesskey, "AlwaysOnTop", 0, conf, CONF_alwaysontop); gppi(sesskey, "FullScreenOnAltEnter", 0, conf, CONF_fullscreenonaltenter); gppi(sesskey, "HideMousePtr", 0, conf, CONF_hide_mouseptr); gppi(sesskey, "SunkenEdge", 0, conf, CONF_sunken_edge); gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border); gppi(sesskey, "CurType", 0, conf, CONF_cursor_type); gppi(sesskey, "BlinkCur", 0, conf, CONF_blink_cur); /* pedantic compiler tells me I can't use conf, CONF_beep as an int * :-) */ gppi(sesskey, "Beep", 1, conf, CONF_beep); gppi(sesskey, "BeepInd", 0, conf, CONF_beep_ind); gppfile(sesskey, "BellWaveFile", conf, CONF_bell_wavefile); gppi(sesskey, "BellOverload", 1, conf, CONF_bellovl); gppi(sesskey, "BellOverloadN", 5, conf, CONF_bellovl_n); i = gppi_raw(sesskey, "BellOverloadT", 2*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif ); conf_set_int(conf, CONF_bellovl_t, i #ifdef PUTTY_UNIX_H / 1000 #endif ); i = gppi_raw(sesskey, "BellOverloadS", 5*TICKSPERSEC #ifdef PUTTY_UNIX_H *1000 #endif ); conf_set_int(conf, CONF_bellovl_s, i #ifdef PUTTY_UNIX_H / 1000 #endif ); gppi(sesskey, "ScrollbackLines", 2000, conf, CONF_savelines); gppi(sesskey, "DECOriginMode", 0, conf, CONF_dec_om); gppi(sesskey, "AutoWrapMode", 1, conf, CONF_wrap_mode); gppi(sesskey, "LFImpliesCR", 0, conf, CONF_lfhascr); gppi(sesskey, "CRImpliesLF", 0, conf, CONF_crhaslf); gppi(sesskey, "DisableArabicShaping", 0, conf, CONF_arabicshaping); gppi(sesskey, "DisableBidi", 0, conf, CONF_bidi); gppi(sesskey, "WinNameAlways", 1, conf, CONF_win_name_always); gpps(sesskey, "WinTitle", "", conf, CONF_wintitle); gppi(sesskey, "TermWidth", 80, conf, CONF_width); gppi(sesskey, "TermHeight", 24, conf, CONF_height); gppfont(sesskey, "Font", conf, CONF_font); gppi(sesskey, "FontQuality", FQ_DEFAULT, conf, CONF_font_quality); gppi(sesskey, "FontVTMode", VT_UNICODE, conf, CONF_vtmode); gppi(sesskey, "UseSystemColours", 0, conf, CONF_system_colour); gppi(sesskey, "TryPalette", 0, conf, CONF_try_palette); gppi(sesskey, "ANSIColour", 1, conf, CONF_ansi_colour); gppi(sesskey, "Xterm256Colour", 1, conf, CONF_xterm_256_colour); i = gppi_raw(sesskey, "BoldAsColour", 1); conf_set_int(conf, CONF_bold_style, i+1); for (i = 0; i < 22; i++) { static const char *const defaults[] = { "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0", "0,255,0", "0,0,0", "85,85,85", "187,0,0", "255,85,85", "0,187,0", "85,255,85", "187,187,0", "255,255,85", "0,0,187", "85,85,255", "187,0,187", "255,85,255", "0,187,187", "85,255,255", "187,187,187", "255,255,255" }; char buf[20], *buf2; int c0, c1, c2; sprintf(buf, "Colour%d", i); buf2 = gpps_raw(sesskey, buf, defaults[i]); if (sscanf(buf2, "%d,%d,%d", &c0, &c1, &c2) == 3) { conf_set_int_int(conf, CONF_colours, i*3+0, c0); conf_set_int_int(conf, CONF_colours, i*3+1, c1); conf_set_int_int(conf, CONF_colours, i*3+2, c2); } sfree(buf2); } gppi(sesskey, "RawCNP", 0, conf, CONF_rawcnp); gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste); gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm); gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select); gppi(sesskey, "MouseOverride", 1, conf, CONF_mouse_override); for (i = 0; i < 256; i += 32) { static const char *const defaults[] = { "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1", "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2", "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1", "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2", "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2" }; char buf[20], *buf2, *p; int j; sprintf(buf, "Wordness%d", i); buf2 = gpps_raw(sesskey, buf, defaults[i / 32]); p = buf2; for (j = i; j < i + 32; j++) { char *q = p; while (*p && *p != ',') p++; if (*p == ',') *p++ = '\0'; conf_set_int_int(conf, CONF_wordness, j, atoi(q)); } sfree(buf2); } /* * The empty default for LineCodePage will be converted later * into a plausible default for the locale. */ gpps(sesskey, "LineCodePage", "", conf, CONF_line_codepage); gppi(sesskey, "CJKAmbigWide", 0, conf, CONF_cjk_ambig_wide); gppi(sesskey, "UTF8Override", 1, conf, CONF_utf8_override); gpps(sesskey, "Printer", "", conf, CONF_printer); gppi(sesskey, "CapsLockCyr", 0, conf, CONF_xlat_capslockcyr); gppi(sesskey, "ScrollBar", 1, conf, CONF_scrollbar); gppi(sesskey, "ScrollBarFullScreen", 0, conf, CONF_scrollbar_in_fullscreen); gppi(sesskey, "ScrollOnKey", 0, conf, CONF_scroll_on_key); gppi(sesskey, "ScrollOnDisp", 1, conf, CONF_scroll_on_disp); gppi(sesskey, "EraseToScrollback", 1, conf, CONF_erase_to_scrollback); gppi(sesskey, "LockSize", 0, conf, CONF_resize_action); gppi(sesskey, "BCE", 1, conf, CONF_bce); gppi(sesskey, "BlinkText", 0, conf, CONF_blinktext); gppi(sesskey, "X11Forward", 0, conf, CONF_x11_forward); gpps(sesskey, "X11Display", "", conf, CONF_x11_display); gppi(sesskey, "X11AuthType", X11_MIT, conf, CONF_x11_auth); gppfile(sesskey, "X11AuthFile", conf, CONF_xauthfile); gppi(sesskey, "LocalPortAcceptAll", 0, conf, CONF_lport_acceptall); gppi(sesskey, "RemotePortAcceptAll", 0, conf, CONF_rport_acceptall); gppmap(sesskey, "PortForwardings", conf, CONF_portfwd); i = gppi_raw(sesskey, "BugIgnore1", 0); conf_set_int(conf, CONF_sshbug_ignore1, 2-i); i = gppi_raw(sesskey, "BugPlainPW1", 0); conf_set_int(conf, CONF_sshbug_plainpw1, 2-i); i = gppi_raw(sesskey, "BugRSA1", 0); conf_set_int(conf, CONF_sshbug_rsa1, 2-i); i = gppi_raw(sesskey, "BugIgnore2", 0); conf_set_int(conf, CONF_sshbug_ignore2, 2-i); { int i; i = gppi_raw(sesskey, "BugHMAC2", 0); conf_set_int(conf, CONF_sshbug_hmac2, 2-i); if (2-i == AUTO) { i = gppi_raw(sesskey, "BuggyMAC", 0); if (i == 1) conf_set_int(conf, CONF_sshbug_hmac2, FORCE_ON); } } i = gppi_raw(sesskey, "BugDeriveKey2", 0); conf_set_int(conf, CONF_sshbug_derivekey2, 2-i); i = gppi_raw(sesskey, "BugRSAPad2", 0); conf_set_int(conf, CONF_sshbug_rsapad2, 2-i); i = gppi_raw(sesskey, "BugPKSessID2", 0); conf_set_int(conf, CONF_sshbug_pksessid2, 2-i); i = gppi_raw(sesskey, "BugRekey2", 0); conf_set_int(conf, CONF_sshbug_rekey2, 2-i); i = gppi_raw(sesskey, "BugMaxPkt2", 0); conf_set_int(conf, CONF_sshbug_maxpkt2, 2-i); i = gppi_raw(sesskey, "BugOldGex2", 0); conf_set_int(conf, CONF_sshbug_oldgex2, 2-i); i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i); i = gppi_raw(sesskey, "BugChanReq", 0); conf_set_int(conf, CONF_sshbug_chanreq, 2-i); conf_set_int(conf, CONF_ssh_simple, FALSE); gppi(sesskey, "StampUtmp", 1, conf, CONF_stamp_utmp); gppi(sesskey, "LoginShell", 1, conf, CONF_login_shell); gppi(sesskey, "ScrollbarOnLeft", 0, conf, CONF_scrollbar_on_left); gppi(sesskey, "ShadowBold", 0, conf, CONF_shadowbold); gppfont(sesskey, "BoldFont", conf, CONF_boldfont); gppfont(sesskey, "WideFont", conf, CONF_widefont); gppfont(sesskey, "WideBoldFont", conf, CONF_wideboldfont); gppi(sesskey, "ShadowBoldOffset", 1, conf, CONF_shadowboldoffset); gpps(sesskey, "SerialLine", "", conf, CONF_serline); gppi(sesskey, "SerialSpeed", 9600, conf, CONF_serspeed); gppi(sesskey, "SerialDataBits", 8, conf, CONF_serdatabits); gppi(sesskey, "SerialStopHalfbits", 2, conf, CONF_serstopbits); gppi(sesskey, "SerialParity", SER_PAR_NONE, conf, CONF_serparity); gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, conf, CONF_serflow); gpps(sesskey, "WindowClass", "", conf, CONF_winclass); gppi(sesskey, "ConnectionSharing", 0, conf, CONF_ssh_connection_sharing); gppi(sesskey, "ConnectionSharingUpstream", 1, conf, CONF_ssh_connection_sharing_upstream); gppi(sesskey, "ConnectionSharingDownstream", 1, conf, CONF_ssh_connection_sharing_downstream); gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys); } void do_defaults(const char *session, Conf *conf) { load_settings(session, conf); } static int sessioncmp(const void *av, const void *bv) { const char *a = *(const char *const *) av; const char *b = *(const char *const *) bv; /* * Alphabetical order, except that "Default Settings" is a * special case and comes first. */ if (!strcmp(a, "Default Settings")) return -1; /* a comes first */ if (!strcmp(b, "Default Settings")) return +1; /* b comes first */ /* * FIXME: perhaps we should ignore the first & in determining * sort order. */ return strcmp(a, b); /* otherwise, compare normally */ } void get_sesslist(struct sesslist *list, int allocate) { char otherbuf[2048]; int buflen, bufsize, i; char *p, *ret; void *handle; if (allocate) { buflen = bufsize = 0; list->buffer = NULL; if ((handle = enum_settings_start()) != NULL) { do { ret = enum_settings_next(handle, otherbuf, sizeof(otherbuf)); if (ret) { int len = strlen(otherbuf) + 1; if (bufsize < buflen + len) { bufsize = buflen + len + 2048; list->buffer = sresize(list->buffer, bufsize, char); } strcpy(list->buffer + buflen, otherbuf); buflen += strlen(list->buffer + buflen) + 1; } } while (ret); enum_settings_finish(handle); } list->buffer = sresize(list->buffer, buflen + 1, char); list->buffer[buflen] = '\0'; /* * Now set up the list of sessions. Note that "Default * Settings" must always be claimed to exist, even if it * doesn't really. */ p = list->buffer; list->nsessions = 1; /* "Default Settings" counts as one */ while (*p) { if (strcmp(p, "Default Settings")) list->nsessions++; while (*p) p++; p++; } list->sessions = snewn(list->nsessions + 1, const char *); list->sessions[0] = "Default Settings"; p = list->buffer; i = 1; while (*p) { if (strcmp(p, "Default Settings")) list->sessions[i++] = p; while (*p) p++; p++; } qsort(list->sessions, i, sizeof(const char *), sessioncmp); } else { sfree(list->buffer); sfree(list->sessions); list->buffer = NULL; list->sessions = NULL; } } its-playback-time-0.2017-08-30.3c40fd3/slookup.c000066400000000000000000000010171333656753000205060ustar00rootroot00000000000000/* * slookup.c - static lookup of character sets. */ #include "charset.h" #include "internal.h" #define ENUM_CHARSET(x) extern charset_spec const charset_##x; #include "enum.c" #undef ENUM_CHARSET static charset_spec const *const cs_table[] = { #define ENUM_CHARSET(x) &charset_##x, #include "enum.c" #undef ENUM_CHARSET }; charset_spec const *charset_find_spec(int charset) { int i; for (i = 0; i < (int)lenof(cs_table); i++) if (cs_table[i]->charset == charset) return cs_table[i]; return NULL; } its-playback-time-0.2017-08-30.3c40fd3/storage.h000066400000000000000000000077431333656753000204770ustar00rootroot00000000000000/* * storage.h: interface defining functions for storage and recovery * of PuTTY's persistent data. */ #ifndef PUTTY_STORAGE_H #define PUTTY_STORAGE_H /* ---------------------------------------------------------------------- * Functions to save and restore PuTTY sessions. Note that this is * only the low-level code to do the reading and writing. The * higher-level code that translates an internal Conf structure into * a set of (key,value) pairs in their external storage format is * elsewhere, since it doesn't (mostly) change between platforms. */ /* * Write a saved session. The caller is expected to call * open_setting_w() to get a `void *' handle, then pass that to a * number of calls to write_setting_s() and write_setting_i(), and * then close it using close_settings_w(). At the end of this call * sequence the settings should have been written to the PuTTY * persistent storage area. * * A given key will be written at most once while saving a session. * Keys may be up to 255 characters long. String values have no length * limit. * * Any returned error message must be freed after use. */ void *open_settings_w(const char *sessionname, char **errmsg); void write_setting_s(void *handle, const char *key, const char *value); void write_setting_i(void *handle, const char *key, int value); void write_setting_filename(void *handle, const char *key, Filename *value); void write_setting_fontspec(void *handle, const char *key, FontSpec *font); void close_settings_w(void *handle); /* * Read a saved session. The caller is expected to call * open_setting_r() to get a `void *' handle, then pass that to a * number of calls to read_setting_s() and read_setting_i(), and * then close it using close_settings_r(). * * read_setting_s() returns a dynamically allocated string which the * caller must free. read_setting_filename() and * read_setting_fontspec() likewise return dynamically allocated * structures. * * If a particular string setting is not present in the session, * read_setting_s() can return NULL, in which case the caller * should invent a sensible default. If an integer setting is not * present, read_setting_i() returns its provided default. */ void *open_settings_r(const char *sessionname); char *read_setting_s(void *handle, const char *key); int read_setting_i(void *handle, const char *key, int defvalue); Filename *read_setting_filename(void *handle, const char *key); FontSpec *read_setting_fontspec(void *handle, const char *key); void close_settings_r(void *handle); /* * Delete a whole saved session. */ void del_settings(const char *sessionname); /* * Enumerate all saved sessions. */ void *enum_settings_start(void); char *enum_settings_next(void *handle, char *buffer, int buflen); void enum_settings_finish(void *handle); /* ---------------------------------------------------------------------- * Functions to access PuTTY's host key database. */ /* * See if a host key matches the database entry. Return values can * be 0 (entry matches database), 1 (entry is absent in database), * or 2 (entry exists in database and is different). */ int verify_host_key(const char *hostname, int port, const char *keytype, const char *key); /* * Write a host key into the database, overwriting any previous * entry that might have been there. */ void store_host_key(const char *hostname, int port, const char *keytype, const char *key); /* ---------------------------------------------------------------------- * Functions to access PuTTY's random number seed file. */ typedef void (*noise_consumer_t) (void *data, int len); /* * Read PuTTY's random seed file and pass its contents to a noise * consumer function. */ void read_random_seed(noise_consumer_t consumer); /* * Write PuTTY's random seed file from a given chunk of noise. */ void write_random_seed(void *data, int len); /* ---------------------------------------------------------------------- * Cleanup function: remove all of PuTTY's persistent state. */ void cleanup_all(void); #endif its-playback-time-0.2017-08-30.3c40fd3/terminal.c000066400000000000000000005523771333656753000206510ustar00rootroot00000000000000/* * Terminal emulator. */ #include #include #include #include #include #include #include "putty.h" #include "terminal.h" #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) ) #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) ) #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x ) #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (term->cols+1) + (p1).x - (p2).x ) /* Product-order comparisons for rectangular block selection. */ #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x ) #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x ) #define incpos(p) ( (p).x == term->cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) ) #define decpos(p) ( (p).x == 0 ? ((p).x = term->cols, (p).y--, 1) : ((p).x--, 0) ) #define VT52_PLUS #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */ #define CL_VT100 0x0002 /* VT100 */ #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */ #define CL_VT102 0x0008 /* VT102 */ #define CL_VT220 0x0010 /* VT220 */ #define CL_VT320 0x0020 /* VT320 */ #define CL_VT420 0x0040 /* VT420 */ #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */ #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */ #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */ #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */ #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */ #define TM_VT100 (CL_ANSIMIN|CL_VT100) #define TM_VT100AVO (TM_VT100|CL_VT100AVO) #define TM_VT102 (TM_VT100AVO|CL_VT102) #define TM_VT220 (TM_VT102|CL_VT220) #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320) #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI) #define TM_PUTTY (0xFFFF) #define UPDATE_DELAY ((TICKSPERSEC+49)/50)/* ticks to defer window update */ #define TBLINK_DELAY ((TICKSPERSEC*9+19)/20)/* ticks between text blinks*/ #define CBLINK_DELAY (CURSORBLINK) /* ticks between cursor blinks */ #define VBELL_DELAY (VBELL_TIMEOUT) /* visual bell timeout in ticks */ #define compatibility(x) \ if ( ((CL_##x)&term->compatibility_level) == 0 ) { \ term->termstate=TOPLEVEL; \ break; \ } #define compatibility2(x,y) \ if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \ term->termstate=TOPLEVEL; \ break; \ } #define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 ) const char *EMPTY_WINDOW_TITLE = ""; const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t)) const wchar_t sel_nl[] = SEL_NL; /* * Fetch the character at a particular position in a line array, * for purposes of `wordtype'. The reason this isn't just a simple * array reference is that if the character we find is UCSWIDE, * then we must look one space further to the left. */ #define UCSGET(a, x) \ ( (x)>0 && (a)[(x)].chr == UCSWIDE ? (a)[(x)-1].chr : (a)[(x)].chr ) /* * Detect the various aliases of U+0020 SPACE. */ #define IS_SPACE_CHR(chr) \ ((chr) == 0x20 || (DIRECT_CHAR(chr) && ((chr) & 0xFF) == 0x20)) /* * Spot magic CSETs. */ #define CSET_OF(chr) (DIRECT_CHAR(chr)||DIRECT_FONT(chr) ? (chr)&CSET_MASK : 0) /* * Internal prototypes. */ static void resizeline(Terminal *, termline *, int); static termline *lineptr(Terminal *, int, int, int); static void unlineptr(termline *); static void check_line_size(Terminal *, termline *); static void do_paint(Terminal *, Context, int); static void erase_lots(Terminal *, int, int, int); static int find_last_nonempty_line(Terminal *, tree234 *); static void swap_screen(Terminal *, int, int, int); static void update_sbar(Terminal *); static void deselect(Terminal *); static void term_print_finish(Terminal *); static void scroll(Terminal *, int, int, int, int); #ifdef OPTIMISE_SCROLL static void scroll_display(Terminal *, int, int, int); #endif /* OPTIMISE_SCROLL */ static termline *newline(Terminal *term, int cols, int bce) { termline *line; int j; line = snew(termline); line->chars = snewn(cols, termchar); for (j = 0; j < cols; j++) line->chars[j] = (bce ? term->erase_char : term->basic_erase_char); line->cols = line->size = cols; line->lattr = LATTR_NORM; line->temporary = FALSE; line->cc_free = 0; return line; } static void freeline(termline *line) { if (line) { sfree(line->chars); sfree(line); } } static void unlineptr(termline *line) { if (line->temporary) freeline(line); } #ifdef TERM_CC_DIAGS /* * Diagnostic function: verify that a termline has a correct * combining character structure. * * This is a performance-intensive check, so it's no longer enabled * by default. */ static void cc_check(termline *line) { unsigned char *flags; int i, j; assert(line->size >= line->cols); flags = snewn(line->size, unsigned char); for (i = 0; i < line->size; i++) flags[i] = (i < line->cols); for (i = 0; i < line->cols; i++) { j = i; while (line->chars[j].cc_next) { j += line->chars[j].cc_next; assert(j >= line->cols && j < line->size); assert(!flags[j]); flags[j] = TRUE; } } j = line->cc_free; if (j) { while (1) { assert(j >= line->cols && j < line->size); assert(!flags[j]); flags[j] = TRUE; if (line->chars[j].cc_next) j += line->chars[j].cc_next; else break; } } j = 0; for (i = 0; i < line->size; i++) j += (flags[i] != 0); assert(j == line->size); sfree(flags); } #endif /* * Add a combining character to a character cell. */ static void add_cc(termline *line, int col, unsigned long chr) { int newcc; assert(col >= 0 && col < line->cols); /* * Start by extending the cols array if the free list is empty. */ if (!line->cc_free) { int n = line->size; line->size += 16 + (line->size - line->cols) / 2; line->chars = sresize(line->chars, line->size, termchar); line->cc_free = n; while (n < line->size) { if (n+1 < line->size) line->chars[n].cc_next = 1; else line->chars[n].cc_next = 0; n++; } } /* * Now walk the cc list of the cell in question. */ while (line->chars[col].cc_next) col += line->chars[col].cc_next; /* * `col' now points at the last cc currently in this cell; so * we simply add another one. */ newcc = line->cc_free; if (line->chars[newcc].cc_next) line->cc_free = newcc + line->chars[newcc].cc_next; else line->cc_free = 0; line->chars[newcc].cc_next = 0; line->chars[newcc].chr = chr; line->chars[col].cc_next = newcc - col; #ifdef TERM_CC_DIAGS cc_check(line); #endif } /* * Clear the combining character list in a character cell. */ static void clear_cc(termline *line, int col) { int oldfree, origcol = col; assert(col >= 0 && col < line->cols); if (!line->chars[col].cc_next) return; /* nothing needs doing */ oldfree = line->cc_free; line->cc_free = col + line->chars[col].cc_next; while (line->chars[col].cc_next) col += line->chars[col].cc_next; if (oldfree) line->chars[col].cc_next = oldfree - col; else line->chars[col].cc_next = 0; line->chars[origcol].cc_next = 0; #ifdef TERM_CC_DIAGS cc_check(line); #endif } /* * Compare two character cells for equality. Special case required * in do_paint() where we override what we expect the chr and attr * fields to be. */ static int termchars_equal_override(termchar *a, termchar *b, unsigned long bchr, unsigned long battr) { /* FULL-TERMCHAR */ if (a->chr != bchr) return FALSE; if ((a->attr &~ DATTR_MASK) != (battr &~ DATTR_MASK)) return FALSE; while (a->cc_next || b->cc_next) { if (!a->cc_next || !b->cc_next) return FALSE; /* one cc-list ends, other does not */ a += a->cc_next; b += b->cc_next; if (a->chr != b->chr) return FALSE; } return TRUE; } static int termchars_equal(termchar *a, termchar *b) { return termchars_equal_override(a, b, b->chr, b->attr); } /* * Copy a character cell. (Requires a pointer to the destination * termline, so as to access its free list.) */ static void copy_termchar(termline *destline, int x, termchar *src) { clear_cc(destline, x); destline->chars[x] = *src; /* copy everything except cc-list */ destline->chars[x].cc_next = 0; /* and make sure this is zero */ while (src->cc_next) { src += src->cc_next; add_cc(destline, x, src->chr); } #ifdef TERM_CC_DIAGS cc_check(destline); #endif } /* * Move a character cell within its termline. */ static void move_termchar(termline *line, termchar *dest, termchar *src) { /* First clear the cc list from the original char, just in case. */ clear_cc(line, dest - line->chars); /* Move the character cell and adjust its cc_next. */ *dest = *src; /* copy everything except cc-list */ if (src->cc_next) dest->cc_next = src->cc_next - (dest-src); /* Ensure the original cell doesn't have a cc list. */ src->cc_next = 0; #ifdef TERM_CC_DIAGS cc_check(line); #endif } /* * Compress and decompress a termline into an RLE-based format for * storing in scrollback. (Since scrollback almost never needs to * be modified and exists in huge quantities, this is a sensible * tradeoff, particularly since it allows us to continue adding * features to the main termchar structure without proportionally * bloating the terminal emulator's memory footprint unless those * features are in constant use.) */ struct buf { unsigned char *data; int len, size; }; static void add(struct buf *b, unsigned char c) { if (b->len >= b->size) { b->size = (b->len * 3 / 2) + 512; b->data = sresize(b->data, b->size, unsigned char); } b->data[b->len++] = c; } static int get(struct buf *b) { return b->data[b->len++]; } static void makerle(struct buf *b, termline *ldata, void (*makeliteral)(struct buf *b, termchar *c, unsigned long *state)) { int hdrpos, hdrsize, n, prevlen, prevpos, thislen, thispos, prev2; termchar *c = ldata->chars; unsigned long state = 0, oldstate; n = ldata->cols; hdrpos = b->len; hdrsize = 0; add(b, 0); prevlen = prevpos = 0; prev2 = FALSE; while (n-- > 0) { thispos = b->len; makeliteral(b, c++, &state); thislen = b->len - thispos; if (thislen == prevlen && !memcmp(b->data + prevpos, b->data + thispos, thislen)) { /* * This literal precisely matches the previous one. * Turn it into a run if it's worthwhile. * * With one-byte literals, it costs us two bytes to * encode a run, plus another byte to write the header * to resume normal output; so a three-element run is * neutral, and anything beyond that is unconditionally * worthwhile. With two-byte literals or more, even a * 2-run is a win. */ if (thislen > 1 || prev2) { int runpos, runlen; /* * It's worth encoding a run. Start at prevpos, * unless hdrsize==0 in which case we can back up * another one and start by overwriting hdrpos. */ hdrsize--; /* remove the literal at prevpos */ if (prev2) { assert(hdrsize > 0); hdrsize--; prevpos -= prevlen;/* and possibly another one */ } if (hdrsize == 0) { assert(prevpos == hdrpos + 1); runpos = hdrpos; b->len = prevpos+prevlen; } else { memmove(b->data + prevpos+1, b->data + prevpos, prevlen); runpos = prevpos; b->len = prevpos+prevlen+1; /* * Terminate the previous run of ordinary * literals. */ assert(hdrsize >= 1 && hdrsize <= 128); b->data[hdrpos] = hdrsize - 1; } runlen = prev2 ? 3 : 2; while (n > 0 && runlen < 129) { int tmppos, tmplen; tmppos = b->len; oldstate = state; makeliteral(b, c, &state); tmplen = b->len - tmppos; b->len = tmppos; if (tmplen != thislen || memcmp(b->data + runpos+1, b->data + tmppos, tmplen)) { state = oldstate; break; /* run over */ } n--, c++, runlen++; } assert(runlen >= 2 && runlen <= 129); b->data[runpos] = runlen + 0x80 - 2; hdrpos = b->len; hdrsize = 0; add(b, 0); /* And ensure this run doesn't interfere with the next. */ prevlen = prevpos = 0; prev2 = FALSE; continue; } else { /* * Just flag that the previous two literals were * identical, in case we find a third identical one * we want to turn into a run. */ prev2 = TRUE; prevlen = thislen; prevpos = thispos; } } else { prev2 = FALSE; prevlen = thislen; prevpos = thispos; } /* * This character isn't (yet) part of a run. Add it to * hdrsize. */ hdrsize++; if (hdrsize == 128) { b->data[hdrpos] = hdrsize - 1; hdrpos = b->len; hdrsize = 0; add(b, 0); prevlen = prevpos = 0; prev2 = FALSE; } } /* * Clean up. */ if (hdrsize > 0) { assert(hdrsize <= 128); b->data[hdrpos] = hdrsize - 1; } else { b->len = hdrpos; } } static void makeliteral_chr(struct buf *b, termchar *c, unsigned long *state) { /* * My encoding for characters is UTF-8-like, in that it stores * 7-bit ASCII in one byte and uses high-bit-set bytes as * introducers to indicate a longer sequence. However, it's * unlike UTF-8 in that it doesn't need to be able to * resynchronise, and therefore I don't want to waste two bits * per byte on having recognisable continuation characters. * Also I don't want to rule out the possibility that I may one * day use values 0x80000000-0xFFFFFFFF for interesting * purposes, so unlike UTF-8 I need a full 32-bit range. * Accordingly, here is my encoding: * * 00000000-0000007F: 0xxxxxxx (but see below) * 00000080-00003FFF: 10xxxxxx xxxxxxxx * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx * * (`Z' is like `x' but is always going to be zero since the * values I'm encoding don't go above 2^32. In principle the * five-byte form of the encoding could extend to 2^35, and * there could be six-, seven-, eight- and nine-byte forms as * well to allow up to 64-bit values to be encoded. But that's * completely unnecessary for these purposes!) * * The encoding as written above would be very simple, except * that 7-bit ASCII can occur in several different ways in the * terminal data; sometimes it crops up in the D800 page * (CSET_ASCII) but at other times it's in the 0000 page (real * Unicode). Therefore, this encoding is actually _stateful_: * the one-byte encoding of 00-7F actually indicates `reuse the * upper three bytes of the last character', and to encode an * absolute value of 00-7F you need to use the two-byte form * instead. */ if ((c->chr & ~0x7F) == *state) { add(b, (unsigned char)(c->chr & 0x7F)); } else if (c->chr < 0x4000) { add(b, (unsigned char)(((c->chr >> 8) & 0x3F) | 0x80)); add(b, (unsigned char)(c->chr & 0xFF)); } else if (c->chr < 0x200000) { add(b, (unsigned char)(((c->chr >> 16) & 0x1F) | 0xC0)); add(b, (unsigned char)((c->chr >> 8) & 0xFF)); add(b, (unsigned char)(c->chr & 0xFF)); } else if (c->chr < 0x10000000) { add(b, (unsigned char)(((c->chr >> 24) & 0x0F) | 0xE0)); add(b, (unsigned char)((c->chr >> 16) & 0xFF)); add(b, (unsigned char)((c->chr >> 8) & 0xFF)); add(b, (unsigned char)(c->chr & 0xFF)); } else { add(b, 0xF0); add(b, (unsigned char)((c->chr >> 24) & 0xFF)); add(b, (unsigned char)((c->chr >> 16) & 0xFF)); add(b, (unsigned char)((c->chr >> 8) & 0xFF)); add(b, (unsigned char)(c->chr & 0xFF)); } *state = c->chr & ~0xFF; } static void makeliteral_attr(struct buf *b, termchar *c, unsigned long *state) { /* * My encoding for attributes is 16-bit-granular and assumes * that the top bit of the word is never required. I either * store a two-byte value with the top bit clear (indicating * just that value), or a four-byte value with the top bit set * (indicating the same value with its top bit clear). * * However, first I permute the bits of the attribute value, so * that the eight bits of colour (four in each of fg and bg) * which are never non-zero unless xterm 256-colour mode is in * use are placed higher up the word than everything else. This * ensures that attribute values remain 16-bit _unless_ the * user uses extended colour. */ unsigned attr, colourbits; attr = c->attr; assert(ATTR_BGSHIFT > ATTR_FGSHIFT); colourbits = (attr >> (ATTR_BGSHIFT + 4)) & 0xF; colourbits <<= 4; colourbits |= (attr >> (ATTR_FGSHIFT + 4)) & 0xF; attr = (((attr >> (ATTR_BGSHIFT + 8)) << (ATTR_BGSHIFT + 4)) | (attr & ((1 << (ATTR_BGSHIFT + 4))-1))); attr = (((attr >> (ATTR_FGSHIFT + 8)) << (ATTR_FGSHIFT + 4)) | (attr & ((1 << (ATTR_FGSHIFT + 4))-1))); attr |= (colourbits << (32-9)); if (attr < 0x8000) { add(b, (unsigned char)((attr >> 8) & 0xFF)); add(b, (unsigned char)(attr & 0xFF)); } else { add(b, (unsigned char)(((attr >> 24) & 0x7F) | 0x80)); add(b, (unsigned char)((attr >> 16) & 0xFF)); add(b, (unsigned char)((attr >> 8) & 0xFF)); add(b, (unsigned char)(attr & 0xFF)); } } static void makeliteral_cc(struct buf *b, termchar *c, unsigned long *state) { /* * For combining characters, I just encode a bunch of ordinary * chars using makeliteral_chr, and terminate with a \0 * character (which I know won't come up as a combining char * itself). * * I don't use the stateful encoding in makeliteral_chr. */ unsigned long zstate; termchar z; while (c->cc_next) { c += c->cc_next; assert(c->chr != 0); zstate = 0; makeliteral_chr(b, c, &zstate); } z.chr = 0; zstate = 0; makeliteral_chr(b, &z, &zstate); } static termline *decompressline(unsigned char *data, int *bytes_used); static unsigned char *compressline(termline *ldata) { struct buf buffer = { NULL, 0, 0 }, *b = &buffer; /* * First, store the column count, 7 bits at a time, least * significant `digit' first, with the high bit set on all but * the last. */ { int n = ldata->cols; while (n >= 128) { add(b, (unsigned char)((n & 0x7F) | 0x80)); n >>= 7; } add(b, (unsigned char)(n)); } /* * Next store the lattrs; same principle. */ { int n = ldata->lattr; while (n >= 128) { add(b, (unsigned char)((n & 0x7F) | 0x80)); n >>= 7; } add(b, (unsigned char)(n)); } /* * Now we store a sequence of separate run-length encoded * fragments, each containing exactly as many symbols as there * are columns in the ldata. * * All of these have a common basic format: * * - a byte 00-7F indicates that X+1 literals follow it * - a byte 80-FF indicates that a single literal follows it * and expects to be repeated (X-0x80)+2 times. * * The format of the `literals' varies between the fragments. */ makerle(b, ldata, makeliteral_chr); makerle(b, ldata, makeliteral_attr); makerle(b, ldata, makeliteral_cc); /* * Diagnostics: ensure that the compressed data really does * decompress to the right thing. * * This is a bit performance-heavy for production code. */ #ifdef TERM_CC_DIAGS #ifndef CHECK_SB_COMPRESSION { int dused; termline *dcl; int i; #ifdef DIAGNOSTIC_SB_COMPRESSION for (i = 0; i < b->len; i++) { printf(" %02x ", b->data[i]); } printf("\n"); #endif dcl = decompressline(b->data, &dused); assert(b->len == dused); assert(ldata->cols == dcl->cols); assert(ldata->lattr == dcl->lattr); for (i = 0; i < ldata->cols; i++) assert(termchars_equal(&ldata->chars[i], &dcl->chars[i])); #ifdef DIAGNOSTIC_SB_COMPRESSION printf("%d cols (%d bytes) -> %d bytes (factor of %g)\n", ldata->cols, 4 * ldata->cols, dused, (double)dused / (4 * ldata->cols)); #endif freeline(dcl); } #endif #endif /* TERM_CC_DIAGS */ /* * Trim the allocated memory so we don't waste any, and return. */ return sresize(b->data, b->len, unsigned char); } static void readrle(struct buf *b, termline *ldata, void (*readliteral)(struct buf *b, termchar *c, termline *ldata, unsigned long *state)) { int n = 0; unsigned long state = 0; while (n < ldata->cols) { int hdr = get(b); if (hdr >= 0x80) { /* A run. */ int pos = b->len, count = hdr + 2 - 0x80; while (count--) { assert(n < ldata->cols); b->len = pos; readliteral(b, ldata->chars + n, ldata, &state); n++; } } else { /* Just a sequence of consecutive literals. */ int count = hdr + 1; while (count--) { assert(n < ldata->cols); readliteral(b, ldata->chars + n, ldata, &state); n++; } } } assert(n == ldata->cols); } static void readliteral_chr(struct buf *b, termchar *c, termline *ldata, unsigned long *state) { int byte; /* * 00000000-0000007F: 0xxxxxxx * 00000080-00003FFF: 10xxxxxx xxxxxxxx * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */ byte = get(b); if (byte < 0x80) { c->chr = byte | *state; } else if (byte < 0xC0) { c->chr = (byte &~ 0xC0) << 8; c->chr |= get(b); } else if (byte < 0xE0) { c->chr = (byte &~ 0xE0) << 16; c->chr |= get(b) << 8; c->chr |= get(b); } else if (byte < 0xF0) { c->chr = (byte &~ 0xF0) << 24; c->chr |= get(b) << 16; c->chr |= get(b) << 8; c->chr |= get(b); } else { assert(byte == 0xF0); c->chr = get(b) << 24; c->chr |= get(b) << 16; c->chr |= get(b) << 8; c->chr |= get(b); } *state = c->chr & ~0xFF; } static void readliteral_attr(struct buf *b, termchar *c, termline *ldata, unsigned long *state) { unsigned val, attr, colourbits; val = get(b) << 8; val |= get(b); if (val >= 0x8000) { val &= ~0x8000; val <<= 16; val |= get(b) << 8; val |= get(b); } colourbits = (val >> (32-9)) & 0xFF; attr = (val & ((1<<(32-9))-1)); attr = (((attr >> (ATTR_FGSHIFT + 4)) << (ATTR_FGSHIFT + 8)) | (attr & ((1 << (ATTR_FGSHIFT + 4))-1))); attr = (((attr >> (ATTR_BGSHIFT + 4)) << (ATTR_BGSHIFT + 8)) | (attr & ((1 << (ATTR_BGSHIFT + 4))-1))); attr |= (colourbits >> 4) << (ATTR_BGSHIFT + 4); attr |= (colourbits & 0xF) << (ATTR_FGSHIFT + 4); c->attr = attr; } static void readliteral_cc(struct buf *b, termchar *c, termline *ldata, unsigned long *state) { termchar n; unsigned long zstate; int x = c - ldata->chars; c->cc_next = 0; while (1) { zstate = 0; readliteral_chr(b, &n, ldata, &zstate); if (!n.chr) break; add_cc(ldata, x, n.chr); } } static termline *decompressline(unsigned char *data, int *bytes_used) { int ncols, byte, shift; struct buf buffer, *b = &buffer; termline *ldata; b->data = data; b->len = 0; /* * First read in the column count. */ ncols = shift = 0; do { byte = get(b); ncols |= (byte & 0x7F) << shift; shift += 7; } while (byte & 0x80); /* * Now create the output termline. */ ldata = snew(termline); ldata->chars = snewn(ncols, termchar); ldata->cols = ldata->size = ncols; ldata->temporary = TRUE; ldata->cc_free = 0; /* * We must set all the cc pointers in ldata->chars to 0 right * now, so that cc diagnostics that verify the integrity of the * whole line will make sense while we're in the middle of * building it up. */ { int i; for (i = 0; i < ldata->cols; i++) ldata->chars[i].cc_next = 0; } /* * Now read in the lattr. */ ldata->lattr = shift = 0; do { byte = get(b); ldata->lattr |= (byte & 0x7F) << shift; shift += 7; } while (byte & 0x80); /* * Now we read in each of the RLE streams in turn. */ readrle(b, ldata, readliteral_chr); readrle(b, ldata, readliteral_attr); readrle(b, ldata, readliteral_cc); /* Return the number of bytes read, for diagnostic purposes. */ if (bytes_used) *bytes_used = b->len; return ldata; } /* * Resize a line to make it `cols' columns wide. */ static void resizeline(Terminal *term, termline *line, int cols) { int i, oldcols; if (line->cols != cols) { oldcols = line->cols; /* * This line is the wrong length, which probably means it * hasn't been accessed since a resize. Resize it now. * * First, go through all the characters that will be thrown * out in the resize (if we're shrinking the line) and * return their cc lists to the cc free list. */ for (i = cols; i < oldcols; i++) clear_cc(line, i); /* * If we're shrinking the line, we now bodily move the * entire cc section from where it started to where it now * needs to be. (We have to do this before the resize, so * that the data we're copying is still there. However, if * we're expanding, we have to wait until _after_ the * resize so that the space we're copying into is there.) */ if (cols < oldcols) memmove(line->chars + cols, line->chars + oldcols, (line->size - line->cols) * TSIZE); /* * Now do the actual resize, leaving the _same_ amount of * cc space as there was to begin with. */ line->size += cols - oldcols; line->chars = sresize(line->chars, line->size, TTYPE); line->cols = cols; /* * If we're expanding the line, _now_ we move the cc * section. */ if (cols > oldcols) memmove(line->chars + cols, line->chars + oldcols, (line->size - line->cols) * TSIZE); /* * Go through what's left of the original line, and adjust * the first cc_next pointer in each list. (All the * subsequent ones are still valid because they are * relative offsets within the cc block.) Also do the same * to the head of the cc_free list. */ for (i = 0; i < oldcols && i < cols; i++) if (line->chars[i].cc_next) line->chars[i].cc_next += cols - oldcols; if (line->cc_free) line->cc_free += cols - oldcols; /* * And finally fill in the new space with erase chars. (We * don't have to worry about cc lists here, because we * _know_ the erase char doesn't have one.) */ for (i = oldcols; i < cols; i++) line->chars[i] = term->basic_erase_char; #ifdef TERM_CC_DIAGS cc_check(line); #endif } } /* * Get the number of lines in the scrollback. */ static int sblines(Terminal *term) { int sblines = count234(term->scrollback); if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { sblines += term->alt_sblines; } return sblines; } /* * Retrieve a line of the screen or of the scrollback, according to * whether the y coordinate is non-negative or negative * (respectively). */ static termline *lineptr(Terminal *term, int y, int lineno, int screen) { termline *line; tree234 *whichtree; int treeindex; if (y >= 0) { whichtree = term->screen; treeindex = y; } else { int altlines = 0; assert(!screen); if (term->erase_to_scrollback && term->alt_which && term->alt_screen) { altlines = term->alt_sblines; } if (y < -altlines) { whichtree = term->scrollback; treeindex = y + altlines + count234(term->scrollback); } else { whichtree = term->alt_screen; treeindex = y + term->alt_sblines; /* treeindex = y + count234(term->alt_screen); */ } } if (whichtree == term->scrollback) { unsigned char *cline = index234(whichtree, treeindex); line = decompressline(cline, NULL); } else { line = index234(whichtree, treeindex); } /* We assume that we don't screw up and retrieve something out of range. */ if (line == NULL) { fatalbox("line==NULL in terminal.c\n" "lineno=%d y=%d w=%d h=%d\n" "count(scrollback=%p)=%d\n" "count(screen=%p)=%d\n" "count(alt=%p)=%d alt_sblines=%d\n" "whichtree=%p treeindex=%d\n\n" "Please contact " "and pass on the above information.", lineno, y, term->cols, term->rows, term->scrollback, count234(term->scrollback), term->screen, count234(term->screen), term->alt_screen, count234(term->alt_screen), term->alt_sblines, whichtree, treeindex); } assert(line != NULL); /* * Here we resize lines to _at least_ the right length, but we * don't truncate them. Truncation is done as a side effect of * modifying the line. * * The point of this policy is to try to arrange that resizing the * terminal window repeatedly - e.g. successive steps in an X11 * opaque window-resize drag, or resizing as a side effect of * retiling by tiling WMs such as xmonad - does not throw away * data gratuitously. Specifically, we want a sequence of resize * operations with no terminal output between them to have the * same effect as a single resize to the ultimate terminal size, * and also (for the case in which xmonad narrows a window that's * scrolling things) we want scrolling up new text at the bottom * of a narrowed window to avoid truncating lines further up when * the window is re-widened. */ if (term->cols > line->cols) resizeline(term, line, term->cols); return line; } #define lineptr(x) (lineptr)(term,x,__LINE__,FALSE) #define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE) /* * Coerce a termline to the terminal's current width. Unlike the * optional resize in lineptr() above, this is potentially destructive * of text, since it can shrink as well as grow the line. * * We call this whenever a termline is actually going to be modified. * Helpfully, putting a single call to this function in check_boundary * deals with _nearly_ all such cases, leaving only a few things like * bulk erase and ESC#8 to handle separately. */ static void check_line_size(Terminal *term, termline *line) { if (term->cols != line->cols) /* trivial optimisation */ resizeline(term, line, term->cols); } static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); static void term_timer(void *ctx, unsigned long now) { Terminal *term = (Terminal *)ctx; int update = FALSE; if (term->tblink_pending && now == term->next_tblink) { term->tblinker = !term->tblinker; term->tblink_pending = FALSE; term_schedule_tblink(term); update = TRUE; } if (term->cblink_pending && now == term->next_cblink) { term->cblinker = !term->cblinker; term->cblink_pending = FALSE; term_schedule_cblink(term); update = TRUE; } if (term->in_vbell && now == term->vbell_end) { term->in_vbell = FALSE; update = TRUE; } if (update || (term->window_update_pending && now == term->next_update)) term_update(term); } static void term_schedule_update(Terminal *term) { if (!term->window_update_pending) { term->window_update_pending = TRUE; term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term); } } /* * Call this whenever the terminal window state changes, to queue * an update. */ static void seen_disp_event(Terminal *term) { term->seen_disp_event = TRUE; /* for scrollback-reset-on-activity */ term_schedule_update(term); } /* * Call when the terminal's blinking-text settings change, or when * a text blink has just occurred. */ static void term_schedule_tblink(Terminal *term) { if (term->blink_is_real) { if (!term->tblink_pending) term->next_tblink = schedule_timer(TBLINK_DELAY, term_timer, term); term->tblink_pending = TRUE; } else { term->tblinker = 1; /* reset when not in use */ term->tblink_pending = FALSE; } } /* * Likewise with cursor blinks. */ static void term_schedule_cblink(Terminal *term) { if (term->blink_cur && term->has_focus) { if (!term->cblink_pending) term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term); term->cblink_pending = TRUE; } else { term->cblinker = 1; /* reset when not in use */ term->cblink_pending = FALSE; } } /* * Call to reset cursor blinking on new output. */ static void term_reset_cblink(Terminal *term) { seen_disp_event(term); term->cblinker = 1; term->cblink_pending = FALSE; term_schedule_cblink(term); } /* * Call to begin a visual bell. */ static void term_schedule_vbell(Terminal *term, int already_started, long startpoint) { long ticks_already_gone; if (already_started) ticks_already_gone = GETTICKCOUNT() - startpoint; else ticks_already_gone = 0; if (ticks_already_gone < VBELL_DELAY) { term->in_vbell = TRUE; term->vbell_end = schedule_timer(VBELL_DELAY - ticks_already_gone, term_timer, term); } else { term->in_vbell = FALSE; } } /* * Set up power-on settings for the terminal. * If 'clear' is false, don't actually clear the primary screen, and * position the cursor below the last non-blank line (scrolling if * necessary). */ static void power_on(Terminal *term, int clear) { term->alt_x = term->alt_y = 0; term->savecurs.x = term->savecurs.y = 0; term->alt_savecurs.x = term->alt_savecurs.y = 0; term->alt_t = term->marg_t = 0; if (term->rows != -1) term->alt_b = term->marg_b = term->rows - 1; else term->alt_b = term->marg_b = 0; if (term->cols != -1) { int i; for (i = 0; i < term->cols; i++) term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE); } term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); term->alt_ins = term->insert = FALSE; term->alt_wnext = term->wrapnext = term->save_wnext = term->alt_save_wnext = FALSE; term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0; term->alt_utf = term->utf = term->save_utf = term->alt_save_utf = 0; term->utf_state = 0; term->alt_sco_acs = term->sco_acs = term->save_sco_acs = term->alt_save_sco_acs = 0; term->cset_attr[0] = term->cset_attr[1] = term->save_csattr = term->alt_save_csattr = CSET_ASCII; term->rvideo = 0; term->in_vbell = FALSE; term->cursor_on = 1; term->big_cursor = 0; term->default_attr = term->save_attr = term->alt_save_attr = term->curr_attr = ATTR_DEFAULT; term->term_editing = term->term_echoing = FALSE; term->app_cursor_keys = conf_get_int(term->conf, CONF_app_cursor); term->app_keypad_keys = conf_get_int(term->conf, CONF_app_keypad); term->use_bce = conf_get_int(term->conf, CONF_bce); term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); term->erase_char = term->basic_erase_char; term->alt_which = 0; term_print_finish(term); term->xterm_mouse = 0; term->xterm_extended_mouse = 0; term->urxvt_extended_mouse = 0; set_raw_mouse_mode(term->frontend, FALSE); term->bracketed_paste = FALSE; { int i; for (i = 0; i < 256; i++) term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); } if (term->screen) { swap_screen(term, 1, FALSE, FALSE); erase_lots(term, FALSE, TRUE, TRUE); swap_screen(term, 0, FALSE, FALSE); if (clear) erase_lots(term, FALSE, TRUE, TRUE); term->curs.y = find_last_nonempty_line(term, term->screen) + 1; if (term->curs.y == term->rows) { term->curs.y--; scroll(term, 0, term->rows - 1, 1, TRUE); } } else { term->curs.y = 0; } term->curs.x = 0; term_schedule_tblink(term); term_schedule_cblink(term); } /* * Force a screen update. */ void term_update(Terminal *term) { Context ctx; term->window_update_pending = FALSE; ctx = get_ctx(term->frontend); if (ctx) { int need_sbar_update = term->seen_disp_event; if (term->seen_disp_event && term->scroll_on_disp) { term->disptop = 0; /* return to main screen */ term->seen_disp_event = 0; need_sbar_update = TRUE; } if (need_sbar_update) update_sbar(term); do_paint(term, ctx, TRUE); sys_cursor(term->frontend, term->curs.x, term->curs.y - term->disptop); free_ctx(ctx); } } /* * Called from front end when a keypress occurs, to trigger * anything magical that needs to happen in that situation. */ void term_seen_key_event(Terminal *term) { /* * On any keypress, clear the bell overload mechanism * completely, on the grounds that large numbers of * beeps coming from deliberate key action are likely * to be intended (e.g. beeps from filename completion * blocking repeatedly). */ term->beep_overloaded = FALSE; while (term->beephead) { struct beeptime *tmp = term->beephead; term->beephead = tmp->next; sfree(tmp); } term->beeptail = NULL; term->nbeeps = 0; /* * Reset the scrollback on keypress, if we're doing that. */ if (term->scroll_on_key) { term->disptop = 0; /* return to main screen */ seen_disp_event(term); } } /* * Same as power_on(), but an external function. */ void term_pwron(Terminal *term, int clear) { power_on(term, clear); if (term->ldisc) /* cause ldisc to notice changes */ ldisc_echoedit_update(term->ldisc); term->disptop = 0; deselect(term); term_update(term); } static void set_erase_char(Terminal *term) { term->erase_char = term->basic_erase_char; if (term->use_bce) term->erase_char.attr = (term->curr_attr & (ATTR_FGMASK | ATTR_BGMASK)); } /* * We copy a bunch of stuff out of the Conf structure into local * fields in the Terminal structure, to avoid the repeated tree234 * lookups which would be involved in fetching them from the former * every time. */ void term_copy_stuff_from_conf(Terminal *term) { term->ansi_colour = conf_get_int(term->conf, CONF_ansi_colour); term->arabicshaping = conf_get_int(term->conf, CONF_arabicshaping); term->beep = conf_get_int(term->conf, CONF_beep); term->bellovl = conf_get_int(term->conf, CONF_bellovl); term->bellovl_n = conf_get_int(term->conf, CONF_bellovl_n); term->bellovl_s = conf_get_int(term->conf, CONF_bellovl_s); term->bellovl_t = conf_get_int(term->conf, CONF_bellovl_t); term->bidi = conf_get_int(term->conf, CONF_bidi); term->bksp_is_delete = conf_get_int(term->conf, CONF_bksp_is_delete); term->blink_cur = conf_get_int(term->conf, CONF_blink_cur); term->blinktext = conf_get_int(term->conf, CONF_blinktext); term->cjk_ambig_wide = conf_get_int(term->conf, CONF_cjk_ambig_wide); term->conf_height = conf_get_int(term->conf, CONF_height); term->conf_width = conf_get_int(term->conf, CONF_width); term->crhaslf = conf_get_int(term->conf, CONF_crhaslf); term->erase_to_scrollback = conf_get_int(term->conf, CONF_erase_to_scrollback); term->funky_type = conf_get_int(term->conf, CONF_funky_type); term->lfhascr = conf_get_int(term->conf, CONF_lfhascr); term->logflush = conf_get_int(term->conf, CONF_logflush); term->logtype = conf_get_int(term->conf, CONF_logtype); term->mouse_override = conf_get_int(term->conf, CONF_mouse_override); term->nethack_keypad = conf_get_int(term->conf, CONF_nethack_keypad); term->no_alt_screen = conf_get_int(term->conf, CONF_no_alt_screen); term->no_applic_c = conf_get_int(term->conf, CONF_no_applic_c); term->no_applic_k = conf_get_int(term->conf, CONF_no_applic_k); term->no_dbackspace = conf_get_int(term->conf, CONF_no_dbackspace); term->no_mouse_rep = conf_get_int(term->conf, CONF_no_mouse_rep); term->no_remote_charset = conf_get_int(term->conf, CONF_no_remote_charset); term->no_remote_resize = conf_get_int(term->conf, CONF_no_remote_resize); term->no_remote_wintitle = conf_get_int(term->conf, CONF_no_remote_wintitle); term->no_remote_clearscroll = conf_get_int(term->conf, CONF_no_remote_clearscroll); term->rawcnp = conf_get_int(term->conf, CONF_rawcnp); term->rect_select = conf_get_int(term->conf, CONF_rect_select); term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action); term->rxvt_homeend = conf_get_int(term->conf, CONF_rxvt_homeend); term->scroll_on_disp = conf_get_int(term->conf, CONF_scroll_on_disp); term->scroll_on_key = conf_get_int(term->conf, CONF_scroll_on_key); term->xterm_256_colour = conf_get_int(term->conf, CONF_xterm_256_colour); /* * Parse the control-character escapes in the configured * answerback string. */ { char *answerback = conf_get_str(term->conf, CONF_answerback); int maxlen = strlen(answerback); term->answerback = snewn(maxlen, char); term->answerbacklen = 0; while (*answerback) { char *n; char c = ctrlparse(answerback, &n); if (n) { term->answerback[term->answerbacklen++] = c; answerback = n; } else { term->answerback[term->answerbacklen++] = *answerback++; } } } } /* * When the user reconfigures us, we need to check the forbidden- * alternate-screen config option, disable raw mouse mode if the * user has disabled mouse reporting, and abandon a print job if * the user has disabled printing. */ void term_reconfig(Terminal *term, Conf *conf) { /* * Before adopting the new config, check all those terminal * settings which control power-on defaults; and if they've * changed, we will modify the current state as well as the * default one. The full list is: Auto wrap mode, DEC Origin * Mode, BCE, blinking text, character classes. */ int reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass; int i; reset_wrap = (conf_get_int(term->conf, CONF_wrap_mode) != conf_get_int(conf, CONF_wrap_mode)); reset_decom = (conf_get_int(term->conf, CONF_dec_om) != conf_get_int(conf, CONF_dec_om)); reset_bce = (conf_get_int(term->conf, CONF_bce) != conf_get_int(conf, CONF_bce)); reset_tblink = (conf_get_int(term->conf, CONF_blinktext) != conf_get_int(conf, CONF_blinktext)); reset_charclass = 0; for (i = 0; i < 256; i++) if (conf_get_int_int(term->conf, CONF_wordness, i) != conf_get_int_int(conf, CONF_wordness, i)) reset_charclass = 1; /* * If the bidi or shaping settings have changed, flush the bidi * cache completely. */ if (conf_get_int(term->conf, CONF_arabicshaping) != conf_get_int(conf, CONF_arabicshaping) || conf_get_int(term->conf, CONF_bidi) != conf_get_int(conf, CONF_bidi)) { for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); term->pre_bidi_cache[i].width = -1; term->pre_bidi_cache[i].chars = NULL; term->post_bidi_cache[i].width = -1; term->post_bidi_cache[i].chars = NULL; } } conf_free(term->conf); term->conf = conf_copy(conf); if (reset_wrap) term->alt_wrap = term->wrap = conf_get_int(term->conf, CONF_wrap_mode); if (reset_decom) term->alt_om = term->dec_om = conf_get_int(term->conf, CONF_dec_om); if (reset_bce) { term->use_bce = conf_get_int(term->conf, CONF_bce); set_erase_char(term); } if (reset_tblink) { term->blink_is_real = conf_get_int(term->conf, CONF_blinktext); } if (reset_charclass) for (i = 0; i < 256; i++) term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i); if (conf_get_int(term->conf, CONF_no_alt_screen)) swap_screen(term, 0, FALSE, FALSE); if (conf_get_int(term->conf, CONF_no_mouse_rep)) { term->xterm_mouse = 0; set_raw_mouse_mode(term->frontend, 0); } if (conf_get_int(term->conf, CONF_no_remote_charset)) { term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII; term->sco_acs = term->alt_sco_acs = 0; term->utf = 0; } if (!conf_get_str(term->conf, CONF_printer)) { term_print_finish(term); } term_schedule_tblink(term); term_schedule_cblink(term); term_copy_stuff_from_conf(term); } /* * Clear the scrollback. */ void term_clrsb(Terminal *term) { unsigned char *line; int i; /* * Scroll forward to the current screen, if we were back in the * scrollback somewhere until now. */ term->disptop = 0; /* * Clear the actual scrollback. */ while ((line = delpos234(term->scrollback, 0)) != NULL) { sfree(line); /* this is compressed data, not a termline */ } /* * When clearing the scrollback, we also truncate any termlines on * the current screen which have remembered data from a previous * larger window size. Rationale: clearing the scrollback is * sometimes done to protect privacy, so the user intention is * specifically that we should not retain evidence of what * previously happened in the terminal, and that ought to include * evidence to the right as well as evidence above. */ for (i = 0; i < term->rows; i++) check_line_size(term, scrlineptr(i)); /* * There are now no lines of real scrollback which can be pulled * back into the screen by a resize, and no lines of the alternate * screen which should be displayed as if part of the scrollback. */ term->tempsblines = 0; term->alt_sblines = 0; /* * Update the scrollbar to reflect the new state of the world. */ update_sbar(term); } /* * Initialise the terminal. */ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, void *frontend) { Terminal *term; /* * Allocate a new Terminal structure and initialise the fields * that need it. */ term = snew(Terminal); term->frontend = frontend; term->ucsdata = ucsdata; term->conf = conf_copy(myconf); term->logctx = NULL; term->compatibility_level = TM_PUTTY; strcpy(term->id_string, "\033[?6c"); term->cblink_pending = term->tblink_pending = FALSE; term->paste_buffer = NULL; term->paste_len = 0; bufchain_init(&term->inbuf); bufchain_init(&term->printer_buf); term->printing = term->only_printing = FALSE; term->print_job = NULL; term->vt52_mode = FALSE; term->cr_lf_return = FALSE; term->seen_disp_event = FALSE; term->mouse_is_down = FALSE; term->reset_132 = FALSE; term->cblinker = term->tblinker = 0; term->has_focus = 1; term->repeat_off = FALSE; term->termstate = TOPLEVEL; term->selstate = NO_SELECTION; term->curstype = 0; term_copy_stuff_from_conf(term); term->screen = term->alt_screen = term->scrollback = NULL; term->tempsblines = 0; term->alt_sblines = 0; term->disptop = 0; term->disptext = NULL; term->dispcursx = term->dispcursy = -1; term->tabs = NULL; deselect(term); term->rows = term->cols = -1; power_on(term, TRUE); term->beephead = term->beeptail = NULL; #ifdef OPTIMISE_SCROLL term->scrollhead = term->scrolltail = NULL; #endif /* OPTIMISE_SCROLL */ term->nbeeps = 0; term->lastbeep = FALSE; term->beep_overloaded = FALSE; term->attr_mask = 0xffffffff; term->resize_fn = NULL; term->resize_ctx = NULL; term->in_term_out = FALSE; term->ltemp = NULL; term->ltemp_size = 0; term->wcFrom = NULL; term->wcTo = NULL; term->wcFromTo_size = 0; term->window_update_pending = FALSE; term->bidi_cache_size = 0; term->pre_bidi_cache = term->post_bidi_cache = NULL; /* FULL-TERMCHAR */ term->basic_erase_char.chr = CSET_ASCII | ' '; term->basic_erase_char.attr = ATTR_DEFAULT; term->basic_erase_char.cc_next = 0; term->erase_char = term->basic_erase_char; return term; } void term_free(Terminal *term) { termline *line; struct beeptime *beep; int i; while ((line = delpos234(term->scrollback, 0)) != NULL) sfree(line); /* compressed data, not a termline */ freetree234(term->scrollback); while ((line = delpos234(term->screen, 0)) != NULL) freeline(line); freetree234(term->screen); while ((line = delpos234(term->alt_screen, 0)) != NULL) freeline(line); freetree234(term->alt_screen); if (term->disptext) { for (i = 0; i < term->rows; i++) freeline(term->disptext[i]); } sfree(term->disptext); while (term->beephead) { beep = term->beephead; term->beephead = beep->next; sfree(beep); } bufchain_clear(&term->inbuf); if(term->print_job) printer_finish_job(term->print_job); bufchain_clear(&term->printer_buf); sfree(term->paste_buffer); sfree(term->ltemp); sfree(term->wcFrom); sfree(term->wcTo); for (i = 0; i < term->bidi_cache_size; i++) { sfree(term->pre_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].chars); sfree(term->post_bidi_cache[i].forward); sfree(term->post_bidi_cache[i].backward); } sfree(term->pre_bidi_cache); sfree(term->post_bidi_cache); sfree(term->tabs); expire_timer_context(term); conf_free(term->conf); sfree(term); } /* * Set up the terminal for a given size. */ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) { tree234 *newalt; termline **newdisp, *line; int i, j, oldrows = term->rows; int sblen; int save_alt_which = term->alt_which; if (newrows == term->rows && newcols == term->cols && newsavelines == term->savelines) return; /* nothing to do */ /* Behave sensibly if we're given zero (or negative) rows/cols */ if (newrows < 1) newrows = 1; if (newcols < 1) newcols = 1; deselect(term); swap_screen(term, 0, FALSE, FALSE); term->alt_t = term->marg_t = 0; term->alt_b = term->marg_b = newrows - 1; if (term->rows == -1) { term->scrollback = newtree234(NULL); term->screen = newtree234(NULL); term->tempsblines = 0; term->rows = 0; } /* * Resize the screen and scrollback. We only need to shift * lines around within our data structures, because lineptr() * will take care of resizing each individual line if * necessary. So: * * - If the new screen is longer, we shunt lines in from temporary * scrollback if possible, otherwise we add new blank lines at * the bottom. * * - If the new screen is shorter, we remove any blank lines at * the bottom if possible, otherwise shunt lines above the cursor * to scrollback if possible, otherwise delete lines below the * cursor. * * - Then, if the new scrollback length is less than the * amount of scrollback we actually have, we must throw some * away. */ sblen = count234(term->scrollback); /* Do this loop to expand the screen if newrows > rows */ assert(term->rows == count234(term->screen)); while (term->rows < newrows) { if (term->tempsblines > 0) { unsigned char *cline; /* Insert a line from the scrollback at the top of the screen. */ assert(sblen >= term->tempsblines); cline = delpos234(term->scrollback, --sblen); line = decompressline(cline, NULL); sfree(cline); line->temporary = FALSE; /* reconstituted line is now real */ term->tempsblines -= 1; addpos234(term->screen, line, 0); term->curs.y += 1; term->savecurs.y += 1; term->alt_y += 1; term->alt_savecurs.y += 1; } else { /* Add a new blank line at the bottom of the screen. */ line = newline(term, newcols, FALSE); addpos234(term->screen, line, count234(term->screen)); } term->rows += 1; } /* Do this loop to shrink the screen if newrows < rows */ while (term->rows > newrows) { if (term->curs.y < term->rows - 1) { /* delete bottom row, unless it contains the cursor */ line = delpos234(term->screen, term->rows - 1); freeline(line); } else { /* push top row to scrollback */ line = delpos234(term->screen, 0); addpos234(term->scrollback, compressline(line), sblen++); freeline(line); term->tempsblines += 1; term->curs.y -= 1; term->savecurs.y -= 1; term->alt_y -= 1; term->alt_savecurs.y -= 1; } term->rows -= 1; } assert(term->rows == newrows); assert(count234(term->screen) == newrows); /* Delete any excess lines from the scrollback. */ while (sblen > newsavelines) { line = delpos234(term->scrollback, 0); sfree(line); sblen--; } if (sblen < term->tempsblines) term->tempsblines = sblen; assert(count234(term->scrollback) <= newsavelines); assert(count234(term->scrollback) >= term->tempsblines); term->disptop = 0; /* Make a new displayed text buffer. */ newdisp = snewn(newrows, termline *); for (i = 0; i < newrows; i++) { newdisp[i] = newline(term, newcols, FALSE); for (j = 0; j < newcols; j++) newdisp[i]->chars[j].attr = ATTR_INVALID; } if (term->disptext) { for (i = 0; i < oldrows; i++) freeline(term->disptext[i]); } sfree(term->disptext); term->disptext = newdisp; term->dispcursx = term->dispcursy = -1; /* Make a new alternate screen. */ newalt = newtree234(NULL); for (i = 0; i < newrows; i++) { line = newline(term, newcols, TRUE); addpos234(newalt, line, i); } if (term->alt_screen) { while (NULL != (line = delpos234(term->alt_screen, 0))) freeline(line); freetree234(term->alt_screen); } term->alt_screen = newalt; term->alt_sblines = 0; term->tabs = sresize(term->tabs, newcols, unsigned char); { int i; for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++) term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE); } /* Check that the cursor positions are still valid. */ if (term->savecurs.y < 0) term->savecurs.y = 0; if (term->savecurs.y >= newrows) term->savecurs.y = newrows - 1; if (term->savecurs.x >= newcols) term->savecurs.x = newcols - 1; if (term->alt_savecurs.y < 0) term->alt_savecurs.y = 0; if (term->alt_savecurs.y >= newrows) term->alt_savecurs.y = newrows - 1; if (term->alt_savecurs.x >= newcols) term->alt_savecurs.x = newcols - 1; if (term->curs.y < 0) term->curs.y = 0; if (term->curs.y >= newrows) term->curs.y = newrows - 1; if (term->curs.x >= newcols) term->curs.x = newcols - 1; if (term->alt_y < 0) term->alt_y = 0; if (term->alt_y >= newrows) term->alt_y = newrows - 1; if (term->alt_x >= newcols) term->alt_x = newcols - 1; term->alt_x = term->alt_y = 0; term->wrapnext = term->alt_wnext = FALSE; term->rows = newrows; term->cols = newcols; term->savelines = newsavelines; swap_screen(term, save_alt_which, FALSE, FALSE); update_sbar(term); term_update(term); if (term->resize_fn) term->resize_fn(term->resize_ctx, term->cols, term->rows); } /* * Hand a function and context pointer to the terminal which it can * use to notify a back end of resizes. */ void term_provide_resize_fn(Terminal *term, void (*resize_fn)(void *, int, int), void *resize_ctx) { term->resize_fn = resize_fn; term->resize_ctx = resize_ctx; if (resize_fn && term->cols > 0 && term->rows > 0) resize_fn(resize_ctx, term->cols, term->rows); } /* Find the bottom line on the screen that has any content. * If only the top line has content, returns 0. * If no lines have content, return -1. */ static int find_last_nonempty_line(Terminal * term, tree234 * screen) { int i; for (i = count234(screen) - 1; i >= 0; i--) { termline *line = index234(screen, i); int j; for (j = 0; j < line->cols; j++) if (!termchars_equal(&line->chars[j], &term->erase_char)) break; if (j != line->cols) break; } return i; } /* * Swap screens. If `reset' is TRUE and we have been asked to * switch to the alternate screen, we must bring most of its * configuration from the main screen and erase the contents of the * alternate screen completely. (This is even true if we're already * on it! Blame xterm.) */ static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos) { int t; pos tp; tree234 *ttr; if (!which) reset = FALSE; /* do no weird resetting if which==0 */ if (which != term->alt_which) { term->alt_which = which; ttr = term->alt_screen; term->alt_screen = term->screen; term->screen = ttr; term->alt_sblines = find_last_nonempty_line(term, term->alt_screen) + 1; t = term->curs.x; if (!reset && !keep_cur_pos) term->curs.x = term->alt_x; term->alt_x = t; t = term->curs.y; if (!reset && !keep_cur_pos) term->curs.y = term->alt_y; term->alt_y = t; t = term->marg_t; if (!reset) term->marg_t = term->alt_t; term->alt_t = t; t = term->marg_b; if (!reset) term->marg_b = term->alt_b; term->alt_b = t; t = term->dec_om; if (!reset) term->dec_om = term->alt_om; term->alt_om = t; t = term->wrap; if (!reset) term->wrap = term->alt_wrap; term->alt_wrap = t; t = term->wrapnext; if (!reset) term->wrapnext = term->alt_wnext; term->alt_wnext = t; t = term->insert; if (!reset) term->insert = term->alt_ins; term->alt_ins = t; t = term->cset; if (!reset) term->cset = term->alt_cset; term->alt_cset = t; t = term->utf; if (!reset) term->utf = term->alt_utf; term->alt_utf = t; t = term->sco_acs; if (!reset) term->sco_acs = term->alt_sco_acs; term->alt_sco_acs = t; tp = term->savecurs; if (!reset && !keep_cur_pos) term->savecurs = term->alt_savecurs; term->alt_savecurs = tp; t = term->save_cset; if (!reset && !keep_cur_pos) term->save_cset = term->alt_save_cset; term->alt_save_cset = t; t = term->save_csattr; if (!reset && !keep_cur_pos) term->save_csattr = term->alt_save_csattr; term->alt_save_csattr = t; t = term->save_attr; if (!reset && !keep_cur_pos) term->save_attr = term->alt_save_attr; term->alt_save_attr = t; t = term->save_utf; if (!reset && !keep_cur_pos) term->save_utf = term->alt_save_utf; term->alt_save_utf = t; t = term->save_wnext; if (!reset && !keep_cur_pos) term->save_wnext = term->alt_save_wnext; term->alt_save_wnext = t; t = term->save_sco_acs; if (!reset && !keep_cur_pos) term->save_sco_acs = term->alt_save_sco_acs; term->alt_save_sco_acs = t; } if (reset && term->screen) { /* * Yes, this _is_ supposed to honour background-colour-erase. */ erase_lots(term, FALSE, TRUE, TRUE); } } /* * Update the scroll bar. */ static void update_sbar(Terminal *term) { int nscroll = sblines(term); set_sbar(term->frontend, nscroll + term->rows, nscroll + term->disptop, term->rows); } /* * Check whether the region bounded by the two pointers intersects * the scroll region, and de-select the on-screen selection if so. */ static void check_selection(Terminal *term, pos from, pos to) { if (poslt(from, term->selend) && poslt(term->selstart, to)) deselect(term); } /* * Scroll the screen. (`lines' is +ve for scrolling forward, -ve * for backward.) `sb' is TRUE if the scrolling is permitted to * affect the scrollback buffer. */ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) { termline *line; int i, seltop, scrollwinsize; #ifdef OPTIMISE_SCROLL int olddisptop, shift; #endif /* OPTIMISE_SCROLL */ if (topline != 0 || term->alt_which != 0) sb = FALSE; #ifdef OPTIMISE_SCROLL olddisptop = term->disptop; shift = lines; #endif /* OPTIMISE_SCROLL */ scrollwinsize = botline - topline + 1; if (lines < 0) { lines = -lines; if (lines > scrollwinsize) lines = scrollwinsize; while (lines-- > 0) { line = delpos234(term->screen, botline); resizeline(term, line, term->cols); for (i = 0; i < term->cols; i++) copy_termchar(line, i, &term->erase_char); line->lattr = LATTR_NORM; addpos234(term->screen, line, topline); if (term->selstart.y >= topline && term->selstart.y <= botline) { term->selstart.y++; if (term->selstart.y > botline) { term->selstart.y = botline + 1; term->selstart.x = 0; } } if (term->selend.y >= topline && term->selend.y <= botline) { term->selend.y++; if (term->selend.y > botline) { term->selend.y = botline + 1; term->selend.x = 0; } } } } else { if (lines > scrollwinsize) lines = scrollwinsize; while (lines-- > 0) { line = delpos234(term->screen, topline); #ifdef TERM_CC_DIAGS cc_check(line); #endif if (sb && term->savelines > 0) { int sblen = count234(term->scrollback); /* * We must add this line to the scrollback. We'll * remove a line from the top of the scrollback if * the scrollback is full. */ if (sblen == term->savelines) { unsigned char *cline; sblen--; cline = delpos234(term->scrollback, 0); sfree(cline); } else term->tempsblines += 1; addpos234(term->scrollback, compressline(line), sblen); /* now `line' itself can be reused as the bottom line */ /* * If the user is currently looking at part of the * scrollback, and they haven't enabled any options * that are going to reset the scrollback as a * result of this movement, then the chances are * they'd like to keep looking at the same line. So * we move their viewpoint at the same rate as the * scroll, at least until their viewpoint hits the * top end of the scrollback buffer, at which point * we don't have the choice any more. * * Thanks to Jan Holmen Holsten for the idea and * initial implementation. */ if (term->disptop > -term->savelines && term->disptop < 0) term->disptop--; } resizeline(term, line, term->cols); for (i = 0; i < term->cols; i++) copy_termchar(line, i, &term->erase_char); line->lattr = LATTR_NORM; addpos234(term->screen, line, botline); /* * If the selection endpoints move into the scrollback, * we keep them moving until they hit the top. However, * of course, if the line _hasn't_ moved into the * scrollback then we don't do this, and cut them off * at the top of the scroll region. * * This applies to selstart and selend (for an existing * selection), and also selanchor (for one being * selected as we speak). */ seltop = sb ? -term->savelines : topline; if (term->selstate != NO_SELECTION) { if (term->selstart.y >= seltop && term->selstart.y <= botline) { term->selstart.y--; if (term->selstart.y < seltop) { term->selstart.y = seltop; term->selstart.x = 0; } } if (term->selend.y >= seltop && term->selend.y <= botline) { term->selend.y--; if (term->selend.y < seltop) { term->selend.y = seltop; term->selend.x = 0; } } if (term->selanchor.y >= seltop && term->selanchor.y <= botline) { term->selanchor.y--; if (term->selanchor.y < seltop) { term->selanchor.y = seltop; term->selanchor.x = 0; } } } } } #ifdef OPTIMISE_SCROLL shift += term->disptop - olddisptop; if (shift < term->rows && shift > -term->rows && shift != 0) scroll_display(term, topline, botline, shift); #endif /* OPTIMISE_SCROLL */ } #ifdef OPTIMISE_SCROLL /* * Add a scroll of a region on the screen into the pending scroll list. * `lines' is +ve for scrolling forward, -ve for backward. * * If the scroll is on the same area as the last scroll in the list, * merge them. */ static void save_scroll(Terminal *term, int topline, int botline, int lines) { struct scrollregion *newscroll; if (term->scrolltail && term->scrolltail->topline == topline && term->scrolltail->botline == botline) { term->scrolltail->lines += lines; } else { newscroll = snew(struct scrollregion); newscroll->topline = topline; newscroll->botline = botline; newscroll->lines = lines; newscroll->next = NULL; if (!term->scrollhead) term->scrollhead = newscroll; else term->scrolltail->next = newscroll; term->scrolltail = newscroll; } } /* * Scroll the physical display, and our conception of it in disptext. */ static void scroll_display(Terminal *term, int topline, int botline, int lines) { int distance, nlines, i, j; distance = lines > 0 ? lines : -lines; nlines = botline - topline + 1 - distance; if (lines > 0) { for (i = 0; i < nlines; i++) for (j = 0; j < term->cols; j++) copy_termchar(term->disptext[i], j, term->disptext[i+distance]->chars+j); if (term->dispcursy >= 0 && term->dispcursy >= topline + distance && term->dispcursy < topline + distance + nlines) term->dispcursy -= distance; for (i = 0; i < distance; i++) for (j = 0; j < term->cols; j++) term->disptext[nlines+i]->chars[j].attr |= ATTR_INVALID; } else { for (i = nlines; i-- ;) for (j = 0; j < term->cols; j++) copy_termchar(term->disptext[i+distance], j, term->disptext[i]->chars+j); if (term->dispcursy >= 0 && term->dispcursy >= topline && term->dispcursy < topline + nlines) term->dispcursy += distance; for (i = 0; i < distance; i++) for (j = 0; j < term->cols; j++) term->disptext[i]->chars[j].attr |= ATTR_INVALID; } save_scroll(term, topline, botline, lines); } #endif /* OPTIMISE_SCROLL */ /* * Move the cursor to a given position, clipping at boundaries. We * may or may not want to clip at the scroll margin: marg_clip is 0 * not to, 1 to disallow _passing_ the margins, and 2 to disallow * even _being_ outside the margins. */ static void move(Terminal *term, int x, int y, int marg_clip) { if (x < 0) x = 0; if (x >= term->cols) x = term->cols - 1; if (marg_clip) { if ((term->curs.y >= term->marg_t || marg_clip == 2) && y < term->marg_t) y = term->marg_t; if ((term->curs.y <= term->marg_b || marg_clip == 2) && y > term->marg_b) y = term->marg_b; } if (y < 0) y = 0; if (y >= term->rows) y = term->rows - 1; term->curs.x = x; term->curs.y = y; term->wrapnext = FALSE; } /* * Save or restore the cursor and SGR mode. */ static void save_cursor(Terminal *term, int save) { if (save) { term->savecurs = term->curs; term->save_attr = term->curr_attr; term->save_cset = term->cset; term->save_utf = term->utf; term->save_wnext = term->wrapnext; term->save_csattr = term->cset_attr[term->cset]; term->save_sco_acs = term->sco_acs; } else { term->curs = term->savecurs; /* Make sure the window hasn't shrunk since the save */ if (term->curs.x >= term->cols) term->curs.x = term->cols - 1; if (term->curs.y >= term->rows) term->curs.y = term->rows - 1; term->curr_attr = term->save_attr; term->cset = term->save_cset; term->utf = term->save_utf; term->wrapnext = term->save_wnext; /* * wrapnext might reset to False if the x position is no * longer at the rightmost edge. */ if (term->wrapnext && term->curs.x < term->cols-1) term->wrapnext = FALSE; term->cset_attr[term->cset] = term->save_csattr; term->sco_acs = term->save_sco_acs; set_erase_char(term); } } /* * This function is called before doing _anything_ which affects * only part of a line of text. It is used to mark the boundary * between two character positions, and it indicates that some sort * of effect is going to happen on only one side of that boundary. * * The effect of this function is to check whether a CJK * double-width character is straddling the boundary, and to remove * it and replace it with two spaces if so. (Of course, one or * other of those spaces is then likely to be replaced with * something else again, as a result of whatever happens next.) * * Also, if the boundary is at the right-hand _edge_ of the screen, * it implies something deliberate is being done to the rightmost * column position; hence we must clear LATTR_WRAPPED2. * * The input to the function is the coordinates of the _second_ * character of the pair. */ static void check_boundary(Terminal *term, int x, int y) { termline *ldata; /* Validate input coordinates, just in case. */ if (x <= 0 || x > term->cols) return; ldata = scrlineptr(y); check_line_size(term, ldata); if (x == term->cols) { ldata->lattr &= ~LATTR_WRAPPED2; } else { if (ldata->chars[x].chr == UCSWIDE) { clear_cc(ldata, x-1); clear_cc(ldata, x); ldata->chars[x-1].chr = ' ' | CSET_ASCII; ldata->chars[x] = ldata->chars[x-1]; } } } /* * Erase a large portion of the screen: the whole screen, or the * whole line, or parts thereof. */ static void erase_lots(Terminal *term, int line_only, int from_begin, int to_end) { pos start, end; int erase_lattr; int erasing_lines_from_top = 0; if (line_only) { start.y = term->curs.y; start.x = 0; end.y = term->curs.y + 1; end.x = 0; erase_lattr = FALSE; } else { start.y = 0; start.x = 0; end.y = term->rows; end.x = 0; erase_lattr = TRUE; } if (!from_begin) { start = term->curs; } if (!to_end) { end = term->curs; incpos(end); } if (!from_begin || !to_end) check_boundary(term, term->curs.x, term->curs.y); check_selection(term, start, end); /* Clear screen also forces a full window redraw, just in case. */ if (start.y == 0 && start.x == 0 && end.y == term->rows) term_invalidate(term); /* Lines scrolled away shouldn't be brought back on if the terminal * resizes. */ if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr) erasing_lines_from_top = 1; if (term->erase_to_scrollback && erasing_lines_from_top) { /* If it's a whole number of lines, starting at the top, and * we're fully erasing them, erase by scrolling and keep the * lines in the scrollback. */ int scrolllines = end.y; if (end.y == term->rows) { /* Shrink until we find a non-empty row.*/ scrolllines = find_last_nonempty_line(term, term->screen) + 1; } if (scrolllines > 0) scroll(term, 0, scrolllines - 1, scrolllines, TRUE); } else { termline *ldata = scrlineptr(start.y); while (poslt(start, end)) { check_line_size(term, ldata); if (start.x == term->cols) { if (!erase_lattr) ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2); else ldata->lattr = LATTR_NORM; } else { copy_termchar(ldata, start.x, &term->erase_char); } if (incpos(start) && start.y < term->rows) { ldata = scrlineptr(start.y); } } } /* After an erase of lines from the top of the screen, we shouldn't * bring the lines back again if the terminal enlarges (since the user or * application has explicitly thrown them away). */ if (erasing_lines_from_top && !(term->alt_which)) term->tempsblines = 0; } /* * Insert or delete characters within the current line. n is +ve if * insertion is desired, and -ve for deletion. */ static void insch(Terminal *term, int n) { int dir = (n < 0 ? -1 : +1); int m, j; pos eol; termline *ldata; n = (n < 0 ? -n : n); if (n > term->cols - term->curs.x) n = term->cols - term->curs.x; m = term->cols - term->curs.x - n; /* * We must de-highlight the selection if it overlaps any part of * the region affected by this operation, i.e. the region from the * current cursor position to end-of-line, _unless_ the entirety * of the selection is going to be moved to the left or right by * this operation but otherwise unchanged, in which case we can * simply move the highlight with the text. */ eol.y = term->curs.y; eol.x = term->cols; if (poslt(term->curs, term->selend) && poslt(term->selstart, eol)) { pos okstart = term->curs; pos okend = eol; if (dir > 0) { /* Insertion: n characters at EOL will be splatted. */ okend.x -= n; } else { /* Deletion: n characters at cursor position will be splatted. */ okstart.x += n; } if (posle(okstart, term->selstart) && posle(term->selend, okend)) { /* Selection is contained entirely in the interval * [okstart,okend), so we need only adjust the selection * bounds. */ term->selstart.x += dir * n; term->selend.x += dir * n; assert(term->selstart.x >= term->curs.x); assert(term->selstart.x < term->cols); assert(term->selend.x > term->curs.x); assert(term->selend.x <= term->cols); } else { /* Selection is not wholly contained in that interval, so * we must unhighlight it. */ deselect(term); } } check_boundary(term, term->curs.x, term->curs.y); if (dir < 0) check_boundary(term, term->curs.x + n, term->curs.y); ldata = scrlineptr(term->curs.y); if (dir < 0) { for (j = 0; j < m; j++) move_termchar(ldata, ldata->chars + term->curs.x + j, ldata->chars + term->curs.x + j + n); while (n--) copy_termchar(ldata, term->curs.x + m++, &term->erase_char); } else { for (j = m; j-- ;) move_termchar(ldata, ldata->chars + term->curs.x + j + n, ldata->chars + term->curs.x + j); while (n--) copy_termchar(ldata, term->curs.x + n, &term->erase_char); } } /* * Toggle terminal mode `mode' to state `state'. (`query' indicates * whether the mode is a DEC private one or a normal one.) */ static void toggle_mode(Terminal *term, int mode, int query, int state) { if (query) switch (mode) { case 1: /* DECCKM: application cursor keys */ term->app_cursor_keys = state; break; case 2: /* DECANM: VT52 mode */ term->vt52_mode = !state; if (term->vt52_mode) { term->blink_is_real = FALSE; term->vt52_bold = FALSE; } else { term->blink_is_real = term->blinktext; } term_schedule_tblink(term); break; case 3: /* DECCOLM: 80/132 columns */ deselect(term); if (!term->no_remote_resize) request_resize(term->frontend, state ? 132 : 80, term->rows); term->reset_132 = state; term->alt_t = term->marg_t = 0; term->alt_b = term->marg_b = term->rows - 1; move(term, 0, 0, 0); erase_lots(term, FALSE, TRUE, TRUE); break; case 5: /* DECSCNM: reverse video */ /* * Toggle reverse video. If we receive an OFF within the * visual bell timeout period after an ON, we trigger an * effective visual bell, so that ESC[?5hESC[?5l will * always be an actually _visible_ visual bell. */ if (term->rvideo && !state) { /* This is an OFF, so set up a vbell */ term_schedule_vbell(term, TRUE, term->rvbell_startpoint); } else if (!term->rvideo && state) { /* This is an ON, so we notice the time and save it. */ term->rvbell_startpoint = GETTICKCOUNT(); } term->rvideo = state; seen_disp_event(term); break; case 6: /* DECOM: DEC origin mode */ term->dec_om = state; break; case 7: /* DECAWM: auto wrap */ term->wrap = state; break; case 8: /* DECARM: auto key repeat */ term->repeat_off = !state; break; case 10: /* DECEDM: set local edit mode */ term->term_editing = state; if (term->ldisc) /* cause ldisc to notice changes */ ldisc_echoedit_update(term->ldisc); break; case 25: /* DECTCEM: enable/disable cursor */ compatibility2(OTHER, VT220); term->cursor_on = state; seen_disp_event(term); break; case 47: /* alternate screen */ compatibility(OTHER); deselect(term); swap_screen(term, term->no_alt_screen ? 0 : state, FALSE, FALSE); if (term->scroll_on_disp) term->disptop = 0; break; case 1000: /* xterm mouse 1 (normal) */ term->xterm_mouse = state ? 1 : 0; set_raw_mouse_mode(term->frontend, state); break; case 1002: /* xterm mouse 2 (inc. button drags) */ term->xterm_mouse = state ? 2 : 0; set_raw_mouse_mode(term->frontend, state); break; case 1006: /* xterm extended mouse */ term->xterm_extended_mouse = state ? 1 : 0; break; case 1015: /* urxvt extended mouse */ term->urxvt_extended_mouse = state ? 1 : 0; break; case 1047: /* alternate screen */ compatibility(OTHER); deselect(term); swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, TRUE); if (term->scroll_on_disp) term->disptop = 0; break; case 1048: /* save/restore cursor */ if (!term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); break; case 1049: /* cursor & alternate screen */ if (state && !term->no_alt_screen) save_cursor(term, state); if (!state) seen_disp_event(term); compatibility(OTHER); deselect(term); swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, FALSE); if (!state && !term->no_alt_screen) save_cursor(term, state); if (term->scroll_on_disp) term->disptop = 0; break; case 2004: /* xterm bracketed paste */ term->bracketed_paste = state ? TRUE : FALSE; break; } else switch (mode) { case 4: /* IRM: set insert mode */ compatibility(VT102); term->insert = state; break; case 12: /* SRM: set echo mode */ term->term_echoing = !state; if (term->ldisc) /* cause ldisc to notice changes */ ldisc_echoedit_update(term->ldisc); break; case 20: /* LNM: Return sends ... */ term->cr_lf_return = state; break; case 34: /* WYULCURM: Make cursor BIG */ compatibility2(OTHER, VT220); term->big_cursor = !state; } } /* * Process an OSC sequence: set window title or icon name. */ static void do_osc(Terminal *term) { if (term->osc_w) { while (term->osc_strlen--) term->wordness[(unsigned char) term->osc_string[term->osc_strlen]] = term->esc_args[0]; } else { term->osc_string[term->osc_strlen] = '\0'; switch (term->esc_args[0]) { case 0: case 1: if (!term->no_remote_wintitle) set_icon(term->frontend, term->osc_string); if (term->esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: if (!term->no_remote_wintitle) set_title(term->frontend, term->osc_string); break; } } } /* * ANSI printing routines. */ static void term_print_setup(Terminal *term, char *printer) { bufchain_clear(&term->printer_buf); term->print_job = printer_start_job(printer); } static void term_print_flush(Terminal *term) { void *data; int len; int size; while ((size = bufchain_size(&term->printer_buf)) > 5) { bufchain_prefix(&term->printer_buf, &data, &len); if (len > size-5) len = size-5; printer_job_data(term->print_job, data, len); bufchain_consume(&term->printer_buf, len); } } static void term_print_finish(Terminal *term) { void *data; int len, size; char c; if (!term->printing && !term->only_printing) return; /* we need do nothing */ term_print_flush(term); while ((size = bufchain_size(&term->printer_buf)) > 0) { bufchain_prefix(&term->printer_buf, &data, &len); c = *(char *)data; if (c == '\033' || c == '\233') { bufchain_consume(&term->printer_buf, size); break; } else { printer_job_data(term->print_job, &c, 1); bufchain_consume(&term->printer_buf, 1); } } printer_finish_job(term->print_job); term->print_job = NULL; term->printing = term->only_printing = FALSE; } /* * Remove everything currently in `inbuf' and stick it up on the * in-memory display. There's a big state machine in here to * process escape sequences... */ static void term_out(Terminal *term) { unsigned long c; int unget; unsigned char localbuf[256], *chars; int nchars = 0; unget = -1; chars = NULL; /* placate compiler warnings */ while (nchars > 0 || unget != -1 || bufchain_size(&term->inbuf) > 0) { if (unget == -1) { if (nchars == 0) { void *ret; bufchain_prefix(&term->inbuf, &ret, &nchars); if (nchars > sizeof(localbuf)) nchars = sizeof(localbuf); memcpy(localbuf, ret, nchars); bufchain_consume(&term->inbuf, nchars); chars = localbuf; assert(chars != NULL); } c = *chars++; nchars--; /* * Optionally log the session traffic to a file. Useful for * debugging and possibly also useful for actual logging. */ if (term->logtype == LGTYP_DEBUG && term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG); } else { c = unget; unget = -1; } /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even * be able to display 8-bit characters, but I'll let that go 'cause * of i18n. */ /* * If we're printing, add the character to the printer * buffer. */ if (term->printing) { bufchain_add(&term->printer_buf, &c, 1); /* * If we're in print-only mode, we use a much simpler * state machine designed only to recognise the ESC[4i * termination sequence. */ if (term->only_printing) { if (c == '\033') term->print_state = 1; else if (c == (unsigned char)'\233') term->print_state = 2; else if (c == '[' && term->print_state == 1) term->print_state = 2; else if (c == '4' && term->print_state == 2) term->print_state = 3; else if (c == 'i' && term->print_state == 3) term->print_state = 4; else term->print_state = 0; if (term->print_state == 4) { term_print_finish(term); } continue; } } /* First see about all those translations. */ if (term->termstate == TOPLEVEL) { if (in_utf(term)) switch (term->utf_state) { case 0: if (c < 0x80) { /* UTF-8 must be stateless so we ignore iso2022. */ if (term->ucsdata->unitab_ctrl[c] != 0xFF) c = term->ucsdata->unitab_ctrl[c]; else c = ((unsigned char)c) | CSET_ASCII; break; } else if ((c & 0xe0) == 0xc0) { term->utf_size = term->utf_state = 1; term->utf_char = (c & 0x1f); } else if ((c & 0xf0) == 0xe0) { term->utf_size = term->utf_state = 2; term->utf_char = (c & 0x0f); } else if ((c & 0xf8) == 0xf0) { term->utf_size = term->utf_state = 3; term->utf_char = (c & 0x07); } else if ((c & 0xfc) == 0xf8) { term->utf_size = term->utf_state = 4; term->utf_char = (c & 0x03); } else if ((c & 0xfe) == 0xfc) { term->utf_size = term->utf_state = 5; term->utf_char = (c & 0x01); } else { c = UCSERR; break; } continue; case 1: case 2: case 3: case 4: case 5: if ((c & 0xC0) != 0x80) { unget = c; c = UCSERR; term->utf_state = 0; break; } term->utf_char = (term->utf_char << 6) | (c & 0x3f); if (--term->utf_state) continue; c = term->utf_char; /* Is somebody trying to be evil! */ if (c < 0x80 || (c < 0x800 && term->utf_size >= 2) || (c < 0x10000 && term->utf_size >= 3) || (c < 0x200000 && term->utf_size >= 4) || (c < 0x4000000 && term->utf_size >= 5)) c = UCSERR; /* Unicode line separator and paragraph separator are CR-LF */ if (c == 0x2028 || c == 0x2029) c = 0x85; /* High controls are probably a Baaad idea too. */ if (c < 0xA0) c = 0xFFFD; /* The UTF-16 surrogates are not nice either. */ /* The standard give the option of decoding these: * I don't want to! */ if (c >= 0xD800 && c < 0xE000) c = UCSERR; /* ISO 10646 characters now limited to UTF-16 range. */ if (c > 0x10FFFF) c = UCSERR; /* This is currently a TagPhobic application.. */ if (c >= 0xE0000 && c <= 0xE007F) continue; /* U+FEFF is best seen as a null. */ if (c == 0xFEFF) continue; /* But U+FFFE is an error. */ if (c == 0xFFFE || c == 0xFFFF) c = UCSERR; break; } /* Are we in the nasty ACS mode? Note: no sco in utf mode. */ else if(term->sco_acs && (c!='\033' && c!='\012' && c!='\015' && c!='\b')) { if (term->sco_acs == 2) c |= 0x80; c |= CSET_SCOACS; } else { switch (term->cset_attr[term->cset]) { /* * Linedraw characters are different from 'ESC ( B' * only for a small range. For ones outside that * range, make sure we use the same font as well as * the same encoding. */ case CSET_LINEDRW: if (term->ucsdata->unitab_ctrl[c] != 0xFF) c = term->ucsdata->unitab_ctrl[c]; else c = ((unsigned char) c) | CSET_LINEDRW; break; case CSET_GBCHR: /* If UK-ASCII, make the '#' a LineDraw Pound */ if (c == '#') { c = '}' | CSET_LINEDRW; break; } /*FALLTHROUGH*/ case CSET_ASCII: if (term->ucsdata->unitab_ctrl[c] != 0xFF) c = term->ucsdata->unitab_ctrl[c]; else c = ((unsigned char) c) | CSET_ASCII; break; case CSET_SCOACS: if (c>=' ') c = ((unsigned char)c) | CSET_SCOACS; break; } } } /* * How about C1 controls? * Explicitly ignore SCI (0x9a), which we don't translate to DECID. */ if ((c & -32) == 0x80 && term->termstate < DO_CTRLS && !term->vt52_mode && has_compat(VT220)) { if (c == 0x9a) c = 0; else { term->termstate = SEEN_ESC; term->esc_query = FALSE; c = '@' + (c & 0x1F); } } /* Or the GL control. */ if (c == '\177' && term->termstate < DO_CTRLS && has_compat(OTHER)) { if (term->curs.x && !term->wrapnext) term->curs.x--; term->wrapnext = FALSE; /* destructive backspace might be disabled */ if (!term->no_dbackspace) { check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+1, term->curs.y); copy_termchar(scrlineptr(term->curs.y), term->curs.x, &term->erase_char); } } else /* Or normal C0 controls. */ if ((c & ~0x1F) == 0 && term->termstate < DO_CTRLS) { switch (c) { case '\005': /* ENQ: terminal type query */ /* * Strictly speaking this is VT100 but a VT100 defaults to * no response. Other terminals respond at their option. * * Don't put a CR in the default string as this tends to * upset some weird software. */ compatibility(ANSIMIN); if (term->ldisc) { lpage_send(term->ldisc, DEFAULT_CODEPAGE, term->answerback, term->answerbacklen, 0); } break; case '\007': /* BEL: Bell */ { struct beeptime *newbeep; unsigned long ticks; ticks = GETTICKCOUNT(); if (!term->beep_overloaded) { newbeep = snew(struct beeptime); newbeep->ticks = ticks; newbeep->next = NULL; if (!term->beephead) term->beephead = newbeep; else term->beeptail->next = newbeep; term->beeptail = newbeep; term->nbeeps++; } /* * Throw out any beeps that happened more than * t seconds ago. */ while (term->beephead && term->beephead->ticks < ticks - term->bellovl_t) { struct beeptime *tmp = term->beephead; term->beephead = tmp->next; sfree(tmp); if (!term->beephead) term->beeptail = NULL; term->nbeeps--; } if (term->bellovl && term->beep_overloaded && ticks - term->lastbeep >= (unsigned)term->bellovl_s) { /* * If we're currently overloaded and the * last beep was more than s seconds ago, * leave overload mode. */ term->beep_overloaded = FALSE; } else if (term->bellovl && !term->beep_overloaded && term->nbeeps >= term->bellovl_n) { /* * Now, if we have n or more beeps * remaining in the queue, go into overload * mode. */ term->beep_overloaded = TRUE; } term->lastbeep = ticks; /* * Perform an actual beep if we're not overloaded. */ if (!term->bellovl || !term->beep_overloaded) { do_beep(term->frontend, term->beep); if (term->beep == BELL_VISUAL) { term_schedule_vbell(term, FALSE, 0); } } seen_disp_event(term); } break; case '\b': /* BS: Back space */ if (term->curs.x == 0 && (term->curs.y == 0 || term->wrap == 0)) /* do nothing */ ; else if (term->curs.x == 0 && term->curs.y > 0) term->curs.x = term->cols - 1, term->curs.y--; else if (term->wrapnext) term->wrapnext = FALSE; else term->curs.x--; seen_disp_event(term); break; case '\016': /* LS1: Locking-shift one */ compatibility(VT100); term->cset = 1; break; case '\017': /* LS0: Locking-shift zero */ compatibility(VT100); term->cset = 0; break; case '\033': /* ESC: Escape */ if (term->vt52_mode) term->termstate = VT52_ESC; else { compatibility(ANSIMIN); term->termstate = SEEN_ESC; term->esc_query = FALSE; } break; case '\015': /* CR: Carriage return */ term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); if (term->crhaslf) { if (term->curs.y == term->marg_b) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; } if (term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); break; case '\014': /* FF: Form feed */ if (has_compat(SCOANSI)) { move(term, 0, 0, 0); erase_lots(term, FALSE, FALSE, TRUE); if (term->scroll_on_disp) term->disptop = 0; term->wrapnext = FALSE; seen_disp_event(term); break; } case '\013': /* VT: Line tabulation */ compatibility(VT100); case '\012': /* LF: Line feed */ if (term->curs.y == term->marg_b) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; if (term->lfhascr) term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); if (term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); break; case '\t': /* HT: Character tabulation */ { pos old_curs = term->curs; termline *ldata = scrlineptr(term->curs.y); do { term->curs.x++; } while (term->curs.x < term->cols - 1 && !term->tabs[term->curs.x]); if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) { if (term->curs.x >= term->cols / 2) term->curs.x = term->cols / 2 - 1; } else { if (term->curs.x >= term->cols) term->curs.x = term->cols - 1; } check_selection(term, old_curs, term->curs); } seen_disp_event(term); break; } } else switch (term->termstate) { case TOPLEVEL: /* Only graphic characters get this far; * ctrls are stripped above */ { termline *cline = scrlineptr(term->curs.y); int width = 0; if (DIRECT_CHAR(c)) width = 1; if (!width) width = (term->cjk_ambig_wide ? mk_wcwidth_cjk((unsigned int) c) : mk_wcwidth((unsigned int) c)); if (term->wrapnext && term->wrap && width > 0) { cline->lattr |= LATTR_WRAPPED; if (term->curs.y == term->marg_b) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; term->curs.x = 0; term->wrapnext = FALSE; cline = scrlineptr(term->curs.y); } if (term->insert && width > 0) insch(term, width); if (term->selstate != NO_SELECTION) { pos cursplus = term->curs; incpos(cursplus); check_selection(term, term->curs, cursplus); } if (((c & CSET_MASK) == CSET_ASCII || (c & CSET_MASK) == 0) && term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); switch (width) { case 2: /* * If we're about to display a double-width * character starting in the rightmost * column, then we do something special * instead. We must print a space in the * last column of the screen, then wrap; * and we also set LATTR_WRAPPED2 which * instructs subsequent cut-and-pasting not * only to splice this line to the one * after it, but to ignore the space in the * last character position as well. * (Because what was actually output to the * terminal was presumably just a sequence * of CJK characters, and we don't want a * space to be pasted in the middle of * those just because they had the * misfortune to start in the wrong parity * column. xterm concurs.) */ check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+2, term->curs.y); if (term->curs.x == term->cols-1) { copy_termchar(cline, term->curs.x, &term->erase_char); cline->lattr |= LATTR_WRAPPED | LATTR_WRAPPED2; if (term->curs.y == term->marg_b) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; term->curs.x = 0; cline = scrlineptr(term->curs.y); /* Now we must check_boundary again, of course. */ check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+2, term->curs.y); } /* FULL-TERMCHAR */ clear_cc(cline, term->curs.x); cline->chars[term->curs.x].chr = c; cline->chars[term->curs.x].attr = term->curr_attr; term->curs.x++; /* FULL-TERMCHAR */ clear_cc(cline, term->curs.x); cline->chars[term->curs.x].chr = UCSWIDE; cline->chars[term->curs.x].attr = term->curr_attr; break; case 1: check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+1, term->curs.y); /* FULL-TERMCHAR */ clear_cc(cline, term->curs.x); cline->chars[term->curs.x].chr = c; cline->chars[term->curs.x].attr = term->curr_attr; break; case 0: if (term->curs.x > 0) { int x = term->curs.x - 1; /* If we're in wrapnext state, the character * to combine with is _here_, not to our left. */ if (term->wrapnext) x++; /* * If the previous character is * UCSWIDE, back up another one. */ if (cline->chars[x].chr == UCSWIDE) { assert(x > 0); x--; } add_cc(cline, x, c); seen_disp_event(term); } continue; default: continue; } term->curs.x++; if (term->curs.x == term->cols) { term->curs.x--; term->wrapnext = TRUE; if (term->wrap && term->vt52_mode) { cline->lattr |= LATTR_WRAPPED; if (term->curs.y == term->marg_b) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; term->curs.x = 0; term->wrapnext = FALSE; } } seen_disp_event(term); } break; case OSC_MAYBE_ST: /* * This state is virtually identical to SEEN_ESC, with the * exception that we have an OSC sequence in the pipeline, * and _if_ we see a backslash, we process it. */ if (c == '\\') { do_osc(term); term->termstate = TOPLEVEL; break; } /* else fall through */ case SEEN_ESC: if (c >= ' ' && c <= '/') { if (term->esc_query) term->esc_query = -1; else term->esc_query = c; break; } term->termstate = TOPLEVEL; switch (ANSI(c, term->esc_query)) { case '[': /* enter CSI mode */ term->termstate = SEEN_CSI; term->esc_nargs = 1; term->esc_args[0] = ARG_DEFAULT; term->esc_query = FALSE; break; case ']': /* OSC: xterm escape sequences */ /* Compatibility is nasty here, xterm, linux, decterm yuk! */ compatibility(OTHER); term->termstate = SEEN_OSC; term->esc_args[0] = 0; break; case '7': /* DECSC: save cursor */ compatibility(VT100); save_cursor(term, TRUE); break; case '8': /* DECRC: restore cursor */ compatibility(VT100); save_cursor(term, FALSE); seen_disp_event(term); break; case '=': /* DECKPAM: Keypad application mode */ compatibility(VT100); term->app_keypad_keys = TRUE; break; case '>': /* DECKPNM: Keypad numeric mode */ compatibility(VT100); term->app_keypad_keys = FALSE; break; case 'D': /* IND: exactly equivalent to LF */ compatibility(VT100); if (term->curs.y == term->marg_b) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; term->wrapnext = FALSE; seen_disp_event(term); break; case 'E': /* NEL: exactly equivalent to CR-LF */ compatibility(VT100); term->curs.x = 0; if (term->curs.y == term->marg_b) scroll(term, term->marg_t, term->marg_b, 1, TRUE); else if (term->curs.y < term->rows - 1) term->curs.y++; term->wrapnext = FALSE; seen_disp_event(term); break; case 'M': /* RI: reverse index - backwards LF */ compatibility(VT100); if (term->curs.y == term->marg_t) scroll(term, term->marg_t, term->marg_b, -1, TRUE); else if (term->curs.y > 0) term->curs.y--; term->wrapnext = FALSE; seen_disp_event(term); break; case 'Z': /* DECID: terminal type query */ compatibility(VT100); if (term->ldisc) ldisc_send(term->ldisc, term->id_string, strlen(term->id_string), 0); break; case 'c': /* RIS: restore power-on settings */ compatibility(VT100); power_on(term, TRUE); if (term->ldisc) /* cause ldisc to notice changes */ ldisc_echoedit_update(term->ldisc); if (term->reset_132) { if (!term->no_remote_resize) request_resize(term->frontend, 80, term->rows); term->reset_132 = 0; } if (term->scroll_on_disp) term->disptop = 0; seen_disp_event(term); break; case 'H': /* HTS: set a tab */ compatibility(VT100); term->tabs[term->curs.x] = TRUE; break; case ANSI('8', '#'): /* DECALN: fills screen with Es :-) */ compatibility(VT100); { termline *ldata; int i, j; pos scrtop, scrbot; for (i = 0; i < term->rows; i++) { ldata = scrlineptr(i); check_line_size(term, ldata); for (j = 0; j < term->cols; j++) { copy_termchar(ldata, j, &term->basic_erase_char); ldata->chars[j].chr = 'E'; } ldata->lattr = LATTR_NORM; } if (term->scroll_on_disp) term->disptop = 0; seen_disp_event(term); scrtop.x = scrtop.y = 0; scrbot.x = 0; scrbot.y = term->rows; check_selection(term, scrtop, scrbot); } break; case ANSI('3', '#'): case ANSI('4', '#'): case ANSI('5', '#'): case ANSI('6', '#'): compatibility(VT100); { int nlattr; termline *ldata; switch (ANSI(c, term->esc_query)) { case ANSI('3', '#'): /* DECDHL: 2*height, top */ nlattr = LATTR_TOP; break; case ANSI('4', '#'): /* DECDHL: 2*height, bottom */ nlattr = LATTR_BOT; break; case ANSI('5', '#'): /* DECSWL: normal */ nlattr = LATTR_NORM; break; default: /* case ANSI('6', '#'): DECDWL: 2*width */ nlattr = LATTR_WIDE; break; } ldata = scrlineptr(term->curs.y); check_line_size(term, ldata); ldata->lattr = nlattr; } break; /* GZD4: G0 designate 94-set */ case ANSI('A', '('): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[0] = CSET_GBCHR; break; case ANSI('B', '('): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[0] = CSET_ASCII; break; case ANSI('0', '('): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[0] = CSET_LINEDRW; break; case ANSI('U', '('): compatibility(OTHER); if (!term->no_remote_charset) term->cset_attr[0] = CSET_SCOACS; break; /* G1D4: G1-designate 94-set */ case ANSI('A', ')'): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[1] = CSET_GBCHR; break; case ANSI('B', ')'): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[1] = CSET_ASCII; break; case ANSI('0', ')'): compatibility(VT100); if (!term->no_remote_charset) term->cset_attr[1] = CSET_LINEDRW; break; case ANSI('U', ')'): compatibility(OTHER); if (!term->no_remote_charset) term->cset_attr[1] = CSET_SCOACS; break; /* DOCS: Designate other coding system */ case ANSI('8', '%'): /* Old Linux code */ case ANSI('G', '%'): compatibility(OTHER); if (!term->no_remote_charset) term->utf = 1; break; case ANSI('@', '%'): compatibility(OTHER); if (!term->no_remote_charset) term->utf = 0; break; } break; case SEEN_CSI: term->termstate = TOPLEVEL; /* default */ if (isdigit(c)) { if (term->esc_nargs <= ARGS_MAX) { if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT) term->esc_args[term->esc_nargs - 1] = 0; if (term->esc_args[term->esc_nargs - 1] <= UINT_MAX / 10 && term->esc_args[term->esc_nargs - 1] * 10 <= UINT_MAX - c - '0') term->esc_args[term->esc_nargs - 1] = 10 * term->esc_args[term->esc_nargs - 1] + c - '0'; else term->esc_args[term->esc_nargs - 1] = UINT_MAX; } term->termstate = SEEN_CSI; } else if (c == ';') { if (term->esc_nargs < ARGS_MAX) term->esc_args[term->esc_nargs++] = ARG_DEFAULT; term->termstate = SEEN_CSI; } else if (c < '@') { if (term->esc_query) term->esc_query = -1; else if (c == '?') term->esc_query = TRUE; else term->esc_query = c; term->termstate = SEEN_CSI; } else #define CLAMP(arg, lim) ((arg) = ((arg) > (lim)) ? (lim) : (arg)) switch (ANSI(c, term->esc_query)) { case 'A': /* CUU: move up N lines */ CLAMP(term->esc_args[0], term->rows); move(term, term->curs.x, term->curs.y - def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case 'e': /* VPR: move down N lines */ compatibility(ANSI); /* FALLTHROUGH */ case 'B': /* CUD: Cursor down */ CLAMP(term->esc_args[0], term->rows); move(term, term->curs.x, term->curs.y + def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case ANSI('c', '>'): /* DA: report xterm version */ compatibility(OTHER); /* this reports xterm version 136 so that VIM can use the drag messages from the mouse reporting */ if (term->ldisc) ldisc_send(term->ldisc, "\033[>0;136;0c", 11, 0); break; case 'a': /* HPR: move right N cols */ compatibility(ANSI); /* FALLTHROUGH */ case 'C': /* CUF: Cursor right */ CLAMP(term->esc_args[0], term->cols); move(term, term->curs.x + def(term->esc_args[0], 1), term->curs.y, 1); seen_disp_event(term); break; case 'D': /* CUB: move left N cols */ CLAMP(term->esc_args[0], term->cols); move(term, term->curs.x - def(term->esc_args[0], 1), term->curs.y, 1); seen_disp_event(term); break; case 'E': /* CNL: move down N lines and CR */ compatibility(ANSI); CLAMP(term->esc_args[0], term->rows); move(term, 0, term->curs.y + def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case 'F': /* CPL: move up N lines and CR */ compatibility(ANSI); CLAMP(term->esc_args[0], term->rows); move(term, 0, term->curs.y - def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case 'G': /* CHA */ case '`': /* HPA: set horizontal posn */ compatibility(ANSI); CLAMP(term->esc_args[0], term->cols); move(term, def(term->esc_args[0], 1) - 1, term->curs.y, 0); seen_disp_event(term); break; case 'd': /* VPA: set vertical posn */ compatibility(ANSI); CLAMP(term->esc_args[0], term->rows); move(term, term->curs.x, ((term->dec_om ? term->marg_t : 0) + def(term->esc_args[0], 1) - 1), (term->dec_om ? 2 : 0)); seen_disp_event(term); break; case 'H': /* CUP */ case 'f': /* HVP: set horz and vert posns at once */ if (term->esc_nargs < 2) term->esc_args[1] = ARG_DEFAULT; CLAMP(term->esc_args[0], term->rows); CLAMP(term->esc_args[1], term->cols); move(term, def(term->esc_args[1], 1) - 1, ((term->dec_om ? term->marg_t : 0) + def(term->esc_args[0], 1) - 1), (term->dec_om ? 2 : 0)); seen_disp_event(term); break; case 'J': /* ED: erase screen or parts of it */ { unsigned int i = def(term->esc_args[0], 0); if (i == 3) { /* Erase Saved Lines (xterm) * This follows Thomas Dickey's xterm. */ if (!term->no_remote_clearscroll) term_clrsb(term); } else { i++; if (i > 3) i = 0; erase_lots(term, FALSE, !!(i & 2), !!(i & 1)); } } if (term->scroll_on_disp) term->disptop = 0; seen_disp_event(term); break; case 'K': /* EL: erase line or parts of it */ { unsigned int i = def(term->esc_args[0], 0) + 1; if (i > 3) i = 0; erase_lots(term, TRUE, !!(i & 2), !!(i & 1)); } seen_disp_event(term); break; case 'L': /* IL: insert lines */ compatibility(VT102); CLAMP(term->esc_args[0], term->rows); if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, -def(term->esc_args[0], 1), FALSE); seen_disp_event(term); break; case 'M': /* DL: delete lines */ compatibility(VT102); CLAMP(term->esc_args[0], term->rows); if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, def(term->esc_args[0], 1), TRUE); seen_disp_event(term); break; case '@': /* ICH: insert chars */ /* XXX VTTEST says this is vt220, vt510 manual says vt102 */ compatibility(VT102); CLAMP(term->esc_args[0], term->cols); insch(term, def(term->esc_args[0], 1)); seen_disp_event(term); break; case 'P': /* DCH: delete chars */ compatibility(VT102); CLAMP(term->esc_args[0], term->cols); insch(term, -def(term->esc_args[0], 1)); seen_disp_event(term); break; case 'c': /* DA: terminal type query */ compatibility(VT100); /* This is the response for a VT102 */ if (term->ldisc) ldisc_send(term->ldisc, term->id_string, strlen(term->id_string), 0); break; case 'n': /* DSR: cursor position query */ if (term->ldisc) { if (term->esc_args[0] == 6) { char buf[32]; sprintf(buf, "\033[%d;%dR", term->curs.y + 1, term->curs.x + 1); ldisc_send(term->ldisc, buf, strlen(buf), 0); } else if (term->esc_args[0] == 5) { ldisc_send(term->ldisc, "\033[0n", 4, 0); } } break; case 'h': /* SM: toggle modes to high */ case ANSI_QUE('h'): compatibility(VT100); { int i; for (i = 0; i < term->esc_nargs; i++) toggle_mode(term, term->esc_args[i], term->esc_query, TRUE); } break; case 'i': /* MC: Media copy */ case ANSI_QUE('i'): compatibility(VT100); { char *printer; if (term->esc_nargs != 1) break; if (term->esc_args[0] == 5 && (printer = conf_get_str(term->conf, CONF_printer))[0]) { term->printing = TRUE; term->only_printing = !term->esc_query; term->print_state = 0; term_print_setup(term, printer); } else if (term->esc_args[0] == 4 && term->printing) { term_print_finish(term); } } break; case 'l': /* RM: toggle modes to low */ case ANSI_QUE('l'): compatibility(VT100); { int i; for (i = 0; i < term->esc_nargs; i++) toggle_mode(term, term->esc_args[i], term->esc_query, FALSE); } break; case 'g': /* TBC: clear tabs */ compatibility(VT100); if (term->esc_nargs == 1) { if (term->esc_args[0] == 0) { term->tabs[term->curs.x] = FALSE; } else if (term->esc_args[0] == 3) { int i; for (i = 0; i < term->cols; i++) term->tabs[i] = FALSE; } } break; case 'r': /* DECSTBM: set scroll margins */ compatibility(VT100); if (term->esc_nargs <= 2) { int top, bot; CLAMP(term->esc_args[0], term->rows); CLAMP(term->esc_args[1], term->rows); top = def(term->esc_args[0], 1) - 1; bot = (term->esc_nargs <= 1 || term->esc_args[1] == 0 ? term->rows : def(term->esc_args[1], term->rows)) - 1; if (bot >= term->rows) bot = term->rows - 1; /* VTTEST Bug 9 - if region is less than 2 lines * don't change region. */ if (bot - top > 0) { term->marg_t = top; term->marg_b = bot; term->curs.x = 0; /* * I used to think the cursor should be * placed at the top of the newly marginned * area. Apparently not: VMS TPU falls over * if so. * * Well actually it should for * Origin mode - RDB */ term->curs.y = (term->dec_om ? term->marg_t : 0); seen_disp_event(term); } } break; case 'm': /* SGR: set graphics rendition */ { /* * A VT100 without the AVO only had one * attribute, either underline or * reverse video depending on the * cursor type, this was selected by * CSI 7m. * * case 2: * This is sometimes DIM, eg on the * GIGI and Linux * case 8: * This is sometimes INVIS various ANSI. * case 21: * This like 22 disables BOLD, DIM and INVIS * * The ANSI colours appear on any * terminal that has colour (obviously) * but the interaction between sgr0 and * the colours varies but is usually * related to the background colour * erase item. The interaction between * colour attributes and the mono ones * is also very implementation * dependent. * * The 39 and 49 attributes are likely * to be unimplemented. */ int i; for (i = 0; i < term->esc_nargs; i++) { switch (def(term->esc_args[i], 0)) { case 0: /* restore defaults */ term->curr_attr = term->default_attr; break; case 1: /* enable bold */ compatibility(VT100AVO); term->curr_attr |= ATTR_BOLD; break; case 21: /* (enable double underline) */ compatibility(OTHER); case 4: /* enable underline */ compatibility(VT100AVO); term->curr_attr |= ATTR_UNDER; break; case 5: /* enable blink */ compatibility(VT100AVO); term->curr_attr |= ATTR_BLINK; break; case 6: /* SCO light bkgrd */ compatibility(SCOANSI); term->blink_is_real = FALSE; term->curr_attr |= ATTR_BLINK; term_schedule_tblink(term); break; case 7: /* enable reverse video */ term->curr_attr |= ATTR_REVERSE; break; case 10: /* SCO acs off */ compatibility(SCOANSI); if (term->no_remote_charset) break; term->sco_acs = 0; break; case 11: /* SCO acs on */ compatibility(SCOANSI); if (term->no_remote_charset) break; term->sco_acs = 1; break; case 12: /* SCO acs on, |0x80 */ compatibility(SCOANSI); if (term->no_remote_charset) break; term->sco_acs = 2; break; case 22: /* disable bold */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_BOLD; break; case 24: /* disable underline */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_UNDER; break; case 25: /* disable blink */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_BLINK; break; case 27: /* disable reverse video */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_REVERSE; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: /* foreground */ term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= (term->esc_args[i] - 30)<curr_attr &= ~ATTR_FGMASK; term->curr_attr |= ((term->esc_args[i] - 90 + 8) << ATTR_FGSHIFT); break; case 39: /* default-foreground */ term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= ATTR_DEFFG; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: /* background */ term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= (term->esc_args[i] - 40)<curr_attr &= ~ATTR_BGMASK; term->curr_attr |= ((term->esc_args[i] - 100 + 8) << ATTR_BGSHIFT); break; case 49: /* default-background */ term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= ATTR_DEFBG; break; case 38: /* xterm 256-colour mode */ if (i+2 < term->esc_nargs && term->esc_args[i+1] == 5) { term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= ((term->esc_args[i+2] & 0xFF) << ATTR_FGSHIFT); i += 2; } break; case 48: /* xterm 256-colour mode */ if (i+2 < term->esc_nargs && term->esc_args[i+1] == 5) { term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= ((term->esc_args[i+2] & 0xFF) << ATTR_BGSHIFT); i += 2; } break; } } set_erase_char(term); } break; case 's': /* save cursor */ save_cursor(term, TRUE); break; case 'u': /* restore cursor */ save_cursor(term, FALSE); seen_disp_event(term); break; case 't': /* DECSLPP: set page size - ie window height */ /* * VT340/VT420 sequence DECSLPP, DEC only allows values * 24/25/36/48/72/144 other emulators (eg dtterm) use * illegal values (eg first arg 1..9) for window changing * and reports. */ if (term->esc_nargs <= 1 && (term->esc_args[0] < 1 || term->esc_args[0] >= 24)) { compatibility(VT340TEXT); if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], 24)); deselect(term); } else if (term->esc_nargs >= 1 && term->esc_args[0] >= 1 && term->esc_args[0] < 24) { compatibility(OTHER); switch (term->esc_args[0]) { int x, y, len; char buf[80]; const char *p; case 1: set_iconic(term->frontend, FALSE); break; case 2: set_iconic(term->frontend, TRUE); break; case 3: if (term->esc_nargs >= 3) { if (!term->no_remote_resize) move_window(term->frontend, def(term->esc_args[1], 0), def(term->esc_args[2], 0)); } break; case 4: /* We should resize the window to a given * size in pixels here, but currently our * resizing code isn't healthy enough to * manage it. */ break; case 5: /* move to top */ set_zorder(term->frontend, TRUE); break; case 6: /* move to bottom */ set_zorder(term->frontend, FALSE); break; case 7: refresh_window(term->frontend); break; case 8: if (term->esc_nargs >= 3) { if (!term->no_remote_resize) request_resize(term->frontend, def(term->esc_args[2], term->conf_width), def(term->esc_args[1], term->conf_height)); } break; case 9: if (term->esc_nargs >= 2) set_zoomed(term->frontend, term->esc_args[1] ? TRUE : FALSE); break; case 11: if (term->ldisc) ldisc_send(term->ldisc, is_iconic(term->frontend) ? "\033[2t" : "\033[1t", 4, 0); break; case 13: if (term->ldisc) { get_window_pos(term->frontend, &x, &y); len = sprintf(buf, "\033[3;%u;%ut", (unsigned)x, (unsigned)y); ldisc_send(term->ldisc, buf, len, 0); } break; case 14: if (term->ldisc) { get_window_pixels(term->frontend, &x, &y); len = sprintf(buf, "\033[4;%d;%dt", y, x); ldisc_send(term->ldisc, buf, len, 0); } break; case 18: if (term->ldisc) { len = sprintf(buf, "\033[8;%d;%dt", term->rows, term->cols); ldisc_send(term->ldisc, buf, len, 0); } break; case 19: /* * Hmmm. Strictly speaking we * should return `the size of the * screen in characters', but * that's not easy: (a) window * furniture being what it is it's * hard to compute, and (b) in * resize-font mode maximising the * window wouldn't change the * number of characters. *shrug*. I * think we'll ignore it for the * moment and see if anyone * complains, and then ask them * what they would like it to do. */ break; case 20: if (term->ldisc && term->remote_qtitle_action != TITLE_NONE) { if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, TRUE); else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]L", 3, 0); ldisc_send(term->ldisc, p, len, 0); ldisc_send(term->ldisc, "\033\\", 2, 0); } break; case 21: if (term->ldisc && term->remote_qtitle_action != TITLE_NONE) { if(term->remote_qtitle_action == TITLE_REAL) p = get_window_title(term->frontend, FALSE); else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]l", 3, 0); ldisc_send(term->ldisc, p, len, 0); ldisc_send(term->ldisc, "\033\\", 2, 0); } break; } } break; case 'S': /* SU: Scroll up */ CLAMP(term->esc_args[0], term->rows); compatibility(SCOANSI); scroll(term, term->marg_t, term->marg_b, def(term->esc_args[0], 1), TRUE); term->wrapnext = FALSE; seen_disp_event(term); break; case 'T': /* SD: Scroll down */ CLAMP(term->esc_args[0], term->rows); compatibility(SCOANSI); scroll(term, term->marg_t, term->marg_b, -def(term->esc_args[0], 1), TRUE); term->wrapnext = FALSE; seen_disp_event(term); break; case ANSI('|', '*'): /* DECSNLS */ /* * Set number of lines on screen * VT420 uses VGA like hardware and can * support any size in reasonable range * (24..49 AIUI) with no default specified. */ compatibility(VT420); if (term->esc_nargs == 1 && term->esc_args[0] > 0) { if (!term->no_remote_resize) request_resize(term->frontend, term->cols, def(term->esc_args[0], term->conf_height)); deselect(term); } break; case ANSI('|', '$'): /* DECSCPP */ /* * Set number of columns per page * Docs imply range is only 80 or 132, but * I'll allow any. */ compatibility(VT340TEXT); if (term->esc_nargs <= 1) { if (!term->no_remote_resize) request_resize(term->frontend, def(term->esc_args[0], term->conf_width), term->rows); deselect(term); } break; case 'X': /* ECH: write N spaces w/o moving cursor */ /* XXX VTTEST says this is vt220, vt510 manual * says vt100 */ compatibility(ANSIMIN); CLAMP(term->esc_args[0], term->cols); { int n = def(term->esc_args[0], 1); pos cursplus; int p = term->curs.x; termline *cline = scrlineptr(term->curs.y); if (n > term->cols - term->curs.x) n = term->cols - term->curs.x; cursplus = term->curs; cursplus.x += n; check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+n, term->curs.y); check_selection(term, term->curs, cursplus); while (n--) copy_termchar(cline, p++, &term->erase_char); seen_disp_event(term); } break; case 'x': /* DECREQTPARM: report terminal characteristics */ compatibility(VT100); if (term->ldisc) { char buf[32]; int i = def(term->esc_args[0], 0); if (i == 0 || i == 1) { strcpy(buf, "\033[2;1;1;112;112;1;0x"); buf[2] += i; ldisc_send(term->ldisc, buf, 20, 0); } } break; case 'Z': /* CBT */ compatibility(OTHER); CLAMP(term->esc_args[0], term->cols); { int i = def(term->esc_args[0], 1); pos old_curs = term->curs; for(;i>0 && term->curs.x>0; i--) { do { term->curs.x--; } while (term->curs.x >0 && !term->tabs[term->curs.x]); } check_selection(term, old_curs, term->curs); } break; case ANSI('c', '='): /* Hide or Show Cursor */ compatibility(SCOANSI); switch(term->esc_args[0]) { case 0: /* hide cursor */ term->cursor_on = FALSE; break; case 1: /* restore cursor */ term->big_cursor = FALSE; term->cursor_on = TRUE; break; case 2: /* block cursor */ term->big_cursor = TRUE; term->cursor_on = TRUE; break; } break; case ANSI('C', '='): /* * set cursor start on scanline esc_args[0] and * end on scanline esc_args[1].If you set * the bottom scan line to a value less than * the top scan line, the cursor will disappear. */ compatibility(SCOANSI); if (term->esc_nargs >= 2) { if (term->esc_args[0] > term->esc_args[1]) term->cursor_on = FALSE; else term->cursor_on = TRUE; } break; case ANSI('D', '='): compatibility(SCOANSI); term->blink_is_real = FALSE; term_schedule_tblink(term); if (term->esc_args[0]>=1) term->curr_attr |= ATTR_BLINK; else term->curr_attr &= ~ATTR_BLINK; break; case ANSI('E', '='): compatibility(SCOANSI); term->blink_is_real = (term->esc_args[0] >= 1); term_schedule_tblink(term); break; case ANSI('F', '='): /* set normal foreground */ compatibility(SCOANSI); if (term->esc_args[0] < 16) { long colour = (sco2ansicolour[term->esc_args[0] & 0x7] | (term->esc_args[0] & 0x8)) << ATTR_FGSHIFT; term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= colour; term->default_attr &= ~ATTR_FGMASK; term->default_attr |= colour; set_erase_char(term); } break; case ANSI('G', '='): /* set normal background */ compatibility(SCOANSI); if (term->esc_args[0] < 16) { long colour = (sco2ansicolour[term->esc_args[0] & 0x7] | (term->esc_args[0] & 0x8)) << ATTR_BGSHIFT; term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= colour; term->default_attr &= ~ATTR_BGMASK; term->default_attr |= colour; set_erase_char(term); } break; case ANSI('L', '='): compatibility(SCOANSI); term->use_bce = (term->esc_args[0] <= 0); set_erase_char(term); break; case ANSI('p', '"'): /* DECSCL: set compat level */ /* * Allow the host to make this emulator a * 'perfect' VT102. This first appeared in * the VT220, but we do need to get back to * PuTTY mode so I won't check it. * * The arg in 40..42,50 are a PuTTY extension. * The 2nd arg, 8bit vs 7bit is not checked. * * Setting VT102 mode should also change * the Fkeys to generate PF* codes as a * real VT102 has no Fkeys. The VT220 does * this, F11..F13 become ESC,BS,LF other * Fkeys send nothing. * * Note ESC c will NOT change this! */ switch (term->esc_args[0]) { case 61: term->compatibility_level &= ~TM_VTXXX; term->compatibility_level |= TM_VT102; break; case 62: term->compatibility_level &= ~TM_VTXXX; term->compatibility_level |= TM_VT220; break; default: if (term->esc_args[0] > 60 && term->esc_args[0] < 70) term->compatibility_level |= TM_VTXXX; break; case 40: term->compatibility_level &= TM_VTXXX; break; case 41: term->compatibility_level = TM_PUTTY; break; case 42: term->compatibility_level = TM_SCOANSI; break; case ARG_DEFAULT: term->compatibility_level = TM_PUTTY; break; case 50: break; } /* Change the response to CSI c */ if (term->esc_args[0] == 50) { int i; char lbuf[64]; strcpy(term->id_string, "\033[?"); for (i = 1; i < term->esc_nargs; i++) { if (i != 1) strcat(term->id_string, ";"); sprintf(lbuf, "%u", term->esc_args[i]); strcat(term->id_string, lbuf); } strcat(term->id_string, "c"); } #if 0 /* Is this a good idea ? * Well we should do a soft reset at this point ... */ if (!has_compat(VT420) && has_compat(VT100)) { if (!term->no_remote_resize) { if (term->reset_132) request_resize(132, 24); else request_resize(80, 24); } } #endif break; } break; case SEEN_OSC: term->osc_w = FALSE; switch (c) { case 'P': /* Linux palette sequence */ term->termstate = SEEN_OSC_P; term->osc_strlen = 0; break; case 'R': /* Linux palette reset */ palette_reset(term->frontend); term_invalidate(term); term->termstate = TOPLEVEL; break; case 'W': /* word-set */ term->termstate = SEEN_OSC_W; term->osc_w = TRUE; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (term->esc_args[0] <= UINT_MAX / 10 && term->esc_args[0] * 10 <= UINT_MAX - c - '0') term->esc_args[0] = 10 * term->esc_args[0] + c - '0'; else term->esc_args[0] = UINT_MAX; break; case 'L': /* * Grotty hack to support xterm and DECterm title * sequences concurrently. */ if (term->esc_args[0] == 2) { term->esc_args[0] = 1; break; } /* else fall through */ default: term->termstate = OSC_STRING; term->osc_strlen = 0; } break; case OSC_STRING: /* * This OSC stuff is EVIL. It takes just one character to get into * sysline mode and it's not initially obvious how to get out. * So I've added CR and LF as string aborts. * This shouldn't effect compatibility as I believe embedded * control characters are supposed to be interpreted (maybe?) * and they don't display anything useful anyway. * * -- RDB */ if (c == '\012' || c == '\015') { term->termstate = TOPLEVEL; } else if (c == 0234 || c == '\007') { /* * These characters terminate the string; ST and BEL * terminate the sequence and trigger instant * processing of it, whereas ESC goes back to SEEN_ESC * mode unless it is followed by \, in which case it is * synonymous with ST in the first place. */ do_osc(term); term->termstate = TOPLEVEL; } else if (c == '\033') term->termstate = OSC_MAYBE_ST; else if (term->osc_strlen < OSC_STR_MAX) term->osc_string[term->osc_strlen++] = (char)c; break; case SEEN_OSC_P: { int max = (term->osc_strlen == 0 ? 21 : 15); int val; if ((int)c >= '0' && (int)c <= '9') val = c - '0'; else if ((int)c >= 'A' && (int)c <= 'A' + max - 10) val = c - 'A' + 10; else if ((int)c >= 'a' && (int)c <= 'a' + max - 10) val = c - 'a' + 10; else { term->termstate = TOPLEVEL; break; } term->osc_string[term->osc_strlen++] = val; if (term->osc_strlen >= 7) { palette_set(term->frontend, term->osc_string[0], term->osc_string[1] * 16 + term->osc_string[2], term->osc_string[3] * 16 + term->osc_string[4], term->osc_string[5] * 16 + term->osc_string[6]); term_invalidate(term); term->termstate = TOPLEVEL; } } break; case SEEN_OSC_W: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (term->esc_args[0] <= UINT_MAX / 10 && term->esc_args[0] * 10 <= UINT_MAX - c - '0') term->esc_args[0] = 10 * term->esc_args[0] + c - '0'; else term->esc_args[0] = UINT_MAX; break; default: term->termstate = OSC_STRING; term->osc_strlen = 0; } break; case VT52_ESC: term->termstate = TOPLEVEL; seen_disp_event(term); switch (c) { case 'A': move(term, term->curs.x, term->curs.y - 1, 1); break; case 'B': move(term, term->curs.x, term->curs.y + 1, 1); break; case 'C': move(term, term->curs.x + 1, term->curs.y, 1); break; case 'D': move(term, term->curs.x - 1, term->curs.y, 1); break; /* * From the VT100 Manual * NOTE: The special graphics characters in the VT100 * are different from those in the VT52 * * From VT102 manual: * 137 _ Blank - Same * 140 ` Reserved - Humm. * 141 a Solid rectangle - Similar * 142 b 1/ - Top half of fraction for the * 143 c 3/ - subscript numbers below. * 144 d 5/ * 145 e 7/ * 146 f Degrees - Same * 147 g Plus or minus - Same * 150 h Right arrow * 151 i Ellipsis (dots) * 152 j Divide by * 153 k Down arrow * 154 l Bar at scan 0 * 155 m Bar at scan 1 * 156 n Bar at scan 2 * 157 o Bar at scan 3 - Similar * 160 p Bar at scan 4 - Similar * 161 q Bar at scan 5 - Similar * 162 r Bar at scan 6 - Same * 163 s Bar at scan 7 - Similar * 164 t Subscript 0 * 165 u Subscript 1 * 166 v Subscript 2 * 167 w Subscript 3 * 170 x Subscript 4 * 171 y Subscript 5 * 172 z Subscript 6 * 173 { Subscript 7 * 174 | Subscript 8 * 175 } Subscript 9 * 176 ~ Paragraph * */ case 'F': term->cset_attr[term->cset = 0] = CSET_LINEDRW; break; case 'G': term->cset_attr[term->cset = 0] = CSET_ASCII; break; case 'H': move(term, 0, 0, 0); break; case 'I': if (term->curs.y == 0) scroll(term, 0, term->rows - 1, -1, TRUE); else if (term->curs.y > 0) term->curs.y--; term->wrapnext = FALSE; break; case 'J': erase_lots(term, FALSE, FALSE, TRUE); if (term->scroll_on_disp) term->disptop = 0; break; case 'K': erase_lots(term, TRUE, FALSE, TRUE); break; #if 0 case 'V': /* XXX Print cursor line */ break; case 'W': /* XXX Start controller mode */ break; case 'X': /* XXX Stop controller mode */ break; #endif case 'Y': term->termstate = VT52_Y1; break; case 'Z': if (term->ldisc) ldisc_send(term->ldisc, "\033/Z", 3, 0); break; case '=': term->app_keypad_keys = TRUE; break; case '>': term->app_keypad_keys = FALSE; break; case '<': /* XXX This should switch to VT100 mode not current or default * VT mode. But this will only have effect in a VT220+ * emulation. */ term->vt52_mode = FALSE; term->blink_is_real = term->blinktext; term_schedule_tblink(term); break; #if 0 case '^': /* XXX Enter auto print mode */ break; case '_': /* XXX Exit auto print mode */ break; case ']': /* XXX Print screen */ break; #endif #ifdef VT52_PLUS case 'E': /* compatibility(ATARI) */ move(term, 0, 0, 0); erase_lots(term, FALSE, FALSE, TRUE); if (term->scroll_on_disp) term->disptop = 0; break; case 'L': /* compatibility(ATARI) */ if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, -1, FALSE); break; case 'M': /* compatibility(ATARI) */ if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, 1, TRUE); break; case 'b': /* compatibility(ATARI) */ term->termstate = VT52_FG; break; case 'c': /* compatibility(ATARI) */ term->termstate = VT52_BG; break; case 'd': /* compatibility(ATARI) */ erase_lots(term, FALSE, TRUE, FALSE); if (term->scroll_on_disp) term->disptop = 0; break; case 'e': /* compatibility(ATARI) */ term->cursor_on = TRUE; break; case 'f': /* compatibility(ATARI) */ term->cursor_on = FALSE; break; /* case 'j': Save cursor position - broken on ST */ /* case 'k': Restore cursor position */ case 'l': /* compatibility(ATARI) */ erase_lots(term, TRUE, TRUE, TRUE); term->curs.x = 0; term->wrapnext = FALSE; break; case 'o': /* compatibility(ATARI) */ erase_lots(term, TRUE, TRUE, FALSE); break; case 'p': /* compatibility(ATARI) */ term->curr_attr |= ATTR_REVERSE; break; case 'q': /* compatibility(ATARI) */ term->curr_attr &= ~ATTR_REVERSE; break; case 'v': /* wrap Autowrap on - Wyse style */ /* compatibility(ATARI) */ term->wrap = 1; break; case 'w': /* Autowrap off */ /* compatibility(ATARI) */ term->wrap = 0; break; case 'R': /* compatibility(OTHER) */ term->vt52_bold = FALSE; term->curr_attr = ATTR_DEFAULT; set_erase_char(term); break; case 'S': /* compatibility(VI50) */ term->curr_attr |= ATTR_UNDER; break; case 'W': /* compatibility(VI50) */ term->curr_attr &= ~ATTR_UNDER; break; case 'U': /* compatibility(VI50) */ term->vt52_bold = TRUE; term->curr_attr |= ATTR_BOLD; break; case 'T': /* compatibility(VI50) */ term->vt52_bold = FALSE; term->curr_attr &= ~ATTR_BOLD; break; #endif } break; case VT52_Y1: term->termstate = VT52_Y2; move(term, term->curs.x, c - ' ', 0); break; case VT52_Y2: term->termstate = TOPLEVEL; move(term, c - ' ', term->curs.y, 0); break; #ifdef VT52_PLUS case VT52_FG: term->termstate = TOPLEVEL; term->curr_attr &= ~ATTR_FGMASK; term->curr_attr &= ~ATTR_BOLD; term->curr_attr |= (c & 0xF) << ATTR_FGSHIFT; set_erase_char(term); break; case VT52_BG: term->termstate = TOPLEVEL; term->curr_attr &= ~ATTR_BGMASK; term->curr_attr &= ~ATTR_BLINK; term->curr_attr |= (c & 0xF) << ATTR_BGSHIFT; set_erase_char(term); break; #endif default: break; /* placate gcc warning about enum use */ } if (term->selstate != NO_SELECTION) { pos cursplus = term->curs; incpos(cursplus); check_selection(term, term->curs, cursplus); } } term_print_flush(term); if (term->logflush && term->logctx) logflush(term->logctx); } /* * To prevent having to run the reasonably tricky bidi algorithm * too many times, we maintain a cache of the last lineful of data * fed to the algorithm on each line of the display. */ static int term_bidi_cache_hit(Terminal *term, int line, termchar *lbefore, int width) { int i; if (!term->pre_bidi_cache) return FALSE; /* cache doesn't even exist yet! */ if (line >= term->bidi_cache_size) return FALSE; /* cache doesn't have this many lines */ if (!term->pre_bidi_cache[line].chars) return FALSE; /* cache doesn't contain _this_ line */ if (term->pre_bidi_cache[line].width != width) return FALSE; /* line is wrong width */ for (i = 0; i < width; i++) if (!termchars_equal(term->pre_bidi_cache[line].chars+i, lbefore+i)) return FALSE; /* line doesn't match cache */ return TRUE; /* it didn't match. */ } static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore, termchar *lafter, bidi_char *wcTo, int width, int size) { int i; if (!term->pre_bidi_cache || term->bidi_cache_size <= line) { int j = term->bidi_cache_size; term->bidi_cache_size = line+1; term->pre_bidi_cache = sresize(term->pre_bidi_cache, term->bidi_cache_size, struct bidi_cache_entry); term->post_bidi_cache = sresize(term->post_bidi_cache, term->bidi_cache_size, struct bidi_cache_entry); while (j < term->bidi_cache_size) { term->pre_bidi_cache[j].chars = term->post_bidi_cache[j].chars = NULL; term->pre_bidi_cache[j].width = term->post_bidi_cache[j].width = -1; term->pre_bidi_cache[j].forward = term->post_bidi_cache[j].forward = NULL; term->pre_bidi_cache[j].backward = term->post_bidi_cache[j].backward = NULL; j++; } } sfree(term->pre_bidi_cache[line].chars); sfree(term->post_bidi_cache[line].chars); sfree(term->post_bidi_cache[line].forward); sfree(term->post_bidi_cache[line].backward); term->pre_bidi_cache[line].width = width; term->pre_bidi_cache[line].chars = snewn(size, termchar); term->post_bidi_cache[line].width = width; term->post_bidi_cache[line].chars = snewn(size, termchar); term->post_bidi_cache[line].forward = snewn(width, int); term->post_bidi_cache[line].backward = snewn(width, int); memcpy(term->pre_bidi_cache[line].chars, lbefore, size * TSIZE); memcpy(term->post_bidi_cache[line].chars, lafter, size * TSIZE); memset(term->post_bidi_cache[line].forward, 0, width * sizeof(int)); memset(term->post_bidi_cache[line].backward, 0, width * sizeof(int)); for (i = 0; i < width; i++) { int p = wcTo[i].index; assert(0 <= p && p < width); term->post_bidi_cache[line].backward[i] = p; term->post_bidi_cache[line].forward[p] = i; } } /* * Prepare the bidi information for a screen line. Returns the * transformed list of termchars, or NULL if no transformation at * all took place (because bidi is disabled). If return was * non-NULL, auxiliary information such as the forward and reverse * mappings of permutation position are available in * term->post_bidi_cache[scr_y].*. */ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, int scr_y) { termchar *lchars; int it; /* Do Arabic shaping and bidi. */ if(!term->bidi || !term->arabicshaping) { if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) { if (term->wcFromTo_size < term->cols) { term->wcFromTo_size = term->cols; term->wcFrom = sresize(term->wcFrom, term->wcFromTo_size, bidi_char); term->wcTo = sresize(term->wcTo, term->wcFromTo_size, bidi_char); } for(it=0; itcols ; it++) { unsigned long uc = (ldata->chars[it].chr); switch (uc & CSET_MASK) { case CSET_LINEDRW: if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } case CSET_ASCII: uc = term->ucsdata->unitab_line[uc & 0xFF]; break; case CSET_SCOACS: uc = term->ucsdata->unitab_scoacs[uc&0xFF]; break; } switch (uc & CSET_MASK) { case CSET_ACP: uc = term->ucsdata->unitab_font[uc & 0xFF]; break; case CSET_OEMCP: uc = term->ucsdata->unitab_oemcp[uc & 0xFF]; break; } term->wcFrom[it].origwc = term->wcFrom[it].wc = (unsigned int)uc; term->wcFrom[it].index = it; } if(!term->bidi) do_bidi(term->wcFrom, term->cols); /* this is saved iff done from inside the shaping */ if(!term->bidi && term->arabicshaping) for(it=0; itcols; it++) term->wcTo[it] = term->wcFrom[it]; if(!term->arabicshaping) do_shape(term->wcFrom, term->wcTo, term->cols); if (term->ltemp_size < ldata->size) { term->ltemp_size = ldata->size; term->ltemp = sresize(term->ltemp, term->ltemp_size, termchar); } memcpy(term->ltemp, ldata->chars, ldata->size * TSIZE); for(it=0; itcols ; it++) { term->ltemp[it] = ldata->chars[term->wcTo[it].index]; if (term->ltemp[it].cc_next) term->ltemp[it].cc_next -= it - term->wcTo[it].index; if (term->wcTo[it].origwc != term->wcTo[it].wc) term->ltemp[it].chr = term->wcTo[it].wc; } term_bidi_cache_store(term, scr_y, ldata->chars, term->ltemp, term->wcTo, term->cols, ldata->size); lchars = term->ltemp; } else { lchars = term->post_bidi_cache[scr_y].chars; } } else { lchars = NULL; } return lchars; } /* * Given a context, update the window. Out of paranoia, we don't * allow WM_PAINT responses to do scrolling optimisations. */ static void do_paint(Terminal *term, Context ctx, int may_optimise) { int i, j, our_curs_y, our_curs_x; int rv, cursor; pos scrpos; wchar_t *ch; int chlen; #ifdef OPTIMISE_SCROLL struct scrollregion *sr; #endif /* OPTIMISE_SCROLL */ termchar *newline; chlen = 1024; ch = snewn(chlen, wchar_t); newline = snewn(term->cols, termchar); rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0); /* Depends on: * screen array, disptop, scrtop, * selection, rv, * blinkpc, blink_is_real, tblinker, * curs.y, curs.x, cblinker, blink_cur, cursor_on, has_focus, wrapnext */ /* Has the cursor position or type changed ? */ if (term->cursor_on) { if (term->has_focus) { if (term->cblinker || !term->blink_cur) cursor = TATTR_ACTCURS; else cursor = 0; } else cursor = TATTR_PASCURS; if (term->wrapnext) cursor |= TATTR_RIGHTCURS; } else cursor = 0; our_curs_y = term->curs.y - term->disptop; { /* * Adjust the cursor position: * - for bidi * - in the case where it's resting on the right-hand half * of a CJK wide character. xterm's behaviour here, * which seems adequate to me, is to display the cursor * covering the _whole_ character, exactly as if it were * one space to the left. */ termline *ldata = lineptr(term->curs.y); termchar *lchars; our_curs_x = term->curs.x; if ( (lchars = term_bidi_line(term, ldata, our_curs_y)) != NULL) { our_curs_x = term->post_bidi_cache[our_curs_y].forward[our_curs_x]; } else lchars = ldata->chars; if (our_curs_x > 0 && lchars[our_curs_x].chr == UCSWIDE) our_curs_x--; unlineptr(ldata); } /* * If the cursor is not where it was last time we painted, and * its previous position is visible on screen, invalidate its * previous position. */ if (term->dispcursy >= 0 && (term->curstype != cursor || term->dispcursy != our_curs_y || term->dispcursx != our_curs_x)) { termchar *dispcurs = term->disptext[term->dispcursy]->chars + term->dispcursx; if (term->dispcursx > 0 && dispcurs->chr == UCSWIDE) dispcurs[-1].attr |= ATTR_INVALID; if (term->dispcursx < term->cols-1 && dispcurs[1].chr == UCSWIDE) dispcurs[1].attr |= ATTR_INVALID; dispcurs->attr |= ATTR_INVALID; term->curstype = 0; } term->dispcursx = term->dispcursy = -1; #ifdef OPTIMISE_SCROLL /* Do scrolls */ sr = term->scrollhead; while (sr) { struct scrollregion *next = sr->next; do_scroll(ctx, sr->topline, sr->botline, sr->lines); sfree(sr); sr = next; } term->scrollhead = term->scrolltail = NULL; #endif /* OPTIMISE_SCROLL */ /* The normal screen data */ for (i = 0; i < term->rows; i++) { termline *ldata; termchar *lchars; int dirty_line, dirty_run, selected; unsigned long attr = 0, cset = 0; int start = 0; int ccount = 0; int last_run_dirty = 0; int laststart, dirtyrect; int *backward; scrpos.y = i + term->disptop; ldata = lineptr(scrpos.y); /* Do Arabic shaping and bidi. */ lchars = term_bidi_line(term, ldata, i); if (lchars) { backward = term->post_bidi_cache[i].backward; } else { lchars = ldata->chars; backward = NULL; } /* * First loop: work along the line deciding what we want * each character cell to look like. */ for (j = 0; j < term->cols; j++) { unsigned long tattr, tchar; termchar *d = lchars + j; scrpos.x = backward ? backward[j] : j; tchar = d->chr; tattr = d->attr; if (!term->ansi_colour) tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) | ATTR_DEFFG | ATTR_DEFBG; if (!term->xterm_256_colour) { int colour; colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT; if (colour >= 16 && colour < 256) tattr = (tattr &~ ATTR_FGMASK) | ATTR_DEFFG; colour = (tattr & ATTR_BGMASK) >> ATTR_BGSHIFT; if (colour >= 16 && colour < 256) tattr = (tattr &~ ATTR_BGMASK) | ATTR_DEFBG; } switch (tchar & CSET_MASK) { case CSET_ASCII: tchar = term->ucsdata->unitab_line[tchar & 0xFF]; break; case CSET_LINEDRW: tchar = term->ucsdata->unitab_xterm[tchar & 0xFF]; break; case CSET_SCOACS: tchar = term->ucsdata->unitab_scoacs[tchar&0xFF]; break; } if (j < term->cols-1 && d[1].chr == UCSWIDE) tattr |= ATTR_WIDE; /* Video reversing things */ if (term->selstate == DRAGGING || term->selstate == SELECTED) { if (term->seltype == LEXICOGRAPHIC) selected = (posle(term->selstart, scrpos) && poslt(scrpos, term->selend)); else selected = (posPle(term->selstart, scrpos) && posPlt(scrpos, term->selend)); } else selected = FALSE; tattr = (tattr ^ rv ^ (selected ? ATTR_REVERSE : 0)); /* 'Real' blinking ? */ if (term->blink_is_real && (tattr & ATTR_BLINK)) { if (term->has_focus && term->tblinker) { tchar = term->ucsdata->unitab_line[(unsigned char)' ']; } tattr &= ~ATTR_BLINK; } /* * Check the font we'll _probably_ be using to see if * the character is wide when we don't want it to be. */ if (tchar != term->disptext[i]->chars[j].chr || tattr != (term->disptext[i]->chars[j].attr &~ (ATTR_NARROW | DATTR_MASK))) { if ((tattr & ATTR_WIDE) == 0 && char_width(ctx, tchar) == 2) tattr |= ATTR_NARROW; } else if (term->disptext[i]->chars[j].attr & ATTR_NARROW) tattr |= ATTR_NARROW; if (i == our_curs_y && j == our_curs_x) { tattr |= cursor; term->curstype = cursor; term->dispcursx = j; term->dispcursy = i; } /* FULL-TERMCHAR */ newline[j].attr = tattr; newline[j].chr = tchar; /* Combining characters are still read from lchars */ newline[j].cc_next = 0; } /* * Now loop over the line again, noting where things have * changed. * * During this loop, we keep track of where we last saw * DATTR_STARTRUN. Any mismatch automatically invalidates * _all_ of the containing run that was last printed: that * is, any rectangle that was drawn in one go in the * previous update should be either left completely alone * or overwritten in its entirety. This, along with the * expectation that front ends clip all text runs to their * bounding rectangle, should solve any possible problems * with fonts that overflow their character cells. */ laststart = 0; dirtyrect = FALSE; for (j = 0; j < term->cols; j++) { if (term->disptext[i]->chars[j].attr & DATTR_STARTRUN) { laststart = j; dirtyrect = FALSE; } if (term->disptext[i]->chars[j].chr != newline[j].chr || (term->disptext[i]->chars[j].attr &~ DATTR_MASK) != newline[j].attr) { int k; if (!dirtyrect) { for (k = laststart; k < j; k++) term->disptext[i]->chars[k].attr |= ATTR_INVALID; dirtyrect = TRUE; } } if (dirtyrect) term->disptext[i]->chars[j].attr |= ATTR_INVALID; } /* * Finally, loop once more and actually do the drawing. */ dirty_run = dirty_line = (ldata->lattr != term->disptext[i]->lattr); term->disptext[i]->lattr = ldata->lattr; for (j = 0; j < term->cols; j++) { unsigned long tattr, tchar; int break_run, do_copy; termchar *d = lchars + j; tattr = newline[j].attr; tchar = newline[j].chr; if ((term->disptext[i]->chars[j].attr ^ tattr) & ATTR_WIDE) dirty_line = TRUE; break_run = ((tattr ^ attr) & term->attr_mask) != 0; #ifdef USES_VTLINE_HACK /* Special hack for VT100 Linedraw glyphs */ if ((tchar >= 0x23BA && tchar <= 0x23BD) || (j > 0 && (newline[j-1].chr >= 0x23BA && newline[j-1].chr <= 0x23BD))) break_run = TRUE; #endif /* * Separate out sequences of characters that have the * same CSET, if that CSET is a magic one. */ if (CSET_OF(tchar) != cset) break_run = TRUE; /* * Break on both sides of any combined-character cell. */ if (d->cc_next != 0 || (j > 0 && d[-1].cc_next != 0)) break_run = TRUE; if (!term->ucsdata->dbcs_screenfont && !dirty_line) { if (term->disptext[i]->chars[j].chr == tchar && (term->disptext[i]->chars[j].attr &~ DATTR_MASK) == tattr) break_run = TRUE; else if (!dirty_run && ccount == 1) break_run = TRUE; } if (break_run) { if ((dirty_run || last_run_dirty) && ccount > 0) { do_text(ctx, start, i, ch, ccount, attr, ldata->lattr); if (attr & (TATTR_ACTCURS | TATTR_PASCURS)) do_cursor(ctx, start, i, ch, ccount, attr, ldata->lattr); } start = j; ccount = 0; attr = tattr; cset = CSET_OF(tchar); if (term->ucsdata->dbcs_screenfont) last_run_dirty = dirty_run; dirty_run = dirty_line; } do_copy = FALSE; if (!termchars_equal_override(&term->disptext[i]->chars[j], d, tchar, tattr)) { do_copy = TRUE; dirty_run = TRUE; } if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } #ifdef PLATFORM_IS_UTF16 if (tchar > 0x10000 && tchar < 0x110000) { ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(tchar); ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(tchar); } else #endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) tchar; if (d->cc_next) { termchar *dd = d; while (dd->cc_next) { unsigned long schar; dd += dd->cc_next; schar = dd->chr; switch (schar & CSET_MASK) { case CSET_ASCII: schar = term->ucsdata->unitab_line[schar & 0xFF]; break; case CSET_LINEDRW: schar = term->ucsdata->unitab_xterm[schar & 0xFF]; break; case CSET_SCOACS: schar = term->ucsdata->unitab_scoacs[schar&0xFF]; break; } if (ccount+2 > chlen) { chlen = ccount + 256; ch = sresize(ch, chlen, wchar_t); } #ifdef PLATFORM_IS_UTF16 if (schar > 0x10000 && schar < 0x110000) { ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(schar); ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(schar); } else #endif /* PLATFORM_IS_UTF16 */ ch[ccount++] = (wchar_t) schar; } attr |= TATTR_COMBINING; } if (do_copy) { copy_termchar(term->disptext[i], j, d); term->disptext[i]->chars[j].chr = tchar; term->disptext[i]->chars[j].attr = tattr; if (start == j) term->disptext[i]->chars[j].attr |= DATTR_STARTRUN; } /* If it's a wide char step along to the next one. */ if (tattr & ATTR_WIDE) { if (++j < term->cols) { d++; /* * By construction above, the cursor should not * be on the right-hand half of this character. * Ever. */ assert(!(i == our_curs_y && j == our_curs_x)); if (!termchars_equal(&term->disptext[i]->chars[j], d)) dirty_run = TRUE; copy_termchar(term->disptext[i], j, d); } } } if (dirty_run && ccount > 0) { do_text(ctx, start, i, ch, ccount, attr, ldata->lattr); if (attr & (TATTR_ACTCURS | TATTR_PASCURS)) do_cursor(ctx, start, i, ch, ccount, attr, ldata->lattr); } unlineptr(ldata); } sfree(newline); sfree(ch); } /* * Invalidate the whole screen so it will be repainted in full. */ void term_invalidate(Terminal *term) { int i, j; for (i = 0; i < term->rows; i++) for (j = 0; j < term->cols; j++) term->disptext[i]->chars[j].attr |= ATTR_INVALID; term_schedule_update(term); } /* * Paint the window in response to a WM_PAINT message. */ void term_paint(Terminal *term, Context ctx, int left, int top, int right, int bottom, int immediately) { int i, j; if (left < 0) left = 0; if (top < 0) top = 0; if (right >= term->cols) right = term->cols-1; if (bottom >= term->rows) bottom = term->rows-1; for (i = top; i <= bottom && i < term->rows; i++) { if ((term->disptext[i]->lattr & LATTR_MODE) == LATTR_NORM) for (j = left; j <= right && j < term->cols; j++) term->disptext[i]->chars[j].attr |= ATTR_INVALID; else for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++) term->disptext[i]->chars[j].attr |= ATTR_INVALID; } if (immediately) { do_paint (term, ctx, FALSE); } else { term_schedule_update(term); } } /* * Attempt to scroll the scrollback. The second parameter gives the * position we want to scroll to; the first is +1 to denote that * this position is relative to the beginning of the scrollback, -1 * to denote it is relative to the end, and 0 to denote that it is * relative to the current position. */ void term_scroll(Terminal *term, int rel, int where) { int sbtop = -sblines(term); #ifdef OPTIMISE_SCROLL int olddisptop = term->disptop; int shift; #endif /* OPTIMISE_SCROLL */ term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where; if (term->disptop < sbtop) term->disptop = sbtop; if (term->disptop > 0) term->disptop = 0; update_sbar(term); #ifdef OPTIMISE_SCROLL shift = (term->disptop - olddisptop); if (shift < term->rows && shift > -term->rows) scroll_display(term, 0, term->rows - 1, shift); #endif /* OPTIMISE_SCROLL */ term_update(term); } /* * Scroll the scrollback to centre it on the beginning or end of the * current selection, if any. */ void term_scroll_to_selection(Terminal *term, int which_end) { pos target; int y; int sbtop = -sblines(term); if (term->selstate != SELECTED) return; if (which_end) target = term->selend; else target = term->selstart; y = target.y - term->rows/2; if (y < sbtop) y = sbtop; else if (y > 0) y = 0; term_scroll(term, -1, y); } /* * Helper routine for clipme(): growing buffer. */ typedef struct { int buflen; /* amount of allocated space in textbuf/attrbuf */ int bufpos; /* amount of actual data */ wchar_t *textbuf; /* buffer for copied text */ wchar_t *textptr; /* = textbuf + bufpos (current insertion point) */ int *attrbuf; /* buffer for copied attributes */ int *attrptr; /* = attrbuf + bufpos */ } clip_workbuf; static void clip_addchar(clip_workbuf *b, wchar_t chr, int attr) { if (b->bufpos >= b->buflen) { b->buflen *= 2; b->textbuf = sresize(b->textbuf, b->buflen, wchar_t); b->textptr = b->textbuf + b->bufpos; b->attrbuf = sresize(b->attrbuf, b->buflen, int); b->attrptr = b->attrbuf + b->bufpos; } *b->textptr++ = chr; *b->attrptr++ = attr; b->bufpos++; } static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) { clip_workbuf buf; int old_top_x; int attr; buf.buflen = 5120; buf.bufpos = 0; buf.textptr = buf.textbuf = snewn(buf.buflen, wchar_t); buf.attrptr = buf.attrbuf = snewn(buf.buflen, int); old_top_x = top.x; /* needed for rect==1 */ while (poslt(top, bottom)) { int nl = FALSE; termline *ldata = lineptr(top.y); pos nlpos; /* * nlpos will point at the maximum position on this line we * should copy up to. So we start it at the end of the * line... */ nlpos.y = top.y; nlpos.x = term->cols; /* * ... move it backwards if there's unused space at the end * of the line (and also set `nl' if this is the case, * because in normal selection mode this means we need a * newline at the end)... */ if (!(ldata->lattr & LATTR_WRAPPED)) { while (nlpos.x && IS_SPACE_CHR(ldata->chars[nlpos.x - 1].chr) && !ldata->chars[nlpos.x - 1].cc_next && poslt(top, nlpos)) decpos(nlpos); if (poslt(nlpos, bottom)) nl = TRUE; } else if (ldata->lattr & LATTR_WRAPPED2) { /* Ignore the last char on the line in a WRAPPED2 line. */ decpos(nlpos); } /* * ... and then clip it to the terminal x coordinate if * we're doing rectangular selection. (In this case we * still did the above, so that copying e.g. the right-hand * column from a table doesn't fill with spaces on the * right.) */ if (rect) { if (nlpos.x > bottom.x) nlpos.x = bottom.x; nl = (top.y < bottom.y); } while (poslt(top, bottom) && poslt(top, nlpos)) { #if 0 char cbuf[16], *p; sprintf(cbuf, "", (ldata[top.x] & 0xFFFF)); #else wchar_t cbuf[16], *p; int c; int x = top.x; if (ldata->chars[x].chr == UCSWIDE) { top.x++; continue; } while (1) { int uc = ldata->chars[x].chr; attr = ldata->chars[x].attr; switch (uc & CSET_MASK) { case CSET_LINEDRW: if (!term->rawcnp) { uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; } case CSET_ASCII: uc = term->ucsdata->unitab_line[uc & 0xFF]; break; case CSET_SCOACS: uc = term->ucsdata->unitab_scoacs[uc&0xFF]; break; } switch (uc & CSET_MASK) { case CSET_ACP: uc = term->ucsdata->unitab_font[uc & 0xFF]; break; case CSET_OEMCP: uc = term->ucsdata->unitab_oemcp[uc & 0xFF]; break; } c = (uc & ~CSET_MASK); #ifdef PLATFORM_IS_UTF16 if (uc > 0x10000 && uc < 0x110000) { cbuf[0] = 0xD800 | ((uc - 0x10000) >> 10); cbuf[1] = 0xDC00 | ((uc - 0x10000) & 0x3FF); cbuf[2] = 0; } else #endif { cbuf[0] = uc; cbuf[1] = 0; } if (DIRECT_FONT(uc)) { if (c >= ' ' && c != 0x7F) { char buf[4]; WCHAR wbuf[4]; int rv; if (is_dbcs_leadbyte(term->ucsdata->font_codepage, (BYTE) c)) { buf[0] = c; buf[1] = (char) (0xFF & ldata->chars[top.x + 1].chr); rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 2, wbuf, 4); top.x++; } else { buf[0] = c; rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 1, wbuf, 4); } if (rv > 0) { memcpy(cbuf, wbuf, rv * sizeof(wchar_t)); cbuf[rv] = 0; } } } #endif for (p = cbuf; *p; p++) clip_addchar(&buf, *p, attr); if (ldata->chars[x].cc_next) x += ldata->chars[x].cc_next; else break; } top.x++; } if (nl) { int i; for (i = 0; i < sel_nl_sz; i++) clip_addchar(&buf, sel_nl[i], 0); } top.y++; top.x = rect ? old_top_x : 0; unlineptr(ldata); } #if SELECTION_NUL_TERMINATED clip_addchar(&buf, 0, 0); #endif /* Finally, transfer all that to the clipboard. */ write_clip(term->frontend, buf.textbuf, buf.attrbuf, buf.bufpos, desel); sfree(buf.textbuf); sfree(buf.attrbuf); } void term_copyall(Terminal *term) { pos top; pos bottom; tree234 *screen = term->screen; top.y = -sblines(term); top.x = 0; bottom.y = find_last_nonempty_line(term, screen); bottom.x = term->cols; clipme(term, top, bottom, 0, TRUE); } /* * The wordness array is mainly for deciding the disposition of the * US-ASCII characters. */ static int wordtype(Terminal *term, int uc) { struct ucsword { int start, end, ctype; }; static const struct ucsword ucs_words[] = { { 128, 160, 0}, { 161, 191, 1}, { 215, 215, 1}, { 247, 247, 1}, { 0x037e, 0x037e, 1}, /* Greek question mark */ { 0x0387, 0x0387, 1}, /* Greek ano teleia */ { 0x055a, 0x055f, 1}, /* Armenian punctuation */ { 0x0589, 0x0589, 1}, /* Armenian full stop */ { 0x0700, 0x070d, 1}, /* Syriac punctuation */ { 0x104a, 0x104f, 1}, /* Myanmar punctuation */ { 0x10fb, 0x10fb, 1}, /* Georgian punctuation */ { 0x1361, 0x1368, 1}, /* Ethiopic punctuation */ { 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */ { 0x17d4, 0x17dc, 1}, /* Khmer punctuation */ { 0x1800, 0x180a, 1}, /* Mongolian punctuation */ { 0x2000, 0x200a, 0}, /* Various spaces */ { 0x2070, 0x207f, 2}, /* superscript */ { 0x2080, 0x208f, 2}, /* subscript */ { 0x200b, 0x27ff, 1}, /* punctuation and symbols */ { 0x3000, 0x3000, 0}, /* ideographic space */ { 0x3001, 0x3020, 1}, /* ideographic punctuation */ { 0x303f, 0x309f, 3}, /* Hiragana */ { 0x30a0, 0x30ff, 3}, /* Katakana */ { 0x3300, 0x9fff, 3}, /* CJK Ideographs */ { 0xac00, 0xd7a3, 3}, /* Hangul Syllables */ { 0xf900, 0xfaff, 3}, /* CJK Ideographs */ { 0xfe30, 0xfe6b, 1}, /* punctuation forms */ { 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */ { 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */ { 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */ { 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */ { 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */ { 0, 0, 0} }; const struct ucsword *wptr; switch (uc & CSET_MASK) { case CSET_LINEDRW: uc = term->ucsdata->unitab_xterm[uc & 0xFF]; break; case CSET_ASCII: uc = term->ucsdata->unitab_line[uc & 0xFF]; break; case CSET_SCOACS: uc = term->ucsdata->unitab_scoacs[uc&0xFF]; break; } switch (uc & CSET_MASK) { case CSET_ACP: uc = term->ucsdata->unitab_font[uc & 0xFF]; break; case CSET_OEMCP: uc = term->ucsdata->unitab_oemcp[uc & 0xFF]; break; } /* For DBCS fonts I can't do anything useful. Even this will sometimes * fail as there's such a thing as a double width space. :-( */ if (term->ucsdata->dbcs_screenfont && term->ucsdata->font_codepage == term->ucsdata->line_codepage) return (uc != ' '); if (uc < 0x80) return term->wordness[uc]; for (wptr = ucs_words; wptr->start; wptr++) { if (uc >= wptr->start && uc <= wptr->end) return wptr->ctype; } return 2; } /* * Spread the selection outwards according to the selection mode. */ static pos sel_spread_half(Terminal *term, pos p, int dir) { termline *ldata; short wvalue; int topy = -sblines(term); ldata = lineptr(p.y); switch (term->selmode) { case SM_CHAR: /* * In this mode, every character is a separate unit, except * for runs of spaces at the end of a non-wrapping line. */ if (!(ldata->lattr & LATTR_WRAPPED)) { termchar *q = ldata->chars + term->cols; while (q > ldata->chars && IS_SPACE_CHR(q[-1].chr) && !q[-1].cc_next) q--; if (q == ldata->chars + term->cols) q--; if (p.x >= q - ldata->chars) p.x = (dir == -1 ? q - ldata->chars : term->cols - 1); } break; case SM_WORD: /* * In this mode, the units are maximal runs of characters * whose `wordness' has the same value. */ wvalue = wordtype(term, UCSGET(ldata->chars, p.x)); if (dir == +1) { while (1) { int maxcols = (ldata->lattr & LATTR_WRAPPED2 ? term->cols-1 : term->cols); if (p.x < maxcols-1) { if (wordtype(term, UCSGET(ldata->chars, p.x+1)) == wvalue) p.x++; else break; } else { if (p.y+1 < term->rows && (ldata->lattr & LATTR_WRAPPED)) { termline *ldata2; ldata2 = lineptr(p.y+1); if (wordtype(term, UCSGET(ldata2->chars, 0)) == wvalue) { p.x = 0; p.y++; unlineptr(ldata); ldata = ldata2; } else { unlineptr(ldata2); break; } } else break; } } } else { while (1) { if (p.x > 0) { if (wordtype(term, UCSGET(ldata->chars, p.x-1)) == wvalue) p.x--; else break; } else { termline *ldata2; int maxcols; if (p.y <= topy) break; ldata2 = lineptr(p.y-1); maxcols = (ldata2->lattr & LATTR_WRAPPED2 ? term->cols-1 : term->cols); if (ldata2->lattr & LATTR_WRAPPED) { if (wordtype(term, UCSGET(ldata2->chars, maxcols-1)) == wvalue) { p.x = maxcols-1; p.y--; unlineptr(ldata); ldata = ldata2; } else { unlineptr(ldata2); break; } } else break; } } } break; case SM_LINE: /* * In this mode, every line is a unit. */ p.x = (dir == -1 ? 0 : term->cols - 1); break; } unlineptr(ldata); return p; } static void sel_spread(Terminal *term) { if (term->seltype == LEXICOGRAPHIC) { term->selstart = sel_spread_half(term, term->selstart, -1); decpos(term->selend); term->selend = sel_spread_half(term, term->selend, +1); incpos(term->selend); } } static void term_paste_callback(void *vterm) { Terminal *term = (Terminal *)vterm; if (term->paste_len == 0) return; while (term->paste_pos < term->paste_len) { int n = 0; while (n + term->paste_pos < term->paste_len) { if (term->paste_buffer[term->paste_pos + n++] == '\015') break; } if (term->ldisc) luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0); term->paste_pos += n; if (term->paste_pos < term->paste_len) { queue_toplevel_callback(term_paste_callback, term); return; } } sfree(term->paste_buffer); term->paste_buffer = NULL; term->paste_len = 0; } void term_do_paste(Terminal *term) { wchar_t *data; int len; get_clip(term->frontend, &data, &len); if (data && len > 0) { wchar_t *p, *q; term_seen_key_event(term); /* pasted data counts */ if (term->paste_buffer) sfree(term->paste_buffer); term->paste_pos = term->paste_len = 0; term->paste_buffer = snewn(len + 12, wchar_t); if (term->bracketed_paste) { memcpy(term->paste_buffer, L"\033[200~", 6 * sizeof(wchar_t)); term->paste_len += 6; } p = q = data; while (p < data + len) { while (p < data + len && !(p <= data + len - sel_nl_sz && !memcmp(p, sel_nl, sizeof(sel_nl)))) p++; { int i; for (i = 0; i < p - q; i++) { term->paste_buffer[term->paste_len++] = q[i]; } } if (p <= data + len - sel_nl_sz && !memcmp(p, sel_nl, sizeof(sel_nl))) { term->paste_buffer[term->paste_len++] = '\015'; p += sel_nl_sz; } q = p; } if (term->bracketed_paste) { memcpy(term->paste_buffer + term->paste_len, L"\033[201~", 6 * sizeof(wchar_t)); term->paste_len += 6; } /* Assume a small paste will be OK in one go. */ if (term->paste_len < 256) { if (term->ldisc) luni_send(term->ldisc, term->paste_buffer, term->paste_len, 0); if (term->paste_buffer) sfree(term->paste_buffer); term->paste_buffer = 0; term->paste_pos = term->paste_len = 0; } } get_clip(term->frontend, NULL, NULL); queue_toplevel_callback(term_paste_callback, term); } void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, Mouse_Action a, int x, int y, int shift, int ctrl, int alt) { pos selpoint; termline *ldata; int raw_mouse = (term->xterm_mouse && !term->no_mouse_rep && !(term->mouse_override && shift)); int default_seltype; if (y < 0) { y = 0; if (a == MA_DRAG && !raw_mouse) term_scroll(term, 0, -1); } if (y >= term->rows) { y = term->rows - 1; if (a == MA_DRAG && !raw_mouse) term_scroll(term, 0, +1); } if (x < 0) { if (y > 0) { x = term->cols - 1; y--; } else x = 0; } if (x >= term->cols) x = term->cols - 1; selpoint.y = y + term->disptop; ldata = lineptr(selpoint.y); if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) x /= 2; /* * Transform x through the bidi algorithm to find the _logical_ * click point from the physical one. */ if (term_bidi_line(term, ldata, y) != NULL) { x = term->post_bidi_cache[y].backward[x]; } selpoint.x = x; unlineptr(ldata); /* * If we're in the middle of a selection operation, we ignore raw * mouse mode until it's done (we must have been not in raw mouse * mode when it started). * This makes use of Shift for selection reliable, and avoids the * host seeing mouse releases for which they never saw corresponding * presses. */ if (raw_mouse && (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) { int encstate = 0, r, c, wheel; char abuf[32]; int len = 0; if (term->ldisc) { switch (braw) { case MBT_LEFT: encstate = 0x00; /* left button down */ wheel = FALSE; break; case MBT_MIDDLE: encstate = 0x01; wheel = FALSE; break; case MBT_RIGHT: encstate = 0x02; wheel = FALSE; break; case MBT_WHEEL_UP: encstate = 0x40; wheel = TRUE; break; case MBT_WHEEL_DOWN: encstate = 0x41; wheel = TRUE; break; default: return; } if (wheel) { /* For mouse wheel buttons, we only ever expect to see * MA_CLICK actions, and we don't try to keep track of * the buttons being 'pressed' (since without matching * click/release pairs that's pointless). */ if (a != MA_CLICK) return; } else switch (a) { case MA_DRAG: if (term->xterm_mouse == 1) return; encstate += 0x20; break; case MA_RELEASE: /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */ if (!term->xterm_extended_mouse) encstate = 0x03; term->mouse_is_down = 0; break; case MA_CLICK: if (term->mouse_is_down == braw) return; term->mouse_is_down = braw; break; default: return; } if (shift) encstate += 0x04; if (ctrl) encstate += 0x10; r = y + 1; c = x + 1; /* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */ if (term->xterm_extended_mouse) { len = sprintf(abuf, "\033[<%d;%d;%d%c", encstate, c, r, a == MA_RELEASE ? 'm' : 'M'); } else if (term->urxvt_extended_mouse) { len = sprintf(abuf, "\033[%d;%d;%dM", encstate + 32, c, r); } else if (c <= 223 && r <= 223) { len = sprintf(abuf, "\033[M%c%c%c", encstate + 32, c + 32, r + 32); } if (len > 0) ldisc_send(term->ldisc, abuf, len, 0); } return; } /* * Set the selection type (rectangular or normal) at the start * of a selection attempt, from the state of Alt. */ if (!alt ^ !term->rect_select) default_seltype = RECTANGULAR; else default_seltype = LEXICOGRAPHIC; if (term->selstate == NO_SELECTION) { term->seltype = default_seltype; } if (bcooked == MBT_SELECT && a == MA_CLICK) { deselect(term); term->selstate = ABOUT_TO; term->seltype = default_seltype; term->selanchor = selpoint; term->selmode = SM_CHAR; } else if (bcooked == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) { deselect(term); term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE); term->selstate = DRAGGING; term->selstart = term->selanchor = selpoint; term->selend = term->selstart; incpos(term->selend); sel_spread(term); } else if ((bcooked == MBT_SELECT && a == MA_DRAG) || (bcooked == MBT_EXTEND && a != MA_RELEASE)) { if (a == MA_DRAG && (term->selstate == NO_SELECTION || term->selstate == SELECTED)) { /* * This can happen if a front end has passed us a MA_DRAG * without a prior MA_CLICK. OS X GTK does so, for * example, if the initial button press was eaten by the * WM when it activated the window in the first place. The * nicest thing to do in this situation is to ignore * further drags, and wait for the user to click in the * window again properly if they want to select. */ return; } if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint)) return; if (bcooked == MBT_EXTEND && a != MA_DRAG && term->selstate == SELECTED) { if (term->seltype == LEXICOGRAPHIC) { /* * For normal selection, we extend by moving * whichever end of the current selection is closer * to the mouse. */ if (posdiff(selpoint, term->selstart) < posdiff(term->selend, term->selstart) / 2) { term->selanchor = term->selend; decpos(term->selanchor); } else { term->selanchor = term->selstart; } } else { /* * For rectangular selection, we have a choice of * _four_ places to put selanchor and selpoint: the * four corners of the selection. */ if (2*selpoint.x < term->selstart.x + term->selend.x) term->selanchor.x = term->selend.x-1; else term->selanchor.x = term->selstart.x; if (2*selpoint.y < term->selstart.y + term->selend.y) term->selanchor.y = term->selend.y; else term->selanchor.y = term->selstart.y; } term->selstate = DRAGGING; } if (term->selstate != ABOUT_TO && term->selstate != DRAGGING) term->selanchor = selpoint; term->selstate = DRAGGING; if (term->seltype == LEXICOGRAPHIC) { /* * For normal selection, we set (selstart,selend) to * (selpoint,selanchor) in some order. */ if (poslt(selpoint, term->selanchor)) { term->selstart = selpoint; term->selend = term->selanchor; incpos(term->selend); } else { term->selstart = term->selanchor; term->selend = selpoint; incpos(term->selend); } } else { /* * For rectangular selection, we may need to * interchange x and y coordinates (if the user has * dragged in the -x and +y directions, or vice versa). */ term->selstart.x = min(term->selanchor.x, selpoint.x); term->selend.x = 1+max(term->selanchor.x, selpoint.x); term->selstart.y = min(term->selanchor.y, selpoint.y); term->selend.y = max(term->selanchor.y, selpoint.y); } sel_spread(term); } else if ((bcooked == MBT_SELECT || bcooked == MBT_EXTEND) && a == MA_RELEASE) { if (term->selstate == DRAGGING) { /* * We've completed a selection. We now transfer the * data to the clipboard. */ clipme(term, term->selstart, term->selend, (term->seltype == RECTANGULAR), FALSE); term->selstate = SELECTED; } else term->selstate = NO_SELECTION; } else if (bcooked == MBT_PASTE && (a == MA_CLICK #if MULTICLICK_ONLY_EVENT || a == MA_2CLK || a == MA_3CLK #endif )) { request_paste(term->frontend); } /* * Since terminal output is suppressed during drag-selects, we * should make sure to write any pending output if one has just * finished. */ if (term->selstate != DRAGGING) term_out(term); term_update(term); } int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl) { char *p = buf; if (term->vt52_mode) p += sprintf((char *) p, "\x1B%c", xkey); else { int app_flg = (term->app_cursor_keys && !term->no_applic_c); #if 0 /* * RDB: VT100 & VT102 manuals both state the app cursor * keys only work if the app keypad is on. * * SGT: That may well be true, but xterm disagrees and so * does at least one application, so I've #if'ed this out * and the behaviour is back to PuTTY's original: app * cursor and app keypad are independently switchable * modes. If anyone complains about _this_ I'll have to * put in a configurable option. */ if (!term->app_keypad_keys) app_flg = 0; #endif /* Useful mapping of Ctrl-arrows */ if (ctrl) app_flg = !app_flg; if (app_flg) p += sprintf((char *) p, "\x1BO%c", xkey); else p += sprintf((char *) p, "\x1B[%c", xkey); } return p - buf; } void term_nopaste(Terminal *term) { if (term->paste_len == 0) return; sfree(term->paste_buffer); term->paste_buffer = NULL; term->paste_len = 0; } static void deselect(Terminal *term) { term->selstate = NO_SELECTION; term->selstart.x = term->selstart.y = term->selend.x = term->selend.y = 0; } void term_deselect(Terminal *term) { deselect(term); term_update(term); /* * Since terminal output is suppressed during drag-selects, we * should make sure to write any pending output if one has just * finished. */ if (term->selstate != DRAGGING) term_out(term); } int term_ldisc(Terminal *term, int option) { if (option == LD_ECHO) return term->term_echoing; if (option == LD_EDIT) return term->term_editing; return FALSE; } int term_data(Terminal *term, int is_stderr, const char *data, int len) { bufchain_add(&term->inbuf, data, len); if (!term->in_term_out) { term->in_term_out = TRUE; term_reset_cblink(term); /* * During drag-selects, we do not process terminal input, * because the user will want the screen to hold still to * be selected. */ if (term->selstate != DRAGGING) term_out(term); term->in_term_out = FALSE; } /* * term_out() always completely empties inbuf. Therefore, * there's no reason at all to return anything other than zero * from this function, because there _can't_ be a question of * the remote side needing to wait until term_out() has cleared * a backlog. * * This is a slightly suboptimal way to deal with SSH-2 - in * principle, the window mechanism would allow us to continue * to accept data on forwarded ports and X connections even * while the terminal processing was going slowly - but we * can't do the 100% right thing without moving the terminal * processing into a separate thread, and that might hurt * portability. So we manage stdout buffering the old SSH-1 way: * if the terminal processing goes slowly, the whole SSH * connection stops accepting data until it's ready. * * In practice, I can't imagine this causing serious trouble. */ return 0; } /* * Write untrusted data to the terminal. * The only control character that should be honoured is \n (which * will behave as a CRLF). */ int term_data_untrusted(Terminal *term, const char *data, int len) { int i; /* FIXME: more sophisticated checking? */ for (i = 0; i < len; i++) { if (data[i] == '\n') term_data(term, 1, "\r\n", 2); else if (data[i] & 0x60) term_data(term, 1, data + i, 1); } return 0; /* assumes that term_data() always returns 0 */ } void term_provide_logctx(Terminal *term, void *logctx) { term->logctx = logctx; } void term_set_focus(Terminal *term, int has_focus) { term->has_focus = has_focus; term_schedule_cblink(term); } /* * Provide "auto" settings for remote tty modes, suitable for an * application with a terminal window. */ char *term_get_ttymode(Terminal *term, const char *mode) { const char *val = NULL; if (strcmp(mode, "ERASE") == 0) { val = term->bksp_is_delete ? "^?" : "^H"; } else if (strcmp(mode, "IUTF8") == 0) { val = frontend_is_utf8(term->frontend) ? "yes" : "no"; } /* FIXME: perhaps we should set ONLCR based on lfhascr as well? */ /* FIXME: or ECHO and friends based on local echo state? */ return dupstr(val); } struct term_userpass_state { size_t curr_prompt; int done_prompt; /* printed out prompt yet? */ size_t pos; /* cursor position */ }; /* * Process some terminal data in the course of username/password * input. */ int term_get_userpass_input(Terminal *term, prompts_t *p, const unsigned char *in, int inlen) { struct term_userpass_state *s = (struct term_userpass_state *)p->data; if (!s) { /* * First call. Set some stuff up. */ p->data = s = snew(struct term_userpass_state); s->curr_prompt = 0; s->done_prompt = 0; /* We only print the `name' caption if we have to... */ if (p->name_reqd && p->name) { size_t l = strlen(p->name); term_data_untrusted(term, p->name, l); if (p->name[l-1] != '\n') term_data_untrusted(term, "\n", 1); } /* ...but we always print any `instruction'. */ if (p->instruction) { size_t l = strlen(p->instruction); term_data_untrusted(term, p->instruction, l); if (p->instruction[l-1] != '\n') term_data_untrusted(term, "\n", 1); } /* * Zero all the results, in case we abort half-way through. */ { int i; for (i = 0; i < (int)p->n_prompts; i++) prompt_set_result(p->prompts[i], ""); } } while (s->curr_prompt < p->n_prompts) { prompt_t *pr = p->prompts[s->curr_prompt]; int finished_prompt = 0; if (!s->done_prompt) { term_data_untrusted(term, pr->prompt, strlen(pr->prompt)); s->done_prompt = 1; s->pos = 0; } /* Breaking out here ensures that the prompt is printed even * if we're now waiting for user data. */ if (!in || !inlen) break; /* FIXME: should we be using local-line-editing code instead? */ while (!finished_prompt && inlen) { char c = *in++; inlen--; switch (c) { case 10: case 13: term_data(term, 0, "\r\n", 2); prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos] = '\0'; /* go to next prompt, if any */ s->curr_prompt++; s->done_prompt = 0; finished_prompt = 1; /* break out */ break; case 8: case 127: if (s->pos > 0) { if (pr->echo) term_data(term, 0, "\b \b", 3); s->pos--; } break; case 21: case 27: while (s->pos > 0) { if (pr->echo) term_data(term, 0, "\b \b", 3); s->pos--; } break; case 3: case 4: /* Immediate abort. */ term_data(term, 0, "\r\n", 2); sfree(s); p->data = NULL; return 0; /* user abort */ default: /* * This simplistic check for printability is disabled * when we're doing password input, because some people * have control characters in their passwords. */ if (!pr->echo || (c >= ' ' && c <= '~') || ((unsigned char) c >= 160)) { prompt_ensure_result_size(pr, s->pos + 1); pr->result[s->pos++] = c; if (pr->echo) term_data(term, 0, &c, 1); } break; } } } if (s->curr_prompt < p->n_prompts) { return -1; /* more data required */ } else { sfree(s); p->data = NULL; return +1; /* all done */ } } its-playback-time-0.2017-08-30.3c40fd3/terminal.h000066400000000000000000000225311333656753000206360ustar00rootroot00000000000000/* * Internals of the Terminal structure, for those other modules * which need to look inside it. It would be nice if this could be * folded back into terminal.c in future, with an abstraction layer * to handle everything that other modules need to know about it; * but for the moment, this will do. */ #ifndef PUTTY_TERMINAL_H #define PUTTY_TERMINAL_H #include "tree234.h" struct beeptime { struct beeptime *next; unsigned long ticks; }; typedef struct { int y, x; } pos; #ifdef OPTIMISE_SCROLL struct scrollregion { struct scrollregion *next; int topline; /* Top line of scroll region. */ int botline; /* Bottom line of scroll region. */ int lines; /* Number of lines to scroll by - +ve is forwards. */ }; #endif /* OPTIMISE_SCROLL */ typedef struct termchar termchar; typedef struct termline termline; struct termchar { /* * Any code in terminal.c which definitely needs to be changed * when extra fields are added here is labelled with a comment * saying FULL-TERMCHAR. */ unsigned long chr; unsigned long attr; /* * The cc_next field is used to link multiple termchars * together into a list, so as to fit more than one character * into a character cell (Unicode combining characters). * * cc_next is a relative offset into the current array of * termchars. I.e. to advance to the next character in a list, * one does `tc += tc->next'. * * Zero means end of list. */ int cc_next; }; struct termline { unsigned short lattr; int cols; /* number of real columns on the line */ int size; /* number of allocated termchars * (cc-lists may make this > cols) */ int temporary; /* TRUE if decompressed from scrollback */ int cc_free; /* offset to first cc in free list */ struct termchar *chars; }; struct bidi_cache_entry { int width; struct termchar *chars; int *forward, *backward; /* the permutations of line positions */ }; struct terminal_tag { int compatibility_level; tree234 *scrollback; /* lines scrolled off top of screen */ tree234 *screen; /* lines on primary screen */ tree234 *alt_screen; /* lines on alternate screen */ int disptop; /* distance scrolled back (0 or -ve) */ int tempsblines; /* number of lines of .scrollback that can be retrieved onto the terminal ("temporary scrollback") */ termline **disptext; /* buffer of text on real screen */ int dispcursx, dispcursy; /* location of cursor on real screen */ int curstype; /* type of cursor on real screen */ #define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */ struct beeptime *beephead, *beeptail; int nbeeps; int beep_overloaded; long lastbeep; #define TTYPE termchar #define TSIZE (sizeof(TTYPE)) #ifdef OPTIMISE_SCROLL struct scrollregion *scrollhead, *scrolltail; #endif /* OPTIMISE_SCROLL */ int default_attr, curr_attr, save_attr; termchar basic_erase_char, erase_char; bufchain inbuf; /* terminal input buffer */ pos curs; /* cursor */ pos savecurs; /* saved cursor position */ int marg_t, marg_b; /* scroll margins */ int dec_om; /* DEC origin mode flag */ int wrap, wrapnext; /* wrap flags */ int insert; /* insert-mode flag */ int cset; /* 0 or 1: which char set */ int save_cset, save_csattr; /* saved with cursor position */ int save_utf, save_wnext; /* saved with cursor position */ int rvideo; /* global reverse video flag */ unsigned long rvbell_startpoint; /* for ESC[?5hESC[?5l vbell */ int cursor_on; /* cursor enabled flag */ int reset_132; /* Flag ESC c resets to 80 cols */ int use_bce; /* Use Background coloured erase */ int cblinker; /* When blinking is the cursor on ? */ int tblinker; /* When the blinking text is on */ int blink_is_real; /* Actually blink blinking text */ int term_echoing; /* Does terminal want local echo? */ int term_editing; /* Does terminal want local edit? */ int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */ int vt52_bold; /* Force bold on non-bold colours */ int utf; /* Are we in toggleable UTF-8 mode? */ int utf_state; /* Is there a pending UTF-8 character */ int utf_char; /* and what is it so far. */ int utf_size; /* The size of the UTF character. */ int printing, only_printing; /* Are we doing ANSI printing? */ int print_state; /* state of print-end-sequence scan */ bufchain printer_buf; /* buffered data for printer */ printer_job *print_job; /* ESC 7 saved state for the alternate screen */ pos alt_savecurs; int alt_save_attr; int alt_save_cset, alt_save_csattr; int alt_save_utf, alt_save_wnext; int alt_save_sco_acs; int rows, cols, savelines; int has_focus; int in_vbell; long vbell_end; int app_cursor_keys, app_keypad_keys, vt52_mode; int repeat_off, cr_lf_return; int seen_disp_event; int big_cursor; int xterm_mouse; /* send mouse messages to host */ int xterm_extended_mouse; int urxvt_extended_mouse; int mouse_is_down; /* used while tracking mouse buttons */ int bracketed_paste; int cset_attr[2]; /* * Saved settings on the alternate screen. */ int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins; int alt_cset, alt_sco_acs, alt_utf; int alt_t, alt_b; int alt_which; int alt_sblines; /* # of lines on alternate screen that should be used for scrollback. */ #define ARGS_MAX 32 /* max # of esc sequence arguments */ #define ARG_DEFAULT 0 /* if an arg isn't specified */ #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) ) unsigned esc_args[ARGS_MAX]; int esc_nargs; int esc_query; #define ANSI(x,y) ((x)+((y)<<8)) #define ANSI_QUE(x) ANSI(x,TRUE) #define OSC_STR_MAX 2048 int osc_strlen; char osc_string[OSC_STR_MAX + 1]; int osc_w; char id_string[1024]; unsigned char *tabs; enum { TOPLEVEL, SEEN_ESC, SEEN_CSI, SEEN_OSC, SEEN_OSC_W, DO_CTRLS, SEEN_OSC_P, OSC_STRING, OSC_MAYBE_ST, VT52_ESC, VT52_Y1, VT52_Y2, VT52_FG, VT52_BG } termstate; enum { NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED } selstate; enum { LEXICOGRAPHIC, RECTANGULAR } seltype; enum { SM_CHAR, SM_WORD, SM_LINE } selmode; pos selstart, selend, selanchor; short wordness[256]; /* Mask of attributes to pay attention to when painting. */ int attr_mask; wchar_t *paste_buffer; int paste_len, paste_pos; void (*resize_fn)(void *, int, int); void *resize_ctx; void *ldisc; void *frontend; void *logctx; struct unicode_data *ucsdata; /* * We maintain a full copy of a Conf here, not merely a pointer * to it. That way, when we're passed a new one for * reconfiguration, we can check the differences and adjust the * _current_ setting of (e.g.) auto wrap mode rather than only * the default. */ Conf *conf; /* * from_backend calls term_out, but it can also be called from * the ldisc if the ldisc is called _within_ term_out. So we * have to guard against re-entrancy - if from_backend is * called recursively like this, it will simply add data to the * end of the buffer term_out is in the process of working * through. */ int in_term_out; /* * We schedule a window update shortly after receiving terminal * data. This tracks whether one is currently pending. */ int window_update_pending; long next_update; /* * Track pending blinks and tblinks. */ int tblink_pending, cblink_pending; long next_tblink, next_cblink; /* * These are buffers used by the bidi and Arabic shaping code. */ termchar *ltemp; int ltemp_size; bidi_char *wcFrom, *wcTo; int wcFromTo_size; struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache; int bidi_cache_size; /* * We copy a bunch of stuff out of the Conf structure into local * fields in the Terminal structure, to avoid the repeated * tree234 lookups which would be involved in fetching them from * the former every time. */ int ansi_colour; char *answerback; int answerbacklen; int arabicshaping; int beep; int bellovl; int bellovl_n; int bellovl_s; int bellovl_t; int bidi; int bksp_is_delete; int blink_cur; int blinktext; int cjk_ambig_wide; int conf_height; int conf_width; int crhaslf; int erase_to_scrollback; int funky_type; int lfhascr; int logflush; int logtype; int mouse_override; int nethack_keypad; int no_alt_screen; int no_applic_c; int no_applic_k; int no_dbackspace; int no_mouse_rep; int no_remote_charset; int no_remote_resize; int no_remote_wintitle; int no_remote_clearscroll; int rawcnp; int rect_select; int remote_qtitle_action; int rxvt_homeend; int scroll_on_disp; int scroll_on_key; int xterm_256_colour; }; #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8) #endif its-playback-time-0.2017-08-30.3c40fd3/time.c000066400000000000000000000005561333656753000177570ustar00rootroot00000000000000/* * 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 #include struct tm ltime(void) { time_t t; time(&t); assert (t != ((time_t)-1)); return *localtime(&t); } its-playback-time-0.2017-08-30.3c40fd3/toucs.c000066400000000000000000000037041333656753000201540ustar00rootroot00000000000000/* * toucs.c - convert charsets to Unicode. */ #include "charset.h" #include "internal.h" struct unicode_emit_param { wchar_t *output; int outlen; const wchar_t *errstr; int errlen; int stopped; }; static void unicode_emit(void *ctx, long int output) { struct unicode_emit_param *param = (struct unicode_emit_param *)ctx; wchar_t outval; wchar_t const *p; int outlen; if (output == ERROR) { if (param->errstr) { p = param->errstr; outlen = param->errlen; } else { outval = 0xFFFD; /* U+FFFD REPLACEMENT CHARACTER */ p = &outval; outlen = 1; } } else { outval = output; p = &outval; outlen = 1; } if (param->outlen >= outlen) { while (outlen > 0) { *param->output++ = *p++; param->outlen--; outlen--; } } else { param->stopped = 1; } } int charset_to_unicode(const char **input, int *inlen, wchar_t *output, int outlen, int charset, charset_state *state, const wchar_t *errstr, int errlen) { charset_spec const *spec = charset_find_spec(charset); charset_state localstate; struct unicode_emit_param param; param.output = output; param.outlen = outlen; param.errstr = errstr; param.errlen = errlen; param.stopped = 0; if (!state) { localstate.s0 = 0; } else { localstate = *state; /* structure copy */ } while (*inlen > 0) { int lenbefore = param.output - output; spec->read(spec, (unsigned char)**input, &localstate, unicode_emit, ¶m); if (param.stopped) { /* * The emit function has _tried_ to output some * characters, but ran up against the end of the * buffer. Leave immediately, and return what happened * _before_ attempting to process this character. */ return lenbefore; } if (state) *state = localstate; /* structure copy */ (*input)++; (*inlen)--; } return param.output - output; } its-playback-time-0.2017-08-30.3c40fd3/tree234.c000066400000000000000000001164241333656753000202130ustar00rootroot00000000000000/* * tree234.c: reasonably generic counted 2-3-4 tree routines. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "tree234.h" #ifdef TEST #define LOG(x) (printf x) #define snew(type) ((type *)malloc(sizeof(type))) #define snewn(n, type) ((type *)malloc((n) * sizeof(type))) #define sresize(ptr, n, type) \ ((type *)realloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr), \ (n) * sizeof(type))) #define sfree(ptr) free(ptr) #else #include "puttymem.h" #define LOG(x) #endif typedef struct node234_Tag node234; struct tree234_Tag { node234 *root; cmpfn234 cmp; }; struct node234_Tag { node234 *parent; node234 *kids[4]; int counts[4]; void *elems[3]; }; /* * Create a 2-3-4 tree. */ tree234 *newtree234(cmpfn234 cmp) { tree234 *ret = snew(tree234); LOG(("created tree %p\n", ret)); ret->root = NULL; ret->cmp = cmp; return ret; } /* * Free a 2-3-4 tree (not including freeing the elements). */ static void freenode234(node234 * n) { if (!n) return; freenode234(n->kids[0]); freenode234(n->kids[1]); freenode234(n->kids[2]); freenode234(n->kids[3]); sfree(n); } void freetree234(tree234 * t) { freenode234(t->root); sfree(t); } /* * Internal function to count a node. */ static int countnode234(node234 * n) { int count = 0; int i; if (!n) return 0; for (i = 0; i < 4; i++) count += n->counts[i]; for (i = 0; i < 3; i++) if (n->elems[i]) count++; return count; } /* * Count the elements in a tree. */ int count234(tree234 * t) { if (t->root) return countnode234(t->root); else return 0; } /* * Add an element e to a 2-3-4 tree t. Returns e on success, or if * an existing element compares equal, returns that. */ static void *add234_internal(tree234 * t, void *e, int index) { node234 *n, **np, *left, *right; void *orig_e = e; int c, lcount, rcount; LOG(("adding node %p to tree %p\n", e, t)); if (t->root == NULL) { t->root = snew(node234); t->root->elems[1] = t->root->elems[2] = NULL; t->root->kids[0] = t->root->kids[1] = NULL; t->root->kids[2] = t->root->kids[3] = NULL; t->root->counts[0] = t->root->counts[1] = 0; t->root->counts[2] = t->root->counts[3] = 0; t->root->parent = NULL; t->root->elems[0] = e; LOG((" created root %p\n", t->root)); return orig_e; } n = NULL; /* placate gcc; will always be set below since t->root != NULL */ np = &t->root; while (*np) { int childnum; n = *np; LOG((" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); if (index >= 0) { if (!n->kids[0]) { /* * Leaf node. We want to insert at kid position * equal to the index: * * 0 A 1 B 2 C 3 */ childnum = index; } else { /* * Internal node. We always descend through it (add * always starts at the bottom, never in the * middle). */ do { /* this is a do ... while (0) to allow `break' */ if (index <= n->counts[0]) { childnum = 0; break; } index -= n->counts[0] + 1; if (index <= n->counts[1]) { childnum = 1; break; } index -= n->counts[1] + 1; if (index <= n->counts[2]) { childnum = 2; break; } index -= n->counts[2] + 1; if (index <= n->counts[3]) { childnum = 3; break; } return NULL; /* error: index out of range */ } while (0); } } else { if ((c = t->cmp(e, n->elems[0])) < 0) childnum = 0; else if (c == 0) return n->elems[0]; /* already exists */ else if (n->elems[1] == NULL || (c = t->cmp(e, n->elems[1])) < 0) childnum = 1; else if (c == 0) return n->elems[1]; /* already exists */ else if (n->elems[2] == NULL || (c = t->cmp(e, n->elems[2])) < 0) childnum = 2; else if (c == 0) return n->elems[2]; /* already exists */ else childnum = 3; } np = &n->kids[childnum]; LOG((" moving to child %d (%p)\n", childnum, *np)); } /* * We need to insert the new element in n at position np. */ left = NULL; lcount = 0; right = NULL; rcount = 0; while (n) { LOG((" at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" need to insert %p/%d [%p] %p/%d at position %d\n", left, lcount, e, right, rcount, (int)(np - n->kids))); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. */ if (np == &n->kids[0]) { LOG((" inserting on left of 2-node\n")); n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else { /* np == &n->kids[1] */ LOG((" inserting on right of 2-node\n")); n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; LOG((" done\n")); break; } else if (n->elems[2] == NULL) { /* * Insert in a 3-node; simple. */ if (np == &n->kids[0]) { LOG((" inserting on left of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else if (np == &n->kids[1]) { LOG((" inserting in middle of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } else { /* np == &n->kids[2] */ LOG((" inserting on right of 3-node\n")); n->kids[3] = right; n->counts[3] = rcount; n->elems[2] = e; n->kids[2] = left; n->counts[2] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; if (n->kids[3]) n->kids[3]->parent = n; LOG((" done\n")); break; } else { node234 *m = snew(node234); m->parent = n->parent; LOG((" splitting a 4-node; created new node %p\n", m)); /* * Insert in a 4-node; split into a 2-node and a * 3-node, and move focus up a level. * * I don't think it matters which way round we put the * 2 and the 3. For simplicity, we'll put the 3 first * always. */ if (np == &n->kids[0]) { m->kids[0] = left; m->counts[0] = lcount; m->elems[0] = e; m->kids[1] = right; m->counts[1] = rcount; m->elems[1] = n->elems[0]; m->kids[2] = n->kids[1]; m->counts[2] = n->counts[1]; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[1]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = left; m->counts[1] = lcount; m->elems[1] = e; m->kids[2] = right; m->counts[2] = rcount; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[2]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = left; m->counts[2] = lcount; /* e = e; */ n->kids[0] = right; n->counts[0] = rcount; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else { /* np == &n->kids[3] */ m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = n->kids[2]; m->counts[2] = n->counts[2]; n->kids[0] = left; n->counts[0] = lcount; n->elems[0] = e; n->kids[1] = right; n->counts[1] = rcount; e = n->elems[2]; } m->kids[3] = n->kids[3] = n->kids[2] = NULL; m->counts[3] = n->counts[3] = n->counts[2] = 0; m->elems[2] = n->elems[2] = n->elems[1] = NULL; if (m->kids[0]) m->kids[0]->parent = m; if (m->kids[1]) m->kids[1]->parent = m; if (m->kids[2]) m->kids[2]->parent = m; if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; LOG((" left (%p): %p/%d [%p] %p/%d [%p] %p/%d\n", m, m->kids[0], m->counts[0], m->elems[0], m->kids[1], m->counts[1], m->elems[1], m->kids[2], m->counts[2])); LOG((" right (%p): %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1])); left = m; lcount = countnode234(left); right = n; rcount = countnode234(right); } if (n->parent) np = (n->parent->kids[0] == n ? &n->parent->kids[0] : n->parent->kids[1] == n ? &n->parent->kids[1] : n->parent->kids[2] == n ? &n->parent->kids[2] : &n->parent->kids[3]); n = n->parent; } /* * If we've come out of here by `break', n will still be * non-NULL and all we need to do is go back up the tree * updating counts. If we've come here because n is NULL, we * need to create a new root for the tree because the old one * has just split into two. */ if (n) { while (n->parent) { int count = countnode234(n); int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum] = count; n = n->parent; } } else { LOG((" root is overloaded, split into two\n")); t->root = snew(node234); t->root->kids[0] = left; t->root->counts[0] = lcount; t->root->elems[0] = e; t->root->kids[1] = right; t->root->counts[1] = rcount; t->root->elems[1] = NULL; t->root->kids[2] = NULL; t->root->counts[2] = 0; t->root->elems[2] = NULL; t->root->kids[3] = NULL; t->root->counts[3] = 0; t->root->parent = NULL; if (t->root->kids[0]) t->root->kids[0]->parent = t->root; if (t->root->kids[1]) t->root->kids[1]->parent = t->root; LOG((" new root is %p/%d [%p] %p/%d\n", t->root->kids[0], t->root->counts[0], t->root->elems[0], t->root->kids[1], t->root->counts[1])); } return orig_e; } void *add234(tree234 * t, void *e) { if (!t->cmp) /* tree is unsorted */ return NULL; return add234_internal(t, e, -1); } void *addpos234(tree234 * t, void *e, int index) { if (index < 0 || /* index out of range */ t->cmp) /* tree is sorted */ return NULL; /* return failure */ return add234_internal(t, e, index); /* this checks the upper bound */ } /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. */ void *index234(tree234 * t, int index) { node234 *n; if (!t->root) return NULL; /* tree is empty */ if (index < 0 || index >= countnode234(t->root)) return NULL; /* out of range */ n = t->root; while (n) { if (index < n->counts[0]) n = n->kids[0]; else if (index -= n->counts[0] + 1, index < 0) return n->elems[0]; else if (index < n->counts[1]) n = n->kids[1]; else if (index -= n->counts[1] + 1, index < 0) return n->elems[1]; else if (index < n->counts[2]) n = n->kids[2]; else if (index -= n->counts[2] + 1, index < 0) return n->elems[2]; else n = n->kids[3]; } /* We shouldn't ever get here. I wonder how we did. */ return NULL; } /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. */ void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp, int relation, int *index) { node234 *n; void *ret; int c; int idx, ecount, kcount, cmpret; if (t->root == NULL) return NULL; if (cmp == NULL) cmp = t->cmp; n = t->root; /* * Attempt to find the element itself. */ idx = 0; ecount = -1; /* * Prepare a fake `cmp' result if e is NULL. */ cmpret = 0; if (e == NULL) { assert(relation == REL234_LT || relation == REL234_GT); if (relation == REL234_LT) cmpret = +1; /* e is a max: always greater */ else if (relation == REL234_GT) cmpret = -1; /* e is a min: always smaller */ } while (1) { for (kcount = 0; kcount < 4; kcount++) { if (kcount >= 3 || n->elems[kcount] == NULL || (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) { break; } if (n->kids[kcount]) idx += n->counts[kcount]; if (c == 0) { ecount = kcount; break; } idx++; } if (ecount >= 0) break; if (n->kids[kcount]) n = n->kids[kcount]; else break; } if (ecount >= 0) { /* * We have found the element we're looking for. It's * n->elems[ecount], at tree index idx. If our search * relation is EQ, LE or GE we can now go home. */ if (relation != REL234_LT && relation != REL234_GT) { if (index) *index = idx; return n->elems[ecount]; } /* * Otherwise, we'll do an indexed lookup for the previous * or next element. (It would be perfectly possible to * implement these search types in a non-counted tree by * going back up from where we are, but far more fiddly.) */ if (relation == REL234_LT) idx--; else idx++; } else { /* * We've found our way to the bottom of the tree and we * know where we would insert this node if we wanted to: * we'd put it in in place of the (empty) subtree * n->kids[kcount], and it would have index idx * * But the actual element isn't there. So if our search * relation is EQ, we're doomed. */ if (relation == REL234_EQ) return NULL; /* * Otherwise, we must do an index lookup for index idx-1 * (if we're going left - LE or LT) or index idx (if we're * going right - GE or GT). */ if (relation == REL234_LT || relation == REL234_LE) { idx--; } } /* * We know the index of the element we want; just call index234 * to do the rest. This will return NULL if the index is out of * bounds, which is exactly what we want. */ ret = index234(t, idx); if (ret && index) *index = idx; return ret; } void *find234(tree234 * t, void *e, cmpfn234 cmp) { return findrelpos234(t, e, cmp, REL234_EQ, NULL); } void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation) { return findrelpos234(t, e, cmp, relation, NULL); } void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index) { return findrelpos234(t, e, cmp, REL234_EQ, index); } /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. */ static void *delpos234_internal(tree234 * t, int index) { node234 *n; void *retval; int ei = -1; retval = 0; n = t->root; LOG(("deleting item %d from tree %p\n", index, t)); while (1) { while (n) { int ki; node234 *sub; LOG( (" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3], index)); if (index < n->counts[0]) { ki = 0; } else if (index -= n->counts[0] + 1, index < 0) { ei = 0; break; } else if (index < n->counts[1]) { ki = 1; } else if (index -= n->counts[1] + 1, index < 0) { ei = 1; break; } else if (index < n->counts[2]) { ki = 2; } else if (index -= n->counts[2] + 1, index < 0) { ei = 2; break; } else { ki = 3; } /* * Recurse down to subtree ki. If it has only one element, * we have to do some transformation to start with. */ LOG((" moving to subtree %d\n", ki)); sub = n->kids[ki]; if (!sub->elems[1]) { LOG((" subtree has only one element!\n")); if (ki > 0 && n->kids[ki - 1]->elems[1]) { /* * Case 3a, left-handed variant. Child ki has * only one element, but child ki-1 has two or * more. So we need to move a subtree from ki-1 * to ki. * * . C . . B . * / \ -> / \ * [more] a A b B c d D e [more] a A b c C d D e */ node234 *sib = n->kids[ki - 1]; int lastelem = (sib->elems[2] ? 2 : sib->elems[1] ? 1 : 0); sub->kids[2] = sub->kids[1]; sub->counts[2] = sub->counts[1]; sub->elems[1] = sub->elems[0]; sub->kids[1] = sub->kids[0]; sub->counts[1] = sub->counts[0]; sub->elems[0] = n->elems[ki - 1]; sub->kids[0] = sib->kids[lastelem + 1]; sub->counts[0] = sib->counts[lastelem + 1]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->elems[ki - 1] = sib->elems[lastelem]; sib->kids[lastelem + 1] = NULL; sib->counts[lastelem + 1] = 0; sib->elems[lastelem] = NULL; n->counts[ki] = countnode234(sub); LOG((" case 3a left\n")); LOG( (" index and left subtree count before adjustment: %d, %d\n", index, n->counts[ki - 1])); index += n->counts[ki - 1]; n->counts[ki - 1] = countnode234(sib); index -= n->counts[ki - 1]; LOG( (" index and left subtree count after adjustment: %d, %d\n", index, n->counts[ki - 1])); } else if (ki < 3 && n->kids[ki + 1] && n->kids[ki + 1]->elems[1]) { /* * Case 3a, right-handed variant. ki has only * one element but ki+1 has two or more. Move a * subtree from ki+1 to ki. * * . B . . C . * / \ -> / \ * a A b c C d D e [more] a A b B c d D e [more] */ node234 *sib = n->kids[ki + 1]; int j; sub->elems[1] = n->elems[ki]; sub->kids[2] = sib->kids[0]; sub->counts[2] = sib->counts[0]; if (sub->kids[2]) sub->kids[2]->parent = sub; n->elems[ki] = sib->elems[0]; sib->kids[0] = sib->kids[1]; sib->counts[0] = sib->counts[1]; for (j = 0; j < 2 && sib->elems[j + 1]; j++) { sib->kids[j + 1] = sib->kids[j + 2]; sib->counts[j + 1] = sib->counts[j + 2]; sib->elems[j] = sib->elems[j + 1]; } sib->kids[j + 1] = NULL; sib->counts[j + 1] = 0; sib->elems[j] = NULL; n->counts[ki] = countnode234(sub); n->counts[ki + 1] = countnode234(sib); LOG((" case 3a right\n")); } else { /* * Case 3b. ki has only one element, and has no * neighbour with more than one. So pick a * neighbour and merge it with ki, taking an * element down from n to go in the middle. * * . B . . * / \ -> | * a A b c C d a A b B c C d * * (Since at all points we have avoided * descending to a node with only one element, * we can be sure that n is not reduced to * nothingness by this move, _unless_ it was * the very first node, ie the root of the * tree. In that case we remove the now-empty * root and replace it with its single large * child as shown.) */ node234 *sib; int j; if (ki > 0) { ki--; index += n->counts[ki] + 1; } sib = n->kids[ki]; sub = n->kids[ki + 1]; sub->kids[3] = sub->kids[1]; sub->counts[3] = sub->counts[1]; sub->elems[2] = sub->elems[0]; sub->kids[2] = sub->kids[0]; sub->counts[2] = sub->counts[0]; sub->elems[1] = n->elems[ki]; sub->kids[1] = sib->kids[1]; sub->counts[1] = sib->counts[1]; if (sub->kids[1]) sub->kids[1]->parent = sub; sub->elems[0] = sib->elems[0]; sub->kids[0] = sib->kids[0]; sub->counts[0] = sib->counts[0]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->counts[ki + 1] = countnode234(sub); sfree(sib); /* * That's built the big node in sub. Now we * need to remove the reference to sib in n. */ for (j = ki; j < 3 && n->kids[j + 1]; j++) { n->kids[j] = n->kids[j + 1]; n->counts[j] = n->counts[j + 1]; n->elems[j] = j < 2 ? n->elems[j + 1] : NULL; } n->kids[j] = NULL; n->counts[j] = 0; if (j < 3) n->elems[j] = NULL; LOG((" case 3b ki=%d\n", ki)); if (!n->elems[0]) { /* * The root is empty and needs to be * removed. */ LOG((" shifting root!\n")); t->root = sub; sub->parent = NULL; sfree(n); } } } n = sub; } if (!retval) retval = n->elems[ei]; if (ei == -1) return NULL; /* although this shouldn't happen */ /* * Treat special case: this is the one remaining item in * the tree. n is the tree root (no parent), has one * element (no elems[1]), and has no kids (no kids[0]). */ if (!n->parent && !n->elems[1] && !n->kids[0]) { LOG((" removed last element in tree\n")); sfree(n); t->root = NULL; return retval; } /* * Now we have the element we want, as n->elems[ei], and we * have also arranged for that element not to be the only * one in its node. So... */ if (!n->kids[0] && n->elems[1]) { /* * Case 1. n is a leaf node with more than one element, * so it's _really easy_. Just delete the thing and * we're done. */ int i; LOG((" case 1\n")); for (i = ei; i < 2 && n->elems[i + 1]; i++) n->elems[i] = n->elems[i + 1]; n->elems[i] = NULL; /* * Having done that to the leaf node, we now go back up * the tree fixing the counts. */ while (n->parent) { int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum]--; n = n->parent; } return retval; /* finished! */ } else if (n->kids[ei]->elems[1]) { /* * Case 2a. n is an internal node, and the root of the * subtree to the left of e has more than one element. * So find the predecessor p to e (ie the largest node * in that subtree), place it where e currently is, and * then start the deletion process over again on the * subtree with p as target. */ node234 *m = n->kids[ei]; void *target; LOG((" case 2a\n")); while (m->kids[0]) { m = (m->kids[3] ? m->kids[3] : m->kids[2] ? m->kids[2] : m->kids[1] ? m->kids[1] : m->kids[0]); } target = (m->elems[2] ? m->elems[2] : m->elems[1] ? m->elems[1] : m->elems[0]); n->elems[ei] = target; index = n->counts[ei] - 1; n = n->kids[ei]; } else if (n->kids[ei + 1]->elems[1]) { /* * Case 2b, symmetric to 2a but s/left/right/ and * s/predecessor/successor/. (And s/largest/smallest/). */ node234 *m = n->kids[ei + 1]; void *target; LOG((" case 2b\n")); while (m->kids[0]) { m = m->kids[0]; } target = m->elems[0]; n->elems[ei] = target; n = n->kids[ei + 1]; index = 0; } else { /* * Case 2c. n is an internal node, and the subtrees to * the left and right of e both have only one element. * So combine the two subnodes into a single big node * with their own elements on the left and right and e * in the middle, then restart the deletion process on * that subtree, with e still as target. */ node234 *a = n->kids[ei], *b = n->kids[ei + 1]; int j; LOG((" case 2c\n")); a->elems[1] = n->elems[ei]; a->kids[2] = b->kids[0]; a->counts[2] = b->counts[0]; if (a->kids[2]) a->kids[2]->parent = a; a->elems[2] = b->elems[0]; a->kids[3] = b->kids[1]; a->counts[3] = b->counts[1]; if (a->kids[3]) a->kids[3]->parent = a; sfree(b); n->counts[ei] = countnode234(a); /* * That's built the big node in a, and destroyed b. Now * remove the reference to b (and e) in n. */ for (j = ei; j < 2 && n->elems[j + 1]; j++) { n->elems[j] = n->elems[j + 1]; n->kids[j + 1] = n->kids[j + 2]; n->counts[j + 1] = n->counts[j + 2]; } n->elems[j] = NULL; n->kids[j + 1] = NULL; n->counts[j + 1] = 0; /* * It's possible, in this case, that we've just removed * the only element in the root of the tree. If so, * shift the root. */ if (n->elems[0] == NULL) { LOG((" shifting root!\n")); t->root = a; a->parent = NULL; sfree(n); } /* * Now go round the deletion process again, with n * pointing at the new big node and e still the same. */ n = a; index = a->counts[0] + a->counts[1] + 1; } } } void *delpos234(tree234 * t, int index) { if (index < 0 || index >= countnode234(t->root)) return NULL; return delpos234_internal(t, index); } void *del234(tree234 * t, void *e) { int index; if (!findrelpos234(t, e, NULL, REL234_EQ, &index)) return NULL; /* it wasn't in there anyway */ return delpos234_internal(t, index); /* it's there; delete it. */ } #ifdef TEST /* * Test code for the 2-3-4 tree. This code maintains an alternative * representation of the data in the tree, in an array (using the * obvious and slow insert and delete functions). After each tree * operation, the verify() function is called, which ensures all * the tree properties are preserved: * - node->child->parent always equals node * - tree->root->parent always equals NULL * - number of kids == 0 or number of elements + 1; * - tree has the same depth everywhere * - every node has at least one element * - subtree element counts are accurate * - any NULL kid pointer is accompanied by a zero count * - in a sorted tree: ordering property between elements of a * node and elements of its children is preserved * and also ensures the list represented by the tree is the same * list it should be. (This last check also doubly verifies the * ordering properties, because the `same list it should be' is by * definition correctly ordered. It also ensures all nodes are * distinct, because the enum functions would get caught in a loop * if not.) */ #include /* * Error reporting function. */ void error(char *fmt, ...) { va_list ap; printf("ERROR: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); } /* The array representation of the data. */ void **array; int arraylen, arraysize; cmpfn234 cmp; /* The tree representation of the same data. */ tree234 *tree; typedef struct { int treedepth; int elemcount; } chkctx; int chknode(chkctx * ctx, int level, node234 * node, void *lowbound, void *highbound) { int nkids, nelems; int i; int count; /* Count the non-NULL kids. */ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++); /* Ensure no kids beyond the first NULL are non-NULL. */ for (i = nkids; i < 4; i++) if (node->kids[i]) { error("node %p: nkids=%d but kids[%d] non-NULL", node, nkids, i); } else if (node->counts[i]) { error("node %p: kids[%d] NULL but count[%d]=%d nonzero", node, i, i, node->counts[i]); } /* Count the non-NULL elements. */ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++); /* Ensure no elements beyond the first NULL are non-NULL. */ for (i = nelems; i < 3; i++) if (node->elems[i]) { error("node %p: nelems=%d but elems[%d] non-NULL", node, nelems, i); } if (nkids == 0) { /* * If nkids==0, this is a leaf node; verify that the tree * depth is the same everywhere. */ if (ctx->treedepth < 0) ctx->treedepth = level; /* we didn't know the depth yet */ else if (ctx->treedepth != level) error("node %p: leaf at depth %d, previously seen depth %d", node, level, ctx->treedepth); } else { /* * If nkids != 0, then it should be nelems+1, unless nelems * is 0 in which case nkids should also be 0 (and so we * shouldn't be in this condition at all). */ int shouldkids = (nelems ? nelems + 1 : 0); if (nkids != shouldkids) { error("node %p: %d elems should mean %d kids but has %d", node, nelems, shouldkids, nkids); } } /* * nelems should be at least 1. */ if (nelems == 0) { error("node %p: no elems", node, nkids); } /* * Add nelems to the running element count of the whole tree. */ ctx->elemcount += nelems; /* * Check ordering property: all elements should be strictly > * lowbound, strictly < highbound, and strictly < each other in * sequence. (lowbound and highbound are NULL at edges of tree * - both NULL at root node - and NULL is considered to be < * everything and > everything. IYSWIM.) */ if (cmp) { for (i = -1; i < nelems; i++) { void *lower = (i == -1 ? lowbound : node->elems[i]); void *higher = (i + 1 == nelems ? highbound : node->elems[i + 1]); if (lower && higher && cmp(lower, higher) >= 0) { error("node %p: kid comparison [%d=%s,%d=%s] failed", node, i, lower, i + 1, higher); } } } /* * Check parent pointers: all non-NULL kids should have a * parent pointer coming back to this node. */ for (i = 0; i < nkids; i++) if (node->kids[i]->parent != node) { error("node %p kid %d: parent ptr is %p not %p", node, i, node->kids[i]->parent, node); } /* * Now (finally!) recurse into subtrees. */ count = nelems; for (i = 0; i < nkids; i++) { void *lower = (i == 0 ? lowbound : node->elems[i - 1]); void *higher = (i >= nelems ? highbound : node->elems[i]); int subcount = chknode(ctx, level + 1, node->kids[i], lower, higher); if (node->counts[i] != subcount) { error("node %p kid %d: count says %d, subtree really has %d", node, i, node->counts[i], subcount); } count += subcount; } return count; } void verify(void) { chkctx ctx; int i; void *p; ctx.treedepth = -1; /* depth unknown yet */ ctx.elemcount = 0; /* no elements seen yet */ /* * Verify validity of tree properties. */ if (tree->root) { if (tree->root->parent != NULL) error("root->parent is %p should be null", tree->root->parent); chknode(&ctx, 0, tree->root, NULL, NULL); } printf("tree depth: %d\n", ctx.treedepth); /* * Enumerate the tree and ensure it matches up to the array. */ for (i = 0; NULL != (p = index234(tree, i)); i++) { if (i >= arraylen) error("tree contains more than %d elements", arraylen); if (array[i] != p) error("enum at position %d: array says %s, tree says %s", i, array[i], p); } if (ctx.elemcount != i) { error("tree really contains %d elements, enum gave %d", ctx.elemcount, i); } if (i < arraylen) { error("enum gave only %d elements, array has %d", i, arraylen); } i = count234(tree); if (ctx.elemcount != i) { error("tree really contains %d elements, count234 gave %d", ctx.elemcount, i); } } void internal_addtest(void *elem, int index, void *realret) { int i, j; void *retval; if (arraysize < arraylen + 1) { arraysize = arraylen + 1 + 256; array = sresize(array, arraysize, void *); } i = index; /* now i points to the first element >= elem */ retval = elem; /* expect elem returned (success) */ for (j = arraylen; j > i; j--) array[j] = array[j - 1]; array[i] = elem; /* add elem to array */ arraylen++; if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } verify(); } void addtest(void *elem) { int i; void *realret; realret = add234(tree, elem); i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i < arraylen && !cmp(elem, array[i])) { void *retval = array[i]; /* expect that returned not elem */ if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } } else internal_addtest(elem, i, realret); } void addpostest(void *elem, int i) { void *realret; realret = addpos234(tree, elem, i); internal_addtest(elem, i, realret); } void delpostest(int i) { int index = i; void *elem = array[i], *ret; /* i points to the right element */ while (i < arraylen - 1) { array[i] = array[i + 1]; i++; } arraylen--; /* delete elem from array */ if (tree->cmp) ret = del234(tree, elem); else ret = delpos234(tree, index); if (ret != elem) { error("del returned %p, expected %p", ret, elem); } verify(); } void deltest(void *elem) { int i; i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i >= arraylen || cmp(elem, array[i]) != 0) return; /* don't do it! */ delpostest(i); } /* A sample data set and test utility. Designed for pseudo-randomness, * and yet repeatability. */ /* * This random number generator uses the `portable implementation' * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits; * change it if not. */ int randomnumber(unsigned *seed) { *seed *= 1103515245; *seed += 12345; return ((*seed) / 65536) % 32768; } int mycmp(void *av, void *bv) { char const *a = (char const *) av; char const *b = (char const *) bv; return strcmp(a, b); } #define lenof(x) ( sizeof((x)) / sizeof(*(x)) ) char *strings[] = { "a", "ab", "absque", "coram", "de", "palam", "clam", "cum", "ex", "e", "sine", "tenus", "pro", "prae", "banana", "carrot", "cabbage", "broccoli", "onion", "zebra", "penguin", "blancmange", "pangolin", "whale", "hedgehog", "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux", "murfl", "spoo", "breen", "flarn", "octothorpe", "snail", "tiger", "elephant", "octopus", "warthog", "armadillo", "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin", "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper", "wand", "ring", "amulet" }; #define NSTR lenof(strings) int findtest(void) { const static int rels[] = { REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT }; const static char *const relnames[] = { "EQ", "GE", "LE", "LT", "GT" }; int i, j, rel, index; char *p, *ret, *realret, *realret2; int lo, hi, mid, c; for (i = 0; i < NSTR; i++) { p = strings[i]; for (j = 0; j < sizeof(rels) / sizeof(*rels); j++) { rel = rels[j]; lo = 0; hi = arraylen - 1; while (lo <= hi) { mid = (lo + hi) / 2; c = strcmp(p, array[mid]); if (c < 0) hi = mid - 1; else if (c > 0) lo = mid + 1; else break; } if (c == 0) { if (rel == REL234_LT) ret = (mid > 0 ? array[--mid] : NULL); else if (rel == REL234_GT) ret = (mid < arraylen - 1 ? array[++mid] : NULL); else ret = array[mid]; } else { assert(lo == hi + 1); if (rel == REL234_LT || rel == REL234_LE) { mid = hi; ret = (hi >= 0 ? array[hi] : NULL); } else if (rel == REL234_GT || rel == REL234_GE) { mid = lo; ret = (lo < arraylen ? array[lo] : NULL); } else ret = NULL; } realret = findrelpos234(tree, p, NULL, rel, &index); if (realret != ret) { error("find(\"%s\",%s) gave %s should be %s", p, relnames[j], realret, ret); } if (realret && index != mid) { error("find(\"%s\",%s) gave %d should be %d", p, relnames[j], index, mid); } if (realret && rel == REL234_EQ) { realret2 = index234(tree, index); if (realret2 != realret) { error("find(\"%s\",%s) gave %s(%d) but %d -> %s", p, relnames[j], realret, index, index, realret2); } } #if 0 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j], realret, index); #endif } } realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index); if (arraylen && (realret != array[0] || index != 0)) { error("find(NULL,GT) gave %s(%d) should be %s(0)", realret, index, array[0]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,GT) gave %s(%d) should be NULL", realret, index); } realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index); if (arraylen && (realret != array[arraylen - 1] || index != arraylen - 1)) { error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index, array[arraylen - 1]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,LT) gave %s(%d) should be NULL", realret, index); } } int main(void) { int in[NSTR]; int i, j, k; unsigned seed = 0; for (i = 0; i < NSTR; i++) in[i] = 0; array = NULL; arraylen = arraysize = 0; tree = newtree234(mycmp); cmp = mycmp; verify(); for (i = 0; i < 10000; i++) { j = randomnumber(&seed); j %= NSTR; printf("trial: %d\n", i); if (in[j]) { printf("deleting %s (%d)\n", strings[j], j); deltest(strings[j]); in[j] = 0; } else { printf("adding %s (%d)\n", strings[j], j); addtest(strings[j]); in[j] = 1; } findtest(); } while (arraylen > 0) { j = randomnumber(&seed); j %= arraylen; deltest(array[j]); } freetree234(tree); /* * Now try an unsorted tree. We don't really need to test * delpos234 because we know del234 is based on it, so it's * already been tested in the above sorted-tree code; but for * completeness we'll use it to tear down our unsorted tree * once we've built it. */ tree = newtree234(NULL); cmp = NULL; verify(); for (i = 0; i < 1000; i++) { printf("trial: %d\n", i); j = randomnumber(&seed); j %= NSTR; k = randomnumber(&seed); k %= count234(tree) + 1; printf("adding string %s at index %d\n", strings[j], k); addpostest(strings[j], k); } while (count234(tree) > 0) { printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); printf("deleting string %s from index %d\n", (const char *)array[j], j); delpostest(j); } return 0; } #endif its-playback-time-0.2017-08-30.3c40fd3/tree234.h000066400000000000000000000127571333656753000202240ustar00rootroot00000000000000/* * tree234.h: header defining functions in tree234.c. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef TREE234_H #define TREE234_H /* * This typedef is opaque outside tree234.c itself. */ typedef struct tree234_Tag tree234; typedef int (*cmpfn234) (void *, void *); /* * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and * lookups by key will fail: you can only look things up by numeric * index, and you have to use addpos234() and delpos234(). */ tree234 *newtree234(cmpfn234 cmp); /* * Free a 2-3-4 tree (not including freeing the elements). */ void freetree234(tree234 * t); /* * Add an element e to a sorted 2-3-4 tree t. Returns e on success, * or if an existing element compares equal, returns that. */ void *add234(tree234 * t, void *e); /* * Add an element e to an unsorted 2-3-4 tree t. Returns e on * success, NULL on failure. (Failure should only occur if the * index is out of range or the tree is sorted.) * * Index range can be from 0 to the tree's current element count, * inclusive. */ void *addpos234(tree234 * t, void *e, int index); /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. * * One obvious use for this function is in iterating over the whole * of a tree (sorted or unsorted): * * for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p); * * or * * int maxcount = count234(tree); * for (i = 0; i < maxcount; i++) { * p = index234(tree, i); * assert(p != NULL); * consume(p); * } */ void *index234(tree234 * t, int index); /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. * * Three of these functions are special cases of findrelpos234. The * non-`pos' variants lack the `index' parameter: if the parameter * is present and non-NULL, it must point to an integer variable * which will be filled with the numeric index of the returned * element. * * The non-`rel' variants lack the `relation' parameter. This * parameter allows you to specify what relation the element you * provide has to the element you're looking for. This parameter * can be: * * REL234_EQ - find only an element that compares equal to e * REL234_LT - find the greatest element that compares < e * REL234_LE - find the greatest element that compares <= e * REL234_GT - find the smallest element that compares > e * REL234_GE - find the smallest element that compares >= e * * Non-`rel' variants assume REL234_EQ. * * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be * NULL. In this case, REL234_GT will return the smallest element * in the tree, and REL234_LT will return the greatest. This gives * an alternative means of iterating over a sorted tree, instead of * using index234: * * // to loop forwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;) * consume(p); * * // to loop backwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;) * consume(p); */ enum { REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE }; void *find234(tree234 * t, void *e, cmpfn234 cmp); void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation); void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index); void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp, int relation, int *index); /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. * * delpos234 deletes the element at a particular tree index: it * works on both sorted and unsorted trees. * * del234 deletes the element passed to it, so it only works on * sorted trees. (It's equivalent to using findpos234 to determine * the index of an element, and then passing that index to * delpos234.) * * Both functions return a pointer to the element they delete, for * the user to free or pass on elsewhere or whatever. If the index * is out of range (delpos234) or the element is already not in the * tree (del234) then they return NULL. */ void *del234(tree234 * t, void *e); void *delpos234(tree234 * t, int index); /* * Return the total element count of a tree234. */ int count234(tree234 * t); #endif /* TREE234_H */ its-playback-time-0.2017-08-30.3c40fd3/ttydump000077500000000000000000000031771333656753000203130ustar00rootroot00000000000000#!/usr/bin/perl # Dump the correspondence between frame numbers and file positions # in a ttyrec, with timestamps for good measure. @args = (); $opts = 1; $join = 0; $to_script = 0; foreach $i (@ARGV) { if ($opts and $i =~ /^-(.*)/) { if ($1 eq "-") { $opts = 0; } elsif ($1 eq "j") { $join = 1; } elsif ($1 eq "s") { $to_script = 1; } else { warn "unrecognised option '-$1'\n"; } } else { push @args, $i; } } if (!defined $args[0]) { print STDERR "usage: ttydump [...]\n"; # If called with no arguments, we assume we're being _asked_ to # print the usage statement, so we do so and exit with no # error. If called with one argument, we have no remaining # choice but to assume the user is mistaken. exit (defined $args[0]); } $tframe = 0; foreach $file (@args) { open F, "<$file" or die "$0: unable to open '$file'\n"; $frame = 0; while (1) { $hdr = ""; $hgot = read F, $hdr, 12; last if $hgot == 0; # clean EOF die "$0: unexpected EOF in '$file' frame header\n" if $hgot < 12; @hdrvals = unpack "VVV", $hdr; $dlen = $hdrvals[2]; $data = ""; $posdata = sprintf "offset 0x%x len 0x%x", (tell F), $dlen; $posdata = "$file " . $posdata if $join; $timestamp = sprintf "%d.%06d", $hdrvals[0], $hdrvals[1]; $dgot = read F, $data, $dlen; die "$0: unexpected EOF in '$file' frame data\n" if $dgot < $dlen; if ($join) { $location = "$tframe"; } else { $location = "$file:$frame"; } if ($to_script) { print $data; } else { print "$location:$timestamp:$posdata\n"; } $frame++; $tframe++; } close F; } its-playback-time-0.2017-08-30.3c40fd3/ttygrep000077500000000000000000000026461333656753000203030ustar00rootroot00000000000000#!/usr/bin/perl # Search a ttyrec file for text matching a regexp. Print the frame # number(s) in which it's found, so that ipbt can be used to go # straight there. @args = (); $opts = 1; $join = 0; foreach $i (@ARGV) { if ($opts and $i =~ /^-(.*)/) { if ($1 eq "-") { $opts = 0; } elsif ($1 eq "j") { $join = 1; } else { warn "unrecognised option '-$1'\n"; } } else { push @args, $i; } } if (!defined $args[1]) { print STDERR "usage: ttygrep [...]\n"; # If called with no arguments, we assume we're being _asked_ to # print the usage statement, so we do so and exit with no # error. If called with one argument, we have no remaining # choice but to assume the user is mistaken. exit (defined $args[0]); } $pattern = shift @args; $tframe = 0; foreach $file (@args) { open F, "<$file" or die "$0: unable to open '$file'\n"; $frame = 0; while (1) { $hdr = ""; $hgot = read F, $hdr, 12; last if $hgot == 0; # clean EOF die "$0: unexpected EOF in '$file' frame header\n" if $hgot < 12; @hdrvals = unpack "VVV", $hdr; $dlen = $hdrvals[2]; $data = ""; $dgot = read F, $data, $dlen; die "$0: unexpected EOF in '$file' frame data\n" if $dgot < $dlen; if ($join) { $location = "$tframe"; } else { $location = "$file:$frame"; } print "$location:$&\n" if $data =~ /[ -~]*$pattern[ -~]*/s; $frame++; $tframe++; } close F; } its-playback-time-0.2017-08-30.3c40fd3/unix.h000066400000000000000000000203651333656753000200110ustar00rootroot00000000000000#ifndef PUTTY_UNIX_H #define PUTTY_UNIX_H #ifdef HAVE_CONFIG_H # include "uxconfig.h" /* Space to hide it from mkfiles.pl */ #endif #include /* for FILENAME_MAX */ #include /* C99 int types */ #ifndef NO_LIBDL #include /* Dynamic library loading */ #endif /* NO_LIBDL */ #include "charset.h" #include /* for mode_t */ #ifdef OSX_GTK /* * Assorted tweaks to various parts of the GTK front end which all * need to be enabled when compiling on OS X. Because I might need the * same tweaks on other systems in future, I don't want to * conditionalise all of them on OSX_GTK directly, so instead, each * one has its own name and we enable them all centrally here if * OSX_GTK is defined at configure time. */ #define NOT_X_WINDOWS /* of course, all the X11 stuff should be disabled */ #define NO_PTY_PRE_INIT /* OS X gets very huffy if we try to set[ug]id */ #define SET_NONBLOCK_VIA_OPENPT /* work around missing fcntl functionality */ #define OSX_META_KEY_CONFIG /* two possible Meta keys to choose from */ /* this potential one of the Meta keys needs manual handling */ #define META_MANUAL_MASK (GDK_MOD1_MASK) #define JUST_USE_GTK_CLIPBOARD_UTF8 /* low-level gdk_selection_* fails */ #define DEFAULT_CLIPBOARD GDK_SELECTION_CLIPBOARD /* OS X has no PRIMARY */ #define BUILDINFO_PLATFORM_GTK "OS X (GTK)" #define BUILDINFO_GTK #elif defined NOT_X_WINDOWS #define BUILDINFO_PLATFORM_GTK "Unix (pure GTK)" #define BUILDINFO_GTK #else #define BUILDINFO_PLATFORM_GTK "Unix (GTK + X11)" #define BUILDINFO_GTK #endif /* BUILDINFO_PLATFORM varies its expansion between the GTK and * pure-CLI utilities, so that Unix Plink, PSFTP etc don't announce * themselves incongruously as having something to do with GTK. */ #define BUILDINFO_PLATFORM_CLI "Unix" extern const int buildinfo_gtk_relevant; #define BUILDINFO_PLATFORM (buildinfo_gtk_relevant ? \ BUILDINFO_PLATFORM_GTK : BUILDINFO_PLATFORM_CLI) char *buildinfo_gtk_version(void); struct Filename { char *path; }; FILE *f_open(const struct Filename *, char const *, int); struct FontSpec { char *name; /* may be "" to indicate no selected font at all */ }; struct FontSpec *fontspec_new(const char *name); typedef void *Context; /* FIXME: probably needs changing */ extern Backend pty_backend; #define BROKEN_PIPE_ERROR_CODE EPIPE /* used in sshshare.c */ typedef uint32_t uint32; /* C99: uint32_t defined in stdint.h */ #define PUTTY_UINT32_DEFINED /* * Under GTK, we send MA_CLICK _and_ MA_2CLK, or MA_CLICK _and_ * MA_3CLK, when a button is pressed for the second or third time. */ #define MULTICLICK_ONLY_EVENT 0 /* * Under GTK, there is no context help available. */ #define HELPCTX(x) P(NULL) #define FILTER_KEY_FILES NULL /* FIXME */ #define FILTER_DYNLIB_FILES NULL /* FIXME */ /* * Under X, selection data must not be NUL-terminated. */ #define SELECTION_NUL_TERMINATED 0 /* * Under X, copying to the clipboard terminates lines with just LF. */ #define SEL_NL { 10 } /* Simple wraparound timer function */ unsigned long getticks(void); #define GETTICKCOUNT getticks #define TICKSPERSEC 1000 /* we choose to use milliseconds */ #define CURSORBLINK 450 /* no standard way to set this */ #define WCHAR wchar_t #define BYTE unsigned char /* * Unix-specific global flag * * FLAG_STDERR_TTY indicates that standard error might be a terminal and * might get its configuration munged, so anything trying to output plain * text (i.e. with newlines in it) will need to put it back into cooked * mode first. Applications setting this flag should also call * stderr_tty_init() before messing with any terminal modes, and can call * premsg() before outputting text to stderr and postmsg() afterwards. */ #define FLAG_STDERR_TTY 0x1000 /* The per-session frontend structure managed by gtkwin.c */ struct gui_data; struct gui_data *new_session_window(Conf *conf, const char *geometry_string); /* Defined in gtkmain.c */ void launch_duplicate_session(Conf *conf); void launch_new_session(void); void launch_saved_session(const char *str); #ifdef MAY_REFER_TO_GTK_IN_HEADERS GtkWidget *make_gtk_toplevel_window(void *frontend); #endif /* Defined in gtkcomm.c */ void gtkcomm_setup(void); /* Things pty.c needs from pterm.c */ const char *get_x_display(void *frontend); int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */ long get_windowid(void *frontend); /* Things gtkdlg.c needs from pterm.c */ void *get_window(void *frontend); /* void * to avoid depending on gtk.h */ void post_main(void); /* called after any subsidiary gtk_main() */ /* Things pterm.c needs from gtkdlg.c */ int do_config_box(const char *title, Conf *conf, int midsession, int protcfginfo); void fatal_message_box(void *window, const char *msg); void nonfatal_message_box(void *window, const char *msg); void about_box(void *window); void *eventlogstuff_new(void); void showeventlog(void *estuff, void *parentwin); void logevent_dlg(void *estuff, const char *string); int reallyclose(void *frontend); #ifdef MAY_REFER_TO_GTK_IN_HEADERS int messagebox(GtkWidget *parentwin, const char *title, const char *msg, int minwid, int selectable, ...); #endif /* Things pterm.c needs from {ptermm,uxputty}.c */ char *make_default_wintitle(char *hostname); int process_nonoption_arg(const char *arg, Conf *conf, int *allow_launch); /* pterm.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); /* Things uxstore.c needs from pterm.c */ char *x_get_default(const char *key); /* Things uxstore.c provides to pterm.c */ void provide_xrm_string(char *string); /* Things provided by uxcons.c */ struct termios; void stderr_tty_init(void); void premsg(struct termios *); void postmsg(struct termios *); /* The interface used by uxsel.c */ typedef struct uxsel_id uxsel_id; void uxsel_init(void); typedef void (*uxsel_callback_fn)(int fd, int event); void uxsel_set(int fd, int rwx, uxsel_callback_fn callback); void uxsel_del(int fd); void select_result(int fd, int event); int first_fd(int *state, int *rwx); int next_fd(int *state, int *rwx); /* The following are expected to be provided _to_ uxsel.c by the frontend */ uxsel_id *uxsel_input_add(int fd, int rwx); /* returns an id */ void uxsel_input_remove(uxsel_id *id); /* uxcfg.c */ struct controlbox; void unix_setup_config_box(struct controlbox *b, int midsession, int protocol); /* gtkcfg.c */ void gtk_setup_config_box(struct controlbox *b, int midsession, void *window); /* * In the Unix Unicode layer, DEFAULT_CODEPAGE is a special value * which causes mb_to_wc and wc_to_mb to call _libc_ rather than * libcharset. That way, we can interface the various charsets * supported by libcharset with the one supported by mbstowcs and * wcstombs (which will be the character set in which stuff read * from the command line or config files is assumed to be encoded). */ #define DEFAULT_CODEPAGE 0xFFFF #define CP_UTF8 CS_UTF8 /* from libcharset */ #define strnicmp strncasecmp #define stricmp strcasecmp /* BSD-semantics version of signal(), and another helpful function */ void (*putty_signal(int sig, void (*func)(int)))(int); void block_signal(int sig, int block_it); /* uxmisc.c */ void cloexec(int); void noncloexec(int); int nonblock(int); int no_nonblock(int); char *make_dir_and_check_ours(const char *dirname); char *make_dir_path(const char *path, mode_t mode); /* * Exports from unicode.c. */ struct unicode_data; int init_ucs(struct unicode_data *ucsdata, char *line_codepage, int utf8_override, int font_charset, int vtmode); /* * Spare function exported directly from uxnet.c. */ void *sk_getxdmdata(void *sock, int *lenp); /* * General helpful Unix stuff: more helpful version of the FD_SET * macro, which also handles maxfd. */ #define FD_SET_MAX(fd, max, set) do { \ FD_SET(fd, &set); \ if (max < fd + 1) max = fd + 1; \ } while (0) /* * Exports from winser.c. */ extern Backend serial_backend; /* * uxpeer.c, wrapping getsockopt(SO_PEERCRED). */ int so_peercred(int fd, int *pid, int *uid, int *gid); /* * Default font setting, which can vary depending on NOT_X_WINDOWS. */ #ifdef NOT_X_WINDOWS #define DEFAULT_GTK_FONT "client:Monospace 12" #else #define DEFAULT_GTK_FONT "server:fixed" #endif #endif its-playback-time-0.2017-08-30.3c40fd3/utf8.c000066400000000000000000000731061333656753000177100ustar00rootroot00000000000000/* * utf8.c - routines to handle UTF-8. */ #ifndef ENUM_CHARSETS #include "charset.h" #include "internal.h" void read_utf8(charset_spec const *, long int, charset_state *, void (*)(void *, long int), void *); void write_utf8(charset_spec const *, long int, charset_state *, void (*)(void *, long int), void *); /* * UTF-8 has no associated data, so `charset' may be ignored. */ void read_utf8(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { UNUSEDARG(charset); /* * For reading UTF-8, the `state' word contains: * * - in bits 29-31, the number of bytes expected to be in the * current multibyte character (which we can tell instantly * from the first byte, of course). * * - in bits 26-28, the number of bytes _seen so far_ in the * current multibyte character. * * - in the remainder of the word, the current value of the * character, which is shifted upwards by 6 bits to * accommodate each new byte. * * As required, the state is zero when we are not in the middle * of a multibyte character at all. * * For example, when reading E9 8D 8B, starting at state=0: * * - after E9, the state is 0x64000009 * - after 8D, the state is 0x6800024d * - after 8B, the state conceptually becomes 0x6c00934b, at * which point we notice we've got as many characters as we * were expecting, output U+934B, and reset the state to * zero. * * Note that the maximum number of bits we might need to store * in the character value field is 25 (U+7FFFFFFF contains 31 * bits, but we will never actually store its full value * because when we receive the last 6 bits in the final * continuation byte we will output it and revert the state to * zero). Hence the character value field never collides with * the byte counts. */ if (input_chr < 0x80) { /* * Single-byte character. If the state is nonzero before * coming here, output an error for an incomplete sequence. * Then output the character. */ if (state->s0 != 0) { emit(emitctx, ERROR); state->s0 = 0; } emit(emitctx, input_chr); } else if (input_chr == 0xFE || input_chr == 0xFF) { /* * FE and FF bytes should _never_ occur in UTF-8. They are * automatic errors; if the state was nonzero to start * with, output a further error for an incomplete sequence. */ if (state->s0 != 0) { emit(emitctx, ERROR); state->s0 = 0; } emit(emitctx, ERROR); } else if (input_chr >= 0x80 && input_chr < 0xC0) { /* * Continuation byte. Output an error for an unexpected * continuation byte, if the state is zero. */ if (state->s0 == 0) { emit(emitctx, ERROR); } else { unsigned long charval; unsigned long topstuff; int bytes; /* * Otherwise, accumulate more of the character value. */ charval = state->s0 & 0x03ffffffL; charval = (charval << 6) | (input_chr & 0x3F); /* * Check the byte counts; if we have not reached the * end of the character, update the state and return. */ topstuff = state->s0 & 0xfc000000L; topstuff += 0x04000000L; /* add one to the byte count */ if (((topstuff << 3) ^ topstuff) & 0xe0000000L) { state->s0 = topstuff | charval; return; } /* * Now we know we've reached the end of the character. * `charval' is the Unicode value. We should check for * various invalid things, and then either output * charval or an error. In all cases we reset the state * to zero. */ bytes = topstuff >> 29; state->s0 = 0; if (charval >= 0xD800 && charval < 0xE000) { /* * Surrogates (0xD800-0xDFFF) may never be encoded * in UTF-8. A surrogate pair in Unicode should * have been encoded as a single UTF-8 character * occupying more than three bytes. */ emit(emitctx, ERROR); } else if (charval == 0xFFFE || charval == 0xFFFF) { /* * U+FFFE and U+FFFF are invalid Unicode characters * and may never be encoded in UTF-8. (This is one * reason why U+FFFF is our way of signalling an * error to our `emit' function :-) */ emit(emitctx, ERROR); } else if ((charval <= 0x7FL /* && bytes > 1 */) || (charval <= 0x7FFL && bytes > 2) || (charval <= 0xFFFFL && bytes > 3) || (charval <= 0x1FFFFFL && bytes > 4) || (charval <= 0x3FFFFFFL && bytes > 5)) { /* * Overlong sequences are not to be tolerated, * under any circumstances. */ emit(emitctx, ERROR); } else { /* * Oh, all right. We'll let this one off. */ emit(emitctx, charval); } } } else { /* * Lead byte. First output an error for an incomplete * sequence, if the state is nonzero. */ if (state->s0 != 0) emit(emitctx, ERROR); /* * Now deal with the lead byte: work out the number of * bytes we expect to see in this character, and extract * the initial bits of it too. */ if (input_chr >= 0xC0 && input_chr < 0xE0) { state->s0 = 0x44000000L | (input_chr & 0x1F); } else if (input_chr >= 0xE0 && input_chr < 0xF0) { state->s0 = 0x64000000L | (input_chr & 0x0F); } else if (input_chr >= 0xF0 && input_chr < 0xF8) { state->s0 = 0x84000000L | (input_chr & 0x07); } else if (input_chr >= 0xF8 && input_chr < 0xFC) { state->s0 = 0xa4000000L | (input_chr & 0x03); } else if (input_chr >= 0xFC && input_chr < 0xFE) { state->s0 = 0xc4000000L | (input_chr & 0x01); } } } /* * UTF-8 is a stateless multi-byte encoding (in the sense that just * after any character has been completed, the state is always the * same); hence when writing it, there is no need to use the * charset_state. */ void write_utf8(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { UNUSEDARG(charset); UNUSEDARG(state); /* * Refuse to output any illegal code points. */ if (input_chr == 0xFFFE || input_chr == 0xFFFF || (input_chr >= 0xD800 && input_chr < 0xE000)) { emit(emitctx, ERROR); } else if (input_chr < 0x80) { /* one-byte character */ emit(emitctx, input_chr); } else if (input_chr < 0x800) { /* two-byte character */ emit(emitctx, 0xC0 | (0x1F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else if (input_chr < 0x10000) { /* three-byte character */ emit(emitctx, 0xE0 | (0x0F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else if (input_chr < 0x200000) { /* four-byte character */ emit(emitctx, 0xF0 | (0x07 & (input_chr >> 18))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else if (input_chr < 0x4000000) {/* five-byte character */ emit(emitctx, 0xF8 | (0x03 & (input_chr >> 24))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 18))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else { /* six-byte character */ emit(emitctx, 0xFC | (0x01 & (input_chr >> 30))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 24))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 18))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } } #ifdef TESTMODE #include #include int total_errs = 0; void utf8_emit(void *ctx, long output) { wchar_t **p = (wchar_t **)ctx; *(*p)++ = output; } void utf8_read_test(int line, char *input, int inlen, ...) { va_list ap; wchar_t *p, str[512]; int i; charset_state state; unsigned long l; state.s0 = 0; p = str; for (i = 0; i < inlen; i++) read_utf8(NULL, input[i] & 0xFF, &state, utf8_emit, &p); va_start(ap, inlen); l = 0; for (i = 0; i < p - str; i++) { l = va_arg(ap, long int); if (l == -1) { printf("%d: correct string shorter than output\n", line); total_errs++; break; } if (l != str[i]) { printf("%d: char %d came out as %08x, should be %08x\n", line, i, str[i], (unsigned)l); total_errs++; } } if (l != -1) { l = va_arg(ap, long int); if (l != -1) { printf("%d: correct string longer than output\n", line); total_errs++; } } va_end(ap); } void utf8_write_test(int line, const long *input, int inlen, ...) { va_list ap; wchar_t *p, str[512]; int i; charset_state state; unsigned long l; state.s0 = 0; p = str; for (i = 0; i < inlen; i++) write_utf8(NULL, input[i], &state, utf8_emit, &p); va_start(ap, inlen); l = 0; for (i = 0; i < p - str; i++) { l = va_arg(ap, long int); if (l == -1) { printf("%d: correct string shorter than output\n", line); total_errs++; break; } if (l != str[i]) { printf("%d: char %d came out as %08x, should be %08x\n", line, i, str[i], (unsigned)l); total_errs++; } } if (l != -1) { l = va_arg(ap, long int); if (l != -1) { printf("%d: correct string longer than output\n", line); total_errs++; } } va_end(ap); } /* Macro to concoct the first three parameters of utf8_read_test. */ #define TESTSTR(x) __LINE__, x, lenof(x) int main(void) { printf("read tests beginning\n"); utf8_read_test(TESTSTR("\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5"), 0x000003BA, /* GREEK SMALL LETTER KAPPA */ 0x00001F79, /* GREEK SMALL LETTER OMICRON WITH OXIA */ 0x000003C3, /* GREEK SMALL LETTER SIGMA */ 0x000003BC, /* GREEK SMALL LETTER MU */ 0x000003B5, /* GREEK SMALL LETTER EPSILON */ 0, -1); utf8_read_test(TESTSTR("\x00"), 0x00000000, /* */ 0, -1); utf8_read_test(TESTSTR("\xC2\x80"), 0x00000080, /* */ 0, -1); utf8_read_test(TESTSTR("\xE0\xA0\x80"), 0x00000800, /* */ 0, -1); utf8_read_test(TESTSTR("\xF0\x90\x80\x80"), 0x00010000, /* */ 0, -1); utf8_read_test(TESTSTR("\xF8\x88\x80\x80\x80"), 0x00200000, /* */ 0, -1); utf8_read_test(TESTSTR("\xFC\x84\x80\x80\x80\x80"), 0x04000000, /* */ 0, -1); utf8_read_test(TESTSTR("\x7F"), 0x0000007F, /* */ 0, -1); utf8_read_test(TESTSTR("\xDF\xBF"), 0x000007FF, /* */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBD"), 0x0000FFFD, /* REPLACEMENT CHARACTER */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBF"), ERROR, /* (invalid char) */ 0, -1); utf8_read_test(TESTSTR("\xF7\xBF\xBF\xBF"), 0x001FFFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF\xBF"), 0x03FFFFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF\xBF"), 0x7FFFFFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xED\x9F\xBF"), 0x0000D7FF, /* */ 0, -1); utf8_read_test(TESTSTR("\xEE\x80\x80"), 0x0000E000, /* */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBD"), 0x0000FFFD, /* REPLACEMENT CHARACTER */ 0, -1); utf8_read_test(TESTSTR("\xF4\x8F\xBF\xBF"), 0x0010FFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xF4\x90\x80\x80"), 0x00110000, /* */ 0, -1); utf8_read_test(TESTSTR("\x80"), ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\xBF"), ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF\x80"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\xC0\x20\xC1\x20\xC2\x20\xC3\x20\xC4\x20\xC5\x20\xC6\x20\xC7\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xE0\x20\xE1\x20\xE2\x20\xE3\x20\xE4\x20\xE5\x20\xE6\x20\xE7\x20\xE8\x20\xE9\x20\xEA\x20\xEB\x20\xEC\x20\xED\x20\xEE\x20\xEF\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xF0\x20\xF1\x20\xF2\x20\xF3\x20\xF4\x20\xF5\x20\xF6\x20\xF7\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xF8\x20\xF9\x20\xFA\x20\xFB\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xFC\x20\xFD\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xC0"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x80\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x80\x80\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xDF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xF7\xBF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF"), ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFE"), ERROR, /* (invalid UTF-8 byte) */ 0, -1); utf8_read_test(TESTSTR("\xFF"), ERROR, /* (invalid UTF-8 byte) */ 0, -1); utf8_read_test(TESTSTR("\xFE\xFE\xFF\xFF"), ERROR, /* (invalid UTF-8 byte) */ ERROR, /* (invalid UTF-8 byte) */ ERROR, /* (invalid UTF-8 byte) */ ERROR, /* (invalid UTF-8 byte) */ 0, -1); utf8_read_test(TESTSTR("\xC0\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x80\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x80\x80\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xC1\xBF"), ERROR, /* (overlong form of 7F) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x9F\xBF"), ERROR, /* (overlong form of DF BF) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x8F\xBF\xBF"), ERROR, /* (overlong form of EF BF BF) (invalid char) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x87\xBF\xBF\xBF"), ERROR, /* (overlong form of F7 BF BF BF) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x83\xBF\xBF\xBF\xBF"), ERROR, /* (overlong form of FB BF BF BF BF) */ 0, -1); utf8_read_test(TESTSTR("\xC0\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x80\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x80\x80\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xED\xA0\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAD\xBF"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAE\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAF\xBF"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xB0\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xBE\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xBF\xBF"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBE"), ERROR, /* (invalid char) */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBF"), ERROR, /* (invalid char) */ 0, -1); printf("read tests completed\n"); printf("write tests beginning\n"); { const static long str[] = {0x03BAL, 0x1F79L, 0x03C3L, 0x03BCL, 0x03B5L, 0}; utf8_write_test(TESTSTR(str), 0xCE, 0xBA, 0xE1, 0xBD, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xB5, 0, -1); } { const static long str[] = {0x0000L, 0}; utf8_write_test(TESTSTR(str), 0x00, 0, -1); } { const static long str[] = {0x0080L, 0}; utf8_write_test(TESTSTR(str), 0xC2, 0x80, 0, -1); } { const static long str[] = {0x0800L, 0}; utf8_write_test(TESTSTR(str), 0xE0, 0xA0, 0x80, 0, -1); } { const static long str[] = {0x00010000L, 0}; utf8_write_test(TESTSTR(str), 0xF0, 0x90, 0x80, 0x80, 0, -1); } { const static long str[] = {0x00200000L, 0}; utf8_write_test(TESTSTR(str), 0xF8, 0x88, 0x80, 0x80, 0x80, 0, -1); } { const static long str[] = {0x04000000L, 0}; utf8_write_test(TESTSTR(str), 0xFC, 0x84, 0x80, 0x80, 0x80, 0x80, 0, -1); } { const static long str[] = {0x007FL, 0}; utf8_write_test(TESTSTR(str), 0x7F, 0, -1); } { const static long str[] = {0x07FFL, 0}; utf8_write_test(TESTSTR(str), 0xDF, 0xBF, 0, -1); } { const static long str[] = {0xFFFDL, 0}; utf8_write_test(TESTSTR(str), 0xEF, 0xBF, 0xBD, 0, -1); } { const static long str[] = {0xFFFFL, 0}; utf8_write_test(TESTSTR(str), ERROR, 0, -1); } { const static long str[] = {0x001FFFFFL, 0}; utf8_write_test(TESTSTR(str), 0xF7, 0xBF, 0xBF, 0xBF, 0, -1); } { const static long str[] = {0x03FFFFFFL, 0}; utf8_write_test(TESTSTR(str), 0xFB, 0xBF, 0xBF, 0xBF, 0xBF, 0, -1); } { const static long str[] = {0x7FFFFFFFL, 0}; utf8_write_test(TESTSTR(str), 0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0, -1); } { const static long str[] = {0xD7FFL, 0}; utf8_write_test(TESTSTR(str), 0xED, 0x9F, 0xBF, 0, -1); } { const static long str[] = {0xD800L, 0}; utf8_write_test(TESTSTR(str), ERROR, 0, -1); } { const static long str[] = {0xD800L, 0xDC00L, 0}; utf8_write_test(TESTSTR(str), ERROR, ERROR, 0, -1); } { const static long str[] = {0xDFFFL, 0}; utf8_write_test(TESTSTR(str), ERROR, 0, -1); } { const static long str[] = {0xE000L, 0}; utf8_write_test(TESTSTR(str), 0xEE, 0x80, 0x80, 0, -1); } printf("write tests completed\n"); printf("total: %d errors\n", total_errs); return (total_errs != 0); } #endif /* TESTMODE */ const charset_spec charset_CS_UTF8 = { CS_UTF8, read_utf8, write_utf8, NULL }; #else /* ENUM_CHARSETS */ ENUM_CHARSET(CS_UTF8) #endif /* ENUM_CHARSETS */ its-playback-time-0.2017-08-30.3c40fd3/uxmisc.c000066400000000000000000000214501333656753000203250ustar00rootroot00000000000000/* * PuTTY miscellaneous Unix stuff */ #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" unsigned long getticks(void) { /* * We want to use milliseconds rather than the microseconds or * nanoseconds given by the underlying clock functions, because we * need a decent number of them to fit into a 32-bit word so it * can be used for keepalives. */ #if defined HAVE_CLOCK_GETTIME && defined HAVE_DECL_CLOCK_MONOTONIC { /* Use CLOCK_MONOTONIC if available, so as to be unconfused if * the system clock changes. */ struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) return ts.tv_sec * TICKSPERSEC + ts.tv_nsec / (1000000000 / TICKSPERSEC); } #endif { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC); } } Filename *filename_from_str(const char *str) { Filename *ret = snew(Filename); ret->path = dupstr(str); return ret; } Filename *filename_copy(const Filename *fn) { return filename_from_str(fn->path); } const char *filename_to_str(const Filename *fn) { return fn->path; } int filename_equal(const Filename *f1, const Filename *f2) { return !strcmp(f1->path, f2->path); } int filename_is_null(const Filename *fn) { return !fn->path[0]; } void filename_free(Filename *fn) { sfree(fn->path); sfree(fn); } int filename_serialise(const Filename *f, void *vdata) { char *data = (char *)vdata; int len = strlen(f->path) + 1; /* include trailing NUL */ if (data) { strcpy(data, f->path); } return len; } Filename *filename_deserialise(void *vdata, int maxsize, int *used) { char *data = (char *)vdata; char *end; end = memchr(data, '\0', maxsize); if (!end) return NULL; end++; *used = end - data; return filename_from_str(data); } char filename_char_sanitise(char c) { if (c == '/') return '.'; return c; } #ifdef DEBUG static FILE *debug_fp = NULL; void dputs(const char *buf) { if (!debug_fp) { debug_fp = fopen("debug.log", "w"); } if (write(1, buf, strlen(buf)) < 0) {} /* 'error check' to placate gcc */ fputs(buf, debug_fp); fflush(debug_fp); } #endif char *get_username(void) { struct passwd *p; uid_t uid = getuid(); char *user, *ret = NULL; /* * First, find who we think we are using getlogin. If this * agrees with our uid, we'll go along with it. This should * allow sharing of uids between several login names whilst * coping correctly with people who have su'ed. */ user = getlogin(); setpwent(); if (user) p = getpwnam(user); else p = NULL; if (p && p->pw_uid == uid) { /* * The result of getlogin() really does correspond to * our uid. Fine. */ ret = user; } else { /* * If that didn't work, for whatever reason, we'll do * the simpler version: look up our uid in the password * file and map it straight to a name. */ p = getpwuid(uid); if (!p) return NULL; ret = p->pw_name; } endpwent(); return dupstr(ret); } /* * Display the fingerprints of the PGP Master Keys to the user. * (This is here rather than in uxcons because it's appropriate even for * Unix GUI apps.) */ void pgp_fingerprints(void) { fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n" "be used to establish a trust path from this executable to another\n" "one. See the manual for more information.\n" "(Note: these fingerprints have nothing to do with SSH!)\n" "\n" "PuTTY Master Key as of 2015 (RSA, 4096-bit):\n" " " PGP_MASTER_KEY_FP "\n\n" "Original PuTTY Master Key (RSA, 1024-bit):\n" " " PGP_RSA_MASTER_KEY_FP "\n" "Original PuTTY Master Key (DSA, 1024-bit):\n" " " PGP_DSA_MASTER_KEY_FP "\n", stdout); } /* * Set and clear fcntl options on a file descriptor. We don't * realistically expect any of these operations to fail (the most * plausible error condition is EBADF, but we always believe ourselves * to be passing a valid fd so even that's an assertion-fail sort of * response), so we don't make any effort to return sensible error * codes to the caller - we just log to standard error and die * unceremoniously. However, nonblock and no_nonblock do return the * previous state of O_NONBLOCK. */ void cloexec(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFD); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) { fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); exit(1); } } void noncloexec(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFD); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) { fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); exit(1); } } int nonblock(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFL); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) { fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); exit(1); } return fdflags & O_NONBLOCK; } int no_nonblock(int fd) { int fdflags; fdflags = fcntl(fd, F_GETFL); if (fdflags < 0) { fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); exit(1); } if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) { fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); exit(1); } return fdflags & O_NONBLOCK; } FILE *f_open(const Filename *filename, char const *mode, int is_private) { if (!is_private) { return fopen(filename->path, mode); } else { int fd; assert(mode[0] == 'w'); /* is_private is meaningless for read, and tricky for append */ fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) return NULL; return fdopen(fd, mode); } } FontSpec *fontspec_new(const char *name) { FontSpec *f = snew(FontSpec); f->name = dupstr(name); return f; } FontSpec *fontspec_copy(const FontSpec *f) { return fontspec_new(f->name); } void fontspec_free(FontSpec *f) { sfree(f->name); sfree(f); } int fontspec_serialise(FontSpec *f, void *data) { int len = strlen(f->name); if (data) strcpy(data, f->name); return len + 1; /* include trailing NUL */ } FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) { char *data = (char *)vdata; char *end = memchr(data, '\0', maxsize); if (!end) return NULL; *used = end - data + 1; return fontspec_new(data); } char *make_dir_and_check_ours(const char *dirname) { struct stat st; /* * Create the directory. We might have created it before, so * EEXIST is an OK error; but anything else is doom. */ if (mkdir(dirname, 0700) < 0 && errno != EEXIST) return dupprintf("%s: mkdir: %s", dirname, strerror(errno)); /* * Now check that that directory is _owned by us_ and not writable * by anybody else. This protects us against somebody else * previously having created the directory in a way that's * writable to us, and thus manipulating us into creating the * actual socket in a directory they can see so that they can * connect to it and use our authenticated SSH sessions. */ if (stat(dirname, &st) < 0) return dupprintf("%s: stat: %s", dirname, strerror(errno)); if (st.st_uid != getuid()) return dupprintf("%s: directory owned by uid %d, not by us", dirname, st.st_uid); if ((st.st_mode & 077) != 0) return dupprintf("%s: directory has overgenerous permissions %03o" " (expected 700)", dirname, st.st_mode & 0777); return NULL; } char *make_dir_path(const char *path, mode_t mode) { int pos = 0; char *prefix; while (1) { pos += strcspn(path + pos, "/"); if (pos > 0) { prefix = dupprintf("%.*s", pos, path); if (mkdir(prefix, mode) < 0 && errno != EEXIST) { char *ret = dupprintf("%s: mkdir: %s", prefix, strerror(errno)); sfree(prefix); return ret; } sfree(prefix); } if (!path[pos]) return NULL; pos += strspn(path + pos, "/"); } } its-playback-time-0.2017-08-30.3c40fd3/uxnogtk.c000066400000000000000000000002711333656753000205120ustar00rootroot00000000000000/* * uxnogtk.c: link into non-GUI Unix programs so that they can tell * buildinfo about a lack of GTK. */ #include "putty.h" char *buildinfo_gtk_version(void) { return NULL; } its-playback-time-0.2017-08-30.3c40fd3/uxucs.c000066400000000000000000000154231333656753000201670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "putty.h" #include "charset.h" #include "terminal.h" #include "misc.h" /* * Unix Unicode-handling routines. */ int is_dbcs_leadbyte(int codepage, char byte) { return 0; /* we don't do DBCS */ } int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, wchar_t *wcstr, int wclen) { if (codepage == DEFAULT_CODEPAGE) { int n = 0; mbstate_t state; memset(&state, 0, sizeof state); while (mblen > 0) { size_t i = mbrtowc(wcstr+n, mbstr, (size_t)mblen, &state); if (i == (size_t)-1 || i == (size_t)-2) break; n++; mbstr += i; mblen -= i; } return n; } else if (codepage == CS_NONE) { int n = 0; while (mblen > 0) { wcstr[n] = 0xD800 | (mbstr[0] & 0xFF); n++; mbstr++; mblen--; } return n; } else return charset_to_unicode(&mbstr, &mblen, wcstr, wclen, codepage, NULL, NULL, 0); } int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, char *mbstr, int mblen, const char *defchr, int *defused, struct unicode_data *ucsdata) { /* FIXME: we should remove the defused param completely... */ if (defused) *defused = 0; if (codepage == DEFAULT_CODEPAGE) { char output[MB_LEN_MAX]; mbstate_t state; int n = 0; memset(&state, 0, sizeof state); while (wclen > 0) { int i = wcrtomb(output, wcstr[0], &state); if (i == (size_t)-1 || i > n - mblen) break; memcpy(mbstr+n, output, i); n += i; wcstr++; wclen--; } return n; } else if (codepage == CS_NONE) { int n = 0; while (wclen > 0 && n < mblen) { if (*wcstr >= 0xD800 && *wcstr < 0xD900) mbstr[n++] = (*wcstr & 0xFF); else if (defchr) mbstr[n++] = *defchr; wcstr++; wclen--; } return n; } else { return charset_from_unicode(&wcstr, &wclen, mbstr, mblen, codepage, NULL, defchr?defchr:NULL, defchr?1:0); } } /* * Return value is TRUE if pterm is to run in direct-to-font mode. */ int init_ucs(struct unicode_data *ucsdata, char *linecharset, int utf8_override, int font_charset, int vtmode) { int i, ret = 0; /* * In the platform-independent parts of the code, font_codepage * is used only for system DBCS support - which we don't * support at all. So we set this to something which will never * be used. */ ucsdata->font_codepage = -1; /* * If utf8_override is set and the POSIX locale settings * dictate a UTF-8 character set, then just go straight for * UTF-8. */ ucsdata->line_codepage = CS_NONE; if (utf8_override) { const char *s; if (((s = getenv("LC_ALL")) && *s) || ((s = getenv("LC_CTYPE")) && *s) || ((s = getenv("LANG")) && *s)) { if (strstr(s, "UTF-8")) ucsdata->line_codepage = CS_UTF8; } } /* * Failing that, line_codepage should be decoded from the * specification in conf. */ if (ucsdata->line_codepage == CS_NONE) ucsdata->line_codepage = decode_codepage(linecharset); /* * If line_codepage is _still_ CS_NONE, we assume we're using * the font's own encoding. This has been passed in to us, so * we use that. If it's still CS_NONE after _that_ - i.e. the * font we were given had an incomprehensible charset - then we * fall back to using the D800 page. */ if (ucsdata->line_codepage == CS_NONE) ucsdata->line_codepage = font_charset; if (ucsdata->line_codepage == CS_NONE) ret = 1; /* * Set up unitab_line, by translating each individual character * in the line codepage into Unicode. */ for (i = 0; i < 256; i++) { char c[1]; const char *p; wchar_t wc[1]; int len; c[0] = i; p = c; len = 1; if (ucsdata->line_codepage == CS_NONE) ucsdata->unitab_line[i] = 0xD800 | i; else if (1 == charset_to_unicode(&p, &len, wc, 1, ucsdata->line_codepage, NULL, L"", 0)) ucsdata->unitab_line[i] = wc[0]; else ucsdata->unitab_line[i] = 0xFFFD; } /* * Set up unitab_xterm. This is the same as unitab_line except * in the line-drawing regions, where it follows the Unicode * encoding. * * (Note that the strange X encoding of line-drawing characters * in the bottom 32 glyphs of ISO8859-1 fonts is taken care of * by the font encoding, which will spot such a font and act as * if it were in a variant encoding of ISO8859-1.) */ for (i = 0; i < 256; i++) { static const wchar_t unitab_xterm_std[32] = { 0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020 }; static const wchar_t unitab_xterm_poorman[32] = L"*#****o~**+++++-----++++|****L. "; const wchar_t *ptr; if (vtmode == VT_POORMAN) ptr = unitab_xterm_poorman; else ptr = unitab_xterm_std; if (i >= 0x5F && i < 0x7F) ucsdata->unitab_xterm[i] = ptr[i & 0x1F]; else ucsdata->unitab_xterm[i] = ucsdata->unitab_line[i]; } /* * Set up unitab_scoacs. The SCO Alternate Character Set is * simply CP437. */ for (i = 0; i < 256; i++) { char c[1]; const char *p; wchar_t wc[1]; int len; c[0] = i; p = c; len = 1; if (1 == charset_to_unicode(&p, &len, wc, 1, CS_CP437, NULL, L"", 0)) ucsdata->unitab_scoacs[i] = wc[0]; else ucsdata->unitab_scoacs[i] = 0xFFFD; } /* * Find the control characters in the line codepage. For * direct-to-font mode using the D800 hack, we assume 00-1F and * 7F are controls, but allow 80-9F through. (It's as good a * guess as anything; and my bet is that half the weird fonts * used in this way will be IBM or MS code pages anyway.) */ for (i = 0; i < 256; i++) { int lineval = ucsdata->unitab_line[i]; if (lineval < ' ' || (lineval >= 0x7F && lineval < 0xA0) || (lineval >= 0xD800 && lineval < 0xD820) || (lineval == 0xD87F)) ucsdata->unitab_ctrl[i] = i; else ucsdata->unitab_ctrl[i] = 0xFF; } return ret; } const char *cp_name(int codepage) { if (codepage == CS_NONE) return "Use font encoding"; return charset_to_localenc(codepage); } const char *cp_enumerate(int index) { int charset; charset = charset_localenc_nth(index); if (charset == CS_NONE) { /* "Use font encoding" comes after all the named charsets */ if (charset_localenc_nth(index-1) != CS_NONE) return "Use font encoding"; return NULL; } return charset_to_localenc(charset); } int decode_codepage(char *cp_name) { if (!cp_name || !*cp_name) return CS_UTF8; return charset_from_localenc(cp_name); } its-playback-time-0.2017-08-30.3c40fd3/wcwidth.c000066400000000000000000000412241333656753000204670ustar00rootroot00000000000000/* * 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 #include "putty.h" /* for prototypes */ struct interval { unsigned int first; unsigned int last; }; /* auxiliary function for binary search in interval table */ static int 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 0; 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 1; } return 0; } /* 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: * http://www.unicode.org/Public/9.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, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE}, {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE0}, {0x17000, 0x187EC}, {0x18800, 0x18AF2}, {0x1B000, 0x1B001}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {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}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6F6}, {0x1F910, 0x1F91E}, {0x1F920, 0x1F927}, {0x1F930, 0x1F930}, {0x1F933, 0x1F93E}, {0x1F940, 0x1F94B}, {0x1F950, 0x1F95E}, {0x1F980, 0x1F991}, {0x1F9C0, 0x1F9C0}, {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; } its-playback-time-0.2017-08-30.3c40fd3/xenc.c000066400000000000000000000046741333656753000177630ustar00rootroot00000000000000/* * xenc.c - translate our internal character set codes to and from * X11 character encoding names. * */ #include #include "charset.h" #include "internal.h" static const struct { const char *name; int charset; } xencs[] = { /* * Officially registered encoding names. This list is derived * from the font encodings section of * * http://ftp.x.org/pub/DOCS/registry * * Where multiple encoding names map to the same encoding id * (such as iso8859-15 and fcd8859-15), the first is considered * canonical and will be returned when translating the id to a * string. */ { "iso8859-1", CS_ISO8859_1 }, { "iso8859-2", CS_ISO8859_2 }, { "iso8859-3", CS_ISO8859_3 }, { "iso8859-4", CS_ISO8859_4 }, { "iso8859-5", CS_ISO8859_5 }, { "iso8859-6", CS_ISO8859_6 }, { "iso8859-7", CS_ISO8859_7 }, { "iso8859-8", CS_ISO8859_8 }, { "iso8859-9", CS_ISO8859_9 }, { "iso8859-10", CS_ISO8859_10 }, { "iso8859-13", CS_ISO8859_13 }, { "iso8859-14", CS_ISO8859_14 }, { "iso8859-15", CS_ISO8859_15 }, { "fcd8859-15", CS_ISO8859_15 }, { "hp-roman8", CS_HP_ROMAN8 }, { "koi8-r", CS_KOI8_R }, /* * Unofficial encoding names found in the wild. */ { "iso8859-16", CS_ISO8859_16 }, { "koi8-u", CS_KOI8_U }, { "ibm-cp437", CS_CP437 }, { "ibm-cp850", CS_CP850 }, { "ibm-cp852", CS_CP852 }, { "ibm-cp866", CS_CP866 }, { "microsoft-cp1250", CS_CP1250 }, { "microsoft-cp1251", CS_CP1251 }, { "microsoft-cp1252", CS_CP1252 }, { "microsoft-cp1253", CS_CP1253 }, { "microsoft-cp1254", CS_CP1254 }, { "microsoft-cp1255", CS_CP1255 }, { "microsoft-cp1256", CS_CP1256 }, { "microsoft-cp1257", CS_CP1257 }, { "microsoft-cp1258", CS_CP1258 }, { "mac-roman", CS_MAC_ROMAN }, { "viscii1.1-1", CS_VISCII }, { "viscii1-1", CS_VISCII }, }; const char *charset_to_xenc(int charset) { int i; for (i = 0; i < (int)lenof(xencs); i++) if (charset == xencs[i].charset) return xencs[i].name; return NULL; /* not found */ } int charset_from_xenc(const char *name) { int i; for (i = 0; i < (int)lenof(xencs); i++) { const char *p, *q; p = name; q = xencs[i].name; while (*p || *q) { if (tolower((unsigned char)*p) != tolower((unsigned char)*q)) break; p++; q++; } if (!*p && !*q) return xencs[i].charset; } return CS_NONE; /* not found */ }