https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=a7f2d458db2ed508959d2a425d326b0cc94806da commit a7f2d458db2ed508959d2a425d326b0cc94806da Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Wed Mar 18 22:04:47 2020 +0000 Fix an allocation length error Allow for the terminating null. https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=2fd5251b66ff0634579c3bfd8c0d0109fc0116b7 commit 2fd5251b66ff0634579c3bfd8c0d0109fc0116b7 Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Tue Mar 17 15:21:50 2020 +0000 Disable old Cygwin setup signing key by default Add a new option '--enable-old-keys', for if you really need to install from an old mirror for some reason. '--disable-old-keys' is still accepted, but is the default now. https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=8effde7afc285895e7a4d40a944ab2a7b9e51af1 commit 8effde7afc285895e7a4d40a944ab2a7b9e51af1 Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Tue Mar 17 14:27:02 2020 +0000 Implement paired boolean options Add some infrastructure for options which may appear with multiple prefixes. Add an implementation of paired boolean options like '--enable-foo' and '--disable-foo', or '--bar' and '--no-bar'. Add/fix tests https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=9a7997b94b5e9cf382e5094f91163f76b64ccb0f commit 9a7997b94b5e9cf382e5094f91163f76b64ccb0f Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Tue Mar 17 13:54:16 2020 +0000 Rename Start Menu folder for 32-bit installs on WoW64 This is not totally straightforward: Since setup can install Cygwin with either bitness (using the '--arch 32|64' option), we must do the right thing if this is a 64-bit installer being used to install 32-bit Cygwin, which will run under WoW, even if the installer isn't... (Naming things like this makes things consistent with Cygwin-X, which already names it's Start Menu folder 'Cygwin-X (32-bit)' on WoW64. It also ensures that there aren't collisions between any setup created shortcuts for 32-bit and 64-bits.) The desktop icon is already named 'Cygwin Terminal' or 'Cygwin64 Terminal' so doesn't need attention. Start menu links made in the 'Cygwin' folder by cygwin-doc, and possibly other packages will also need adjusting. https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=e73fb694a2d21f3cf36e658d9dd066b8ceaaa330 commit e73fb694a2d21f3cf36e658d9dd066b8ceaaa330 Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Tue Mar 17 13:52:55 2020 +0000 Factor out WoW detection Factor out WoW detection as a separate function Diff: --- archive.cc | 2 +- crypto.cc | 7 ++-- desktop.cc | 13 ++++-- libgetopt++/include/getopt++/BoolOption.h | 17 ++++++-- libgetopt++/include/getopt++/Option.h | 6 ++- libgetopt++/include/getopt++/OptionSet.h | 3 +- libgetopt++/include/getopt++/StringArrayOption.h | 2 +- libgetopt++/include/getopt++/StringOption.h | 2 +- libgetopt++/src/BoolOption.cc | 40 ++++++++++++++++--- libgetopt++/src/Option.cc | 7 ++++ libgetopt++/src/OptionSet.cc | 50 +++++++++++++++--------- libgetopt++/src/StringArrayOption.cc | 2 +- libgetopt++/src/StringOption.cc | 2 +- libgetopt++/tests/BoolOptionTest.cc | 26 +++++++++--- libgetopt++/tests/OptionSet.cc | 4 +- nio-ie5.cc | 45 ++++++++------------- win32.cc | 26 ++++++++++++ win32.h | 2 + 18 files changed, 177 insertions(+), 79 deletions(-) diff --git a/archive.cc b/archive.cc index e4af4f7b..1ceb3552 100644 --- a/archive.cc +++ b/archive.cc @@ -173,7 +173,7 @@ archive::extract_file (archive * source, const std::string& prefixURL, break; case ARCHIVE_FILE_DIRECTORY: { - char *path = (char *) alloca (destfilename.size()); + char *path = (char *) alloca (destfilename.size() + 1); strcpy (path, destfilename.c_str()); while (path[0] && path[strlen (path) - 1] == '/') path[strlen (path) - 1] = 0; diff --git a/crypto.cc b/crypto.cc index 2e4ba218..88ced387 100644 --- a/crypto.cc +++ b/crypto.cc @@ -52,8 +52,9 @@ static BoolOption UntrustedKeysOption (false, 'u', "untrusted-keys", "Use untrusted saved extra keys"); static BoolOption KeepUntrustedKeysOption (false, 'U', "keep-untrusted-keys", "Use untrusted keys and retain all"); -static BoolOption DisableOldKeysOption (false, '\0', "disable-old-keys", - "Disable old cygwin.com keys"); +static BoolOption EnableOldKeysOption (false, '\0', "old-keys", + "Enable old cygwin.com keys", + BoolOption::BoolOptionType::pairedAble); /* Embedded public half of Cygwin signing key. */ static const char *cygwin_pubkey_sexpr = @@ -708,7 +709,7 @@ verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner) /* If not disabled, also try the old built-in key */ gcry_sexp_t cygwin_old_key; - if (!DisableOldKeysOption) + if (EnableOldKeysOption) { rv = gcry_sexp_new (&cygwin_old_key, cygwin_old_pubkey_sexpr, strlen (cygwin_old_pubkey_sexpr), 1); if (rv != GPG_ERR_NO_ERROR) diff --git a/desktop.cc b/desktop.cc index eec8ca91..29dbf635 100644 --- a/desktop.cc +++ b/desktop.cc @@ -95,6 +95,14 @@ make_link (const std::string& linkpath, icon.c_str(), fname.c_str()); } +static const char *startmenudir() +{ + if (!is_64bit && (WowNativeMachine() != IMAGE_FILE_MACHINE_I386)) + return "/Cygwin (32-bit)"; + else + return "/Cygwin"; +} + static void start_menu (const std::string& title, const std::string& target, const std::string& arg, const std::string& iconpath) @@ -107,7 +115,7 @@ start_menu (const std::string& title, const std::string& target, issystem ? CSIDL_COMMON_PROGRAMS : CSIDL_PROGRAMS, &id); SHGetPathFromIDList (id, path); - strncat (path, "/Cygwin", MAX_PATH - strlen(path) - 1); + strncat (path, startmenudir(), MAX_PATH - strlen(path) - 1); LogBabblePrintf ("Program directory for program link: %s", path); make_link (path, title, target, arg, iconpath); } @@ -194,7 +202,6 @@ save_icon (std::string &iconpath, const char *resource_name) #define TERMINALICON "/Cygwin-Terminal.ico" #define TERMINALTITLE (is_64bit ? "Cygwin64 Terminal" \ : "Cygwin Terminal") -#define STARTMENUDIR "/Cygwin" static void do_desktop_setup () @@ -300,7 +307,7 @@ check_startmenu (const std::string title, const std::string target) CSIDL_PROGRAMS, &id); SHGetPathFromIDList (id, path); LogBabblePrintf ("Program directory for program link: %s", path); - strcat (path, STARTMENUDIR); + strcat (path, startmenudir()); std::string fname = std::string(path) + "/" + title + ".lnk"; if (_access (fname.c_str(), 0) == 0) diff --git a/libgetopt++/include/getopt++/BoolOption.h b/libgetopt++/include/getopt++/BoolOption.h index 35227cb1..8b20e1cc 100644 --- a/libgetopt++/include/getopt++/BoolOption.h +++ b/libgetopt++/include/getopt++/BoolOption.h @@ -23,17 +23,25 @@ class BoolOption : public Option { public: + enum class BoolOptionType + { + simple, + pairedAble, + pairedNo, + }; + BoolOption(bool const defaultvalue, char shortopt, char const *longopt = 0, - std::string const &shorthelp = std::string(), - OptionSet &owner=GetOption::GetInstance()); + std::string const &shorthelp = std::string(), + BoolOptionType type = BoolOptionType::simple, + OptionSet &owner=GetOption::GetInstance()); virtual ~ BoolOption (); virtual std::string const shortOption () const; virtual std::string const longOption () const; + virtual std::vector<std::string> const & longOptionPrefixes () const; virtual std::string const shortHelp () const; - virtual Result Process (char const *); + virtual Result Process (char const *, int); virtual Argument argument () const; operator bool () const; - private: bool _value; @@ -41,6 +49,7 @@ private: char _shortopt; char const *_longopt; std::string _shorthelp; + BoolOptionType _type; }; #endif // _BOOLOPTION_H_ diff --git a/libgetopt++/include/getopt++/Option.h b/libgetopt++/include/getopt++/Option.h index a32f9495..b3b140ff 100644 --- a/libgetopt++/include/getopt++/Option.h +++ b/libgetopt++/include/getopt++/Option.h @@ -21,9 +21,10 @@ #endif #if HAVE_STRING_H #include <string> -#else +#else #error "<string> required" #endif +#include <vector> // Each registered option must implement this class. class Option @@ -32,13 +33,14 @@ public: virtual ~ Option (); virtual std::string const shortOption () const = 0; virtual std::string const longOption () const = 0; + virtual std::vector<std::string> const & longOptionPrefixes () const; virtual std::string const shortHelp () const = 0; enum Result { Failed, Ok, Stop }; - virtual Result Process (char const *) = 0; + virtual Result Process (char const *, int) = 0; enum Argument { None, Optional, diff --git a/libgetopt++/include/getopt++/OptionSet.h b/libgetopt++/include/getopt++/OptionSet.h index 4ccaddaf..dbd8046b 100644 --- a/libgetopt++/include/getopt++/OptionSet.h +++ b/libgetopt++/include/getopt++/OptionSet.h @@ -44,7 +44,8 @@ private: bool isOption(std::string::size_type) const; void doOption(std::string &option, std::string::size_type const &pos); bool doNoArgumentOption(std::string &option, std::string::size_type const &pos); - Option * findOption(std::string &option, std::string::size_type const &pos) const; + void findOption(std::string &option, std::string::size_type const &pos, + Option *&theOption, int & prefixIndex) const; std::vector<Option *> options; std::vector<std::string> argv; std::vector<std::string> nonoptions; diff --git a/libgetopt++/include/getopt++/StringArrayOption.h b/libgetopt++/include/getopt++/StringArrayOption.h index d3f87c0d..b589d283 100644 --- a/libgetopt++/include/getopt++/StringArrayOption.h +++ b/libgetopt++/include/getopt++/StringArrayOption.h @@ -29,7 +29,7 @@ public: virtual std::string const shortOption () const; virtual std::string const longOption () const; virtual std::string const shortHelp () const; - virtual Result Process (char const *); + virtual Result Process (char const *, int); virtual Argument argument () const; operator std::vector<std::string> () const; diff --git a/libgetopt++/include/getopt++/StringOption.h b/libgetopt++/include/getopt++/StringOption.h index f13be8cb..37e723a6 100644 --- a/libgetopt++/include/getopt++/StringOption.h +++ b/libgetopt++/include/getopt++/StringOption.h @@ -30,7 +30,7 @@ public: virtual std::string const shortOption () const; virtual std::string const longOption () const; virtual std::string const shortHelp () const; - virtual Result Process (char const *); + virtual Result Process (char const *, int); virtual Argument argument () const; operator const std::string& () const; diff --git a/libgetopt++/src/BoolOption.cc b/libgetopt++/src/BoolOption.cc index 4b265539..37d9a229 100644 --- a/libgetopt++/src/BoolOption.cc +++ b/libgetopt++/src/BoolOption.cc @@ -16,10 +16,10 @@ #include <getopt++/BoolOption.h> BoolOption::BoolOption(bool const defaultvalue, char shortopt, - char const *longopt, std::string const &shorthelp, - OptionSet &owner) : _value (defaultvalue) , - _ovalue (defaultvalue), _shortopt(shortopt), - _longopt (longopt), _shorthelp (shorthelp) + char const *longopt, std::string const &shorthelp, + BoolOptionType type, OptionSet &owner) : + _value (defaultvalue), _ovalue (defaultvalue), _shortopt(shortopt), + _longopt (longopt), _shorthelp (shorthelp), _type(type) { owner.Register (this); }; @@ -38,6 +38,24 @@ BoolOption::longOption () const return _longopt; } +std::vector<std::string> const & +BoolOption::longOptionPrefixes () const +{ + switch (_type) + { + default: + case BoolOption::BoolOptionType::simple: + static std::vector<std::string> simple = {""}; + return simple; + case BoolOption::BoolOptionType::pairedAble: + static std::vector<std::string> able = {"enable-", "disable-"}; + return able; + case BoolOption::BoolOptionType::pairedNo: + static std::vector<std::string> no = {"", "no-"}; + return no; + } +} + std::string const BoolOption::shortHelp () const { @@ -45,9 +63,19 @@ BoolOption::shortHelp () const } Option::Result -BoolOption::Process (char const *) +BoolOption::Process (char const *, int prefixIndex) { - _value = !_ovalue; + switch (_type) + { + default: + case BoolOption::BoolOptionType::simple: + _value = !_ovalue; + case BoolOption::BoolOptionType::pairedAble: + _value = (prefixIndex == 0); + case BoolOption::BoolOptionType::pairedNo: + _value = (prefixIndex == 0); + } + return Ok; } diff --git a/libgetopt++/src/Option.cc b/libgetopt++/src/Option.cc index 7c61ebad..2e6e9d8f 100644 --- a/libgetopt++/src/Option.cc +++ b/libgetopt++/src/Option.cc @@ -22,3 +22,10 @@ Option::Option () : present(false) Option::~Option () { } + +std::vector<std::string> const & +Option::longOptionPrefixes () const +{ + static std::vector<std::string> noprefix = {""}; + return noprefix; +} diff --git a/libgetopt++/src/OptionSet.cc b/libgetopt++/src/OptionSet.cc index a8f02e82..5a8ddee4 100644 --- a/libgetopt++/src/OptionSet.cc +++ b/libgetopt++/src/OptionSet.cc @@ -38,37 +38,47 @@ OptionSet::processOne() if (!isOption(pos)) { /* Push the non option into storage */ - if (nonOptionHandler) { - lastResult = nonOptionHandler->Process(option.c_str()); - } else { - nonoptions.push_back(option); - lastResult = Option::Ok; - } + if (nonOptionHandler) { + lastResult = nonOptionHandler->Process(option.c_str(), 0); + } else { + nonoptions.push_back(option); + lastResult = Option::Ok; + } } else { - doOption(option, pos); + doOption(option, pos); } } -Option * -OptionSet::findOption(std::string &option, std::string::size_type const &pos) const +void +OptionSet::findOption(std::string &option, std::string::size_type const &pos, + Option *&theOption, int & prefixIndex) const { - Option *theOption = NULL; + theOption = NULL; + prefixIndex = 0; for (std::vector<Option *>::const_iterator i = options.begin(); i != options.end(); ++i) { if (pos == 1) { if (option[0] == (*i)->shortOption()[0]) { theOption = (*i); + return; } } else { - /* pos == 2 : todo - prefix matches */ - - if (option.find((*i)->longOption()) == 0) { + /* pos == 2 */ + std::vector<std::string> prefixes = (*i)->longOptionPrefixes(); + for (std::vector<std::string>::const_iterator j = prefixes.begin(); + j != prefixes.end(); + j++) + { + std::string prefixedOption = *j + (*i)->longOption(); + if (option.find(prefixedOption) == 0) { theOption = (*i); + prefixIndex = j - prefixes.begin(); + return; + } } } } - return theOption; } bool @@ -98,7 +108,9 @@ OptionSet::doOption(std::string &option, std::string::size_type const &pos) { lastResult = Option::Failed; option.erase(0, pos); - Option *theOption = findOption(option, pos); + Option *theOption = NULL; + int prefixIndex = 0; + findOption(option, pos, theOption, prefixIndex); char const *optionValue = NULL; std::string value; @@ -108,9 +120,9 @@ OptionSet::doOption(std::string &option, std::string::size_type const &pos) switch (theOption->argument()) { case Option::None: - if (!doNoArgumentOption (option, pos)) - return; - break; + if (!doNoArgumentOption (option, pos)) + return; + break; case Option::Optional: { if (pos == 1) { @@ -231,7 +243,7 @@ OptionSet::doOption(std::string &option, std::string::size_type const &pos) break; } theOption->setPresent(true); - lastResult = theOption->Process(optionValue); + lastResult = theOption->Process(optionValue, prefixIndex); } OptionSet::OptionSet () {} diff --git a/libgetopt++/src/StringArrayOption.cc b/libgetopt++/src/StringArrayOption.cc index 7cbee201..fe6f6131 100644 --- a/libgetopt++/src/StringArrayOption.cc +++ b/libgetopt++/src/StringArrayOption.cc @@ -44,7 +44,7 @@ StringArrayOption::shortHelp () const } Option::Result -StringArrayOption::Process (char const *optarg) +StringArrayOption::Process (char const *optarg, int prefixIndex) { if (optarg) { diff --git a/libgetopt++/src/StringOption.cc b/libgetopt++/src/StringOption.cc index d3592366..e3c4af64 100644 --- a/libgetopt++/src/StringOption.cc +++ b/libgetopt++/src/StringOption.cc @@ -49,7 +49,7 @@ StringOption::shortHelp () const } Option::Result -StringOption::Process (char const *optarg) +StringOption::Process (char const *optarg, int prefixIndex) { if (optarg) _value = optarg; diff --git a/libgetopt++/tests/BoolOptionTest.cc b/libgetopt++/tests/BoolOptionTest.cc index d2c51909..7fdd2591 100644 --- a/libgetopt++/tests/BoolOptionTest.cc +++ b/libgetopt++/tests/BoolOptionTest.cc @@ -20,16 +20,18 @@ #include <iostream> #include <string.h> -static BoolOption testoption (false, 't', "testoption", "Tests the use of boolean options"); int main (int anargc, char **anargv) { + BoolOption helpoption (false, 'h', "help", "Tests the use of help output."); + BoolOption helpoption2 (false, 'o', "help2", "Tests the use of help output."); + BoolOption ableoption (false, '\0', "foo", "Tests the use of paired option.", BoolOption::BoolOptionType::pairedAble); + int argc=2; char *argv[4]; argv[0] = strdup("BoolOptionTest"); argv[1] = strdup("-h"); { - BoolOption helpoption (false, 'h', "help", "Tests the use of help output."); if (!GetOption::GetInstance().Process (argc, argv, NULL)) { std::cout << "Failed to process options" << std::endl; @@ -44,17 +46,31 @@ main (int anargc, char **anargv) argc = 0; } { - BoolOption helpoption (false, 'h', "help", "Tests the use of help output."); if (!GetOption::GetInstance().Process (argc, argv, NULL)) { std::cout << "Failed to process options (2) " << std::endl; return 1; } - if (helpoption) + if (helpoption2) { - std::cout << "Recieved unexpected help option" << std::endl; + std::cout << "Received unexpected help option" << std::endl; return 1; } } + argc=2; + argv[0] = strdup("BoolOptionTest"); + argv[1] = strdup("--enable-foo"); + { + if (!GetOption::GetInstance().Process (argc, argv, NULL)) + { + std::cout << "Failed to process options (3) " << std::endl; + return 1; + } + if (!ableoption) + { + std::cout << "Did not receive expected enable-foo option" << std::endl; + return 1; + } + } return 0; } diff --git a/libgetopt++/tests/OptionSet.cc b/libgetopt++/tests/OptionSet.cc index ff63e390..4eac880c 100644 --- a/libgetopt++/tests/OptionSet.cc +++ b/libgetopt++/tests/OptionSet.cc @@ -36,7 +36,7 @@ class StringCollector : public Option { return ""; } - virtual Option::Result Process(const char * value); + virtual Option::Result Process(const char * value, int index); virtual Option::Argument argument() const { return Required; @@ -46,7 +46,7 @@ class StringCollector : public Option }; Option::Result -StringCollector::Process(const char * value) +StringCollector::Process(const char * value, int index) { values.push_back(value); if (values.size() == 1) diff --git a/nio-ie5.cc b/nio-ie5.cc index f5ad020c..fe61b778 100644 --- a/nio-ie5.cc +++ b/nio-ie5.cc @@ -52,37 +52,24 @@ determine_default_useragent(void) #ifdef __x86_64__ bitness = "Win64"; #else - typedef BOOL (WINAPI *PFNISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); - PFNISWOW64PROCESS2 pfnIsWow64Process2 = (PFNISWOW64PROCESS2)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process2"); - - typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL); - PFNISWOW64PROCESS pfnIsWow64Process = (PFNISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process"); - + USHORT nativeMachine = WowNativeMachine(); std::stringstream native_desc; - USHORT processMachine, nativeMachine; - if ((pfnIsWow64Process2) && - (pfnIsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine))) { - switch (nativeMachine) - { - case IMAGE_FILE_MACHINE_I386: - bitness = "Win32"; - break; - case IMAGE_FILE_MACHINE_AMD64: - bitness = "WoW64"; - break; - case IMAGE_FILE_MACHINE_ARM64: - bitness = "WoW64-ARM64"; - break; - default: - native_desc << "WoW64-" << std::hex << nativeMachine; - bitness = native_desc.str(); - } - } else if (pfnIsWow64Process) { - BOOL bIsWow64 = FALSE; - if (pfnIsWow64Process(GetCurrentProcess(), &bIsWow64)) - bitness = bIsWow64 ? "WoW64" : "Win32"; - } + switch (nativeMachine) + { + case IMAGE_FILE_MACHINE_I386: + bitness = "Win32"; + break; + case IMAGE_FILE_MACHINE_AMD64: + bitness = "WoW64"; + break; + case IMAGE_FILE_MACHINE_ARM64: + bitness = "WoW64-ARM64"; + break; + default: + native_desc << "WoW64-" << std::hex << nativeMachine; + bitness = native_desc.str(); + } #endif default_useragent = std::string("Cygwin-Setup/") + setup_version + " (" + os.str() + ";" + bitness + ")"; return default_useragent; diff --git a/win32.cc b/win32.cc index 6a551bac..45c7bf1b 100644 --- a/win32.cc +++ b/win32.cc @@ -393,3 +393,29 @@ VersionInfo& GetVer () static VersionInfo *vi = new VersionInfo (); return *vi; } + +/* Identify native machine arch if we are running under WoW */ +USHORT +WowNativeMachine () +{ +#ifdef __x86_64__ + return IMAGE_FILE_MACHINE_AMD64; +#else + typedef BOOL (WINAPI *PFNISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); + PFNISWOW64PROCESS2 pfnIsWow64Process2 = (PFNISWOW64PROCESS2)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process2"); + + typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL); + PFNISWOW64PROCESS pfnIsWow64Process = (PFNISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process"); + + USHORT processMachine, nativeMachine; + if ((pfnIsWow64Process2) && + (pfnIsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine))) + return nativeMachine; + else if (pfnIsWow64Process) { + BOOL bIsWow64 = FALSE; + if (pfnIsWow64Process(GetCurrentProcess(), &bIsWow64)) + return bIsWow64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_MACHINE_I386; + } + return IMAGE_FILE_MACHINE_I386; +#endif +} diff --git a/win32.h b/win32.h index 1b9db490..a7d025d2 100644 --- a/win32.h +++ b/win32.h @@ -177,6 +177,8 @@ VersionInfo& GetVer (); #define OSMinorVersion() (GetVer ().minor ()) #define OSBuildNumber() (GetVer ().buildNumber ()) +USHORT WowNativeMachine (); + static inline void GetDlgItemRect (HWND h, int item, LPRECT r) {