diff options
Diffstat (limited to 'tdecore/network/ksocketaddress.cpp')
-rw-r--r-- | tdecore/network/ksocketaddress.cpp | 957 |
1 files changed, 957 insertions, 0 deletions
diff --git a/tdecore/network/ksocketaddress.cpp b/tdecore/network/ksocketaddress.cpp new file mode 100644 index 000000000..feaabfab5 --- /dev/null +++ b/tdecore/network/ksocketaddress.cpp @@ -0,0 +1,957 @@ +/* -*- C++ -*- + * Copyright (C) 2003 Thiago Macieira <thiago.macieira@kdemail.net> + * + * + * 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 OR 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. + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <tqfile.h> +#include <tqobject.h> + +#include "klocale.h" +#include "ksocketaddress.h" + +#include "netsupp.h" + +using namespace KNetwork; + +#if 0 +class KIpAddress_localhostV4 : public KIpAddress +{ +public: + KIpAddress_localhostV4() + { + *m_data = htonl(0x7f000001); + m_version = 4; + } +}; + +class KIpAddress_localhostV6 : public KIpAddress +{ +public: + KIpAddress_localhostV6() + : KIpAddress(0L, 6) + { + m_data[3] = htonl(1); + } +}; +#endif + +static const char localhostV4_data[] = { 127, 0, 0, 1 }; +static const char localhostV6_data[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,1 }; + +const KIpAddress KIpAddress::localhostV4(&localhostV4_data, 4); +const KIpAddress KIpAddress::localhostV6(&localhostV6_data, 6); +const KIpAddress KIpAddress::anyhostV4(0L, 4); +const KIpAddress KIpAddress::anyhostV6(0L, 6); + +// helper function to test if an IPv6 v4-mapped address is equal to its IPv4 counterpart +static bool check_v4mapped(const TQ_UINT32* v6addr, TQ_UINT32 v4addr) +{ + // check that the v6 is a v4-mapped address + if (!(v6addr[0] == 0 && v6addr[1] == 0 && v6addr[2] == htonl(0x0000ffff))) + return false; // not a v4-mapped address + + return v6addr[3] == v4addr; +} + +// copy operator +KIpAddress& KIpAddress::operator =(const KIpAddress& other) +{ + m_version = other.m_version; + if (m_version == 4 || m_version == 6) + memcpy(m_data, other.m_data, sizeof(m_data)); + return *this; +} + +// comparison +bool KIpAddress::compare(const KIpAddress& other, bool checkMapped) const +{ + if (m_version == other.m_version) + switch (m_version) + { + case 0: + // both objects are empty + return true; + + case 4: + // IPv4 address + return *m_data == *other.m_data; + + case 6: + // IPv6 address + // they are 128-bit long, that is, 16 bytes + return memcmp(m_data, other.m_data, 16) == 0; + } + + if (checkMapped) + { + // check the possibility of a v4-mapped address being compared to an IPv4 one + if (m_version == 6 && other.m_version == 4 && check_v4mapped(m_data, *other.m_data)) + return true; + + if (other.m_version == 6 && m_version == 4 && check_v4mapped(other.m_data, *m_data)) + return true; + } + + return false; +} + +// sets the address to the given address +bool KIpAddress::setAddress(const TQString& address) +{ + m_version = 0; + + // try to guess the address version + if (address.find(':') != -1) + { +#ifdef AF_INET6 + // guessing IPv6 + + TQ_UINT32 buf[4]; + if (inet_pton(AF_INET6, address.latin1(), buf)) + { + memcpy(m_data, buf, sizeof(m_data)); + m_version = 6; + return true; + } +#endif + + return false; + } + else + { + TQ_UINT32 buf; + if (inet_pton(AF_INET, address.latin1(), &buf)) + { + *m_data = buf; + m_version = 4; + return true; + } + + return false; + } + + return false; // can never happen! +} + +bool KIpAddress::setAddress(const char* address) +{ + return setAddress(TQString::tqfromLatin1(address)); +} + +// set from binary data +bool KIpAddress::setAddress(const void* raw, int version) +{ + // this always succeeds + // except if version is invalid + if (version != 4 && version != 6) + return false; + + m_version = version; + if (raw != 0L) + memcpy(m_data, raw, version == 4 ? 4 : 16); + else + memset(m_data, 0, 16); + + return true; +} + +// presentation form +TQString KIpAddress::toString() const +{ + char buf[sizeof "1111:2222:3333:4444:5555:6666:255.255.255.255" + 2]; + buf[0] = '\0'; + switch (m_version) + { + case 4: + inet_ntop(AF_INET, m_data, buf, sizeof(buf) - 1); + return TQString::tqfromLatin1(buf); + + case 6: +#ifdef AF_INET6 + inet_ntop(AF_INET6, m_data, buf, sizeof(buf) - 1); +#endif + return TQString::tqfromLatin1(buf); + } + + return TQString::null; +} + +TQ_UINT32 KIpAddress::hostIPv4Addr(bool convertMapped) const +{ + TQ_UINT32 addr = IPv4Addr(convertMapped); + return ntohl(addr); +} + +/* + * An IPv6 socket address + * This is taken from RFC 2553. + */ +struct our_sockaddr_in6 +{ +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + TQ_UINT8 sin6_len; + TQ_UINT8 sin6_family; +# else //!HAVE_STRUCT_SOCKADDR_SA_LEN + TQ_UINT16 sin6_family; +# endif + TQ_UINT16 sin6_port; /* RFC says in_port_t */ + TQ_UINT32 sin6_flowinfo; + TQ_UINT8 sin6_addr[16]; // 24 bytes up to here + TQ_UINT32 sin6_scope_id; // 28 bytes total +}; + +// useful definitions +#define MIN_SOCKADDR_LEN sizeof(TQ_UINT16) +#define SOCKADDR_IN_LEN sizeof(sockaddr_in) +#define MIN_SOCKADDR_IN6_LEN ((unsigned long) &(((our_sockaddr_in6*)0)->sin6_scope_id)) +#define SOCKADDR_IN6_LEN sizeof(our_sockaddr_in6) +#define MIN_SOCKADDR_UN_LEN (sizeof(TQ_UINT16) + sizeof(char)) + + +class KNetwork::KSocketAddressData +{ +public: + /* + * Note: maybe this should be virtual + * But since the data is shared via the d pointer, it doesn't really matter + * what one class sees, so will the other + */ + class QMixSocketAddressRef : public KInetSocketAddress, public KUnixSocketAddress + { + public: + QMixSocketAddressRef(KSocketAddressData* d) + : KInetSocketAddress(d), KUnixSocketAddress(d) + { + } + }; + QMixSocketAddressRef ref; + + union + { + struct sockaddr *generic; + struct sockaddr_in *in; + struct our_sockaddr_in6 *in6; + struct sockaddr_un *un; + } addr; + TQ_UINT16 curlen, reallen; + + KSocketAddressData() + : ref(this) + { + addr.generic = 0L; + curlen = 0; + tqinvalidate(); + } + + ~KSocketAddressData() + { + if (addr.generic != 0L) + free(addr.generic); + } + + inline bool invalid() const + { return reallen == 0; } + + inline void tqinvalidate() + { reallen = 0; } + + void dup(const sockaddr* sa, TQ_UINT16 len, bool clear = true); + + void makeipv4() + { + short oldport = 0; + if (!invalid()) + switch (addr.generic->sa_family) + { + case AF_INET: + return; // nothing to do here +#ifdef AF_INET6 + case AF_INET6: + oldport = addr.in6->sin6_port; + break; +#endif + } + + // create new space + dup(0L, SOCKADDR_IN_LEN); + + addr.in->sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + addr.in->sin_len = SOCKADDR_IN_LEN; +#endif + addr.in->sin_port = oldport; + } + + void makeipv6() + { + short oldport = 0; + if (!invalid()) + switch (addr.generic->sa_family) + { + case AF_INET: + oldport = addr.in->sin_port; + break; + +#ifdef AF_INET6 + case AF_INET6: + return; // nothing to do here +#endif + } + + // make room + dup(0L, SOCKADDR_IN6_LEN); +#ifdef AF_INET6 + addr.in6->sin6_family = AF_INET6; +#endif +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + addr.in6->sin6_len = SOCKADDR_IN6_LEN; +#endif + addr.in6->sin6_port = oldport; + // sin6_scope_id and sin6_flowid are zero + } + +}; + +// create duplicates of +void KSocketAddressData::dup(const sockaddr* sa, TQ_UINT16 len, bool clear) +{ + if (len < MIN_SOCKADDR_LEN) + { + // certainly invalid + tqinvalidate(); + return; + } + + if (sa && ((sa->sa_family == AF_INET && len < SOCKADDR_IN_LEN) || +#ifdef AF_INET6 + (sa->sa_family == AF_INET6 && len < MIN_SOCKADDR_IN6_LEN) || +#endif + (sa->sa_family == AF_UNIX && len < MIN_SOCKADDR_UN_LEN))) + { + // also invalid + tqinvalidate(); + return; + } + + // good + reallen = len; + if (len > curlen) + { + if (len < 32) + curlen = 32; // big enough for sockaddr_in and sockaddr_in6 + else + curlen = len; + addr.generic = (sockaddr*)realloc(addr.generic, curlen); + } + + if (sa != 0L) + { + memcpy(addr.generic, sa, len); // copy + + // now, normalise the data + if (addr.generic->sa_family == AF_INET) + reallen = SOCKADDR_IN_LEN; // no need to be larger +#ifdef AF_INET6 + else if (addr.generic->sa_family == AF_INET6) + { + // set the extra field (sin6_scope_id) + + // the buffer is never smaller than 32 bytes, so this is always + // allowed + if (reallen < SOCKADDR_IN6_LEN) + addr.in6->sin6_scope_id = 0; + + reallen = SOCKADDR_IN6_LEN; + } +#endif + else if (addr.generic->sa_family == AF_UNIX) + reallen = MIN_SOCKADDR_UN_LEN + strlen(addr.un->sun_path); + } + else if (clear) + { + memset(addr.generic, 0, len); + addr.generic->sa_family = AF_UNSPEC; + } +} + +// default constructor +KSocketAddress::KSocketAddress() + : d(new KSocketAddressData) +{ +} + +// constructor from binary data +KSocketAddress::KSocketAddress(const sockaddr *sa, TQ_UINT16 len) + : d(new KSocketAddressData) +{ + setAddress(sa, len); +} + +KSocketAddress::KSocketAddress(const KSocketAddress& other) + : d(new(KSocketAddressData)) +{ + *this = other; +} + +KSocketAddress::KSocketAddress(KSocketAddressData *d2) + : d(d2) +{ +} + +KSocketAddress::~KSocketAddress() +{ + // prevent double-deletion, since we're already being deleted + if (d) + { + d->ref.KInetSocketAddress::d = 0L; + d->ref.KUnixSocketAddress::d = 0L; + delete d; + } +} + +KSocketAddress& KSocketAddress::operator =(const KSocketAddress& other) +{ + if (other.d && !other.d->invalid()) + d->dup(other.d->addr.generic, other.d->reallen); + else + d->tqinvalidate(); + return *this; +} + +const sockaddr* KSocketAddress::address() const +{ + if (d->invalid()) + return 0L; + return d->addr.generic; +} + +sockaddr* KSocketAddress::address() +{ + if (d->invalid()) + return 0L; + return d->addr.generic; +} + +KSocketAddress& KSocketAddress::setAddress(const sockaddr* sa, TQ_UINT16 len) +{ + if (sa != 0L && len >= MIN_SOCKADDR_LEN) + d->dup(sa, len); + else + d->tqinvalidate(); + + return *this; +} + +TQ_UINT16 KSocketAddress::length() const +{ + if (d->invalid()) + return 0; + return d->reallen; +} + +KSocketAddress& KSocketAddress::setLength(TQ_UINT16 len) +{ + d->dup((sockaddr*)0L, len, false); + + return *this; +} + +int KSocketAddress::family() const +{ + if (d->invalid()) + return AF_UNSPEC; + return d->addr.generic->sa_family; +} + +KSocketAddress& KSocketAddress::setFamily(int family) +{ + if (d->invalid()) + d->dup((sockaddr*)0L, MIN_SOCKADDR_LEN); + d->addr.generic->sa_family = family; + + return *this; +} + +bool KSocketAddress::operator ==(const KSocketAddress& other) const +{ + // if this is invalid, it's only equal if the other one is invalid as well + if (d->invalid()) + return other.d->invalid(); + + // check the family to make sure we don't do unnecessary comparison + if (d->addr.generic->sa_family != other.d->addr.generic->sa_family) + return false; // not the same family, not equal + + // same family then + // check the ones we know already + switch (d->addr.generic->sa_family) + { + case AF_INET: + Q_ASSERT(d->reallen == SOCKADDR_IN_LEN); + Q_ASSERT(other.d->reallen == SOCKADDR_IN_LEN); + return memcmp(d->addr.in, other.d->addr.in, SOCKADDR_IN_LEN) == 0; + +#ifdef AF_INET6 + case AF_INET6: + Q_ASSERT(d->reallen >= MIN_SOCKADDR_IN6_LEN); + Q_ASSERT(other.d->reallen >= MIN_SOCKADDR_IN6_LEN); + +# if !defined(HAVE_STRUCT_SOCKADDR_IN6) || defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID) + // check for the case where sin6_scope_id isn't present + if (d->reallen != other.d->reallen) + { + if (memcmp(d->addr.in6, other.d->addr.in6, MIN_SOCKADDR_IN6_LEN) != 0) + return false; // not equal + if (d->reallen > other.d->reallen) + return d->addr.in6->sin6_scope_id == 0; + else + return other.d->addr.in6->sin6_scope_id == 0; + } +# endif + + return memcmp(d->addr.in6, other.d->addr.in6, d->reallen) == 0; +#endif + + case AF_UNIX: + Q_ASSERT(d->reallen >= MIN_SOCKADDR_UN_LEN); + Q_ASSERT(other.d->reallen >= MIN_SOCKADDR_UN_LEN); + + // do a string comparison here + return strcmp(d->addr.un->sun_path, other.d->addr.un->sun_path) == 0; + + default: + // something else we don't know about + // they are equal if and only if they are exactly equal + if (d->reallen == other.d->reallen) + return memcmp(d->addr.generic, other.d->addr.generic, d->reallen) == 0; + } + + return false; // not equal in any other case +} + +TQString KSocketAddress::nodeName() const +{ + if (d->invalid()) + return TQString::null; + + switch (d->addr.generic->sa_family) + { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: + + TQString scopeid("%"); + if (d->addr.generic->sa_family == AF_INET6 && d->addr.in6->sin6_scope_id) + scopeid += TQString::number(d->addr.in6->sin6_scope_id); + else + scopeid.truncate(0); + return d->ref.ipAddress().toString() + scopeid; +#else + return d->ref.ipAddress().toString(); +#endif + } + + // any other case, including AF_UNIX + return TQString::null; +} + +TQString KSocketAddress::serviceName() const +{ + if (d->invalid()) + return TQString::null; + + switch (d->addr.generic->sa_family) + { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + return TQString::number(d->ref.port()); + + case AF_UNIX: + return d->ref.pathname(); + } + + return TQString::null; +} + +TQString KSocketAddress::toString() const +{ + if (d->invalid()) + return TQString::null; + + TQString fmt; + + if (d->addr.generic->sa_family == AF_INET) + fmt = "%1:%2"; +#ifdef AF_INET6 + else if (d->addr.generic->sa_family == AF_INET6) + fmt = "[%1]:%2"; +#endif + else if (d->addr.generic->sa_family == AF_UNIX) + return TQString::tqfromLatin1("unix:%1").arg(serviceName()); + else + return i18n("1: the unknown socket address family number", + "Unknown family %1").arg(d->addr.generic->sa_family); + + return fmt.arg(nodeName()).arg(serviceName()); +} + +KInetSocketAddress& KSocketAddress::asInet() +{ + return d->ref; +} + +KInetSocketAddress KSocketAddress::asInet() const +{ + return d->ref; +} + +KUnixSocketAddress& KSocketAddress::asUnix() +{ + return d->ref; +} + +KUnixSocketAddress KSocketAddress::asUnix() const +{ + return d->ref; +} + +int KSocketAddress::ianaFamily(int af) +{ + switch (af) + { + case AF_INET: + return 1; + +#ifdef AF_INET6 + case AF_INET6: + return 2; +#endif + + default: + return 0; + } +} + +int KSocketAddress::fromIanaFamily(int iana) +{ + switch (iana) + { + case 1: + return AF_INET; + +#ifdef AF_INET6 + case 2: + return AF_INET6; +#endif + + default: + return AF_UNSPEC; + } +} + +// default constructor +KInetSocketAddress::KInetSocketAddress() +{ +} + +// binary data constructor +KInetSocketAddress::KInetSocketAddress(const sockaddr* sa, TQ_UINT16 len) + : KSocketAddress(sa, len) +{ + if (!d->invalid()) + update(); +} + +// create from IP and port +KInetSocketAddress::KInetSocketAddress(const KIpAddress& host, TQ_UINT16 port) +{ + setHost(host); + setPort(port); +} + +// copy constructor +KInetSocketAddress::KInetSocketAddress(const KInetSocketAddress& other) + : KSocketAddress(other) +{ +} + +// special copy constructor +KInetSocketAddress::KInetSocketAddress(const KSocketAddress& other) + : KSocketAddress(other) +{ + if (!d->invalid()) + update(); +} + +// special constructor +KInetSocketAddress::KInetSocketAddress(KSocketAddressData *d) + : KSocketAddress(d) +{ +} + +// destructor +KInetSocketAddress::~KInetSocketAddress() +{ + /* nothing to do */ +} + +// copy operator +KInetSocketAddress& KInetSocketAddress::operator =(const KInetSocketAddress& other) +{ + KSocketAddress::operator =(other); + return *this; +} + +// IP version +int KInetSocketAddress::ipVersion() const +{ + if (d->invalid()) + return 0; + + switch (d->addr.generic->sa_family) + { + case AF_INET: + return 4; + +#ifdef AF_INET6 + case AF_INET6: + return 6; +#endif + } + + return 0; // for all other cases +} + +KIpAddress KInetSocketAddress::ipAddress() const +{ + if (d->invalid()) + return KIpAddress(); // return an empty address as well + + switch (d->addr.generic->sa_family) + { + case AF_INET: + return KIpAddress(&d->addr.in->sin_addr, 4); +#ifdef AF_INET6 + case AF_INET6: + return KIpAddress(&d->addr.in6->sin6_addr, 6); +#endif + } + + return KIpAddress(); // empty in all other cases +} + +KInetSocketAddress& KInetSocketAddress::setHost(const KIpAddress& ip) +{ + switch (ip.version()) + { + case 4: + makeIPv4(); + memcpy(&d->addr.in->sin_addr, ip.addr(), sizeof(d->addr.in->sin_addr)); + break; + + case 6: + makeIPv6(); + memcpy(&d->addr.in6->sin6_addr, ip.addr(), sizeof(d->addr.in6->sin6_addr)); + break; + + default: + // empty + d->tqinvalidate(); + } + + return *this; +} + +// returns the port +TQ_UINT16 KInetSocketAddress::port() const +{ + if (d->invalid()) + return 0; + + switch (d->addr.generic->sa_family) + { + case AF_INET: + return ntohs(d->addr.in->sin_port); + +#ifdef AF_INET6 + case AF_INET6: + return ntohs(d->addr.in6->sin6_port); +#endif + } + + return 0; +} + +KInetSocketAddress& KInetSocketAddress::setPort(TQ_UINT16 port) +{ + if (d->invalid()) + makeIPv4(); + + switch (d->addr.generic->sa_family) + { + case AF_INET: + d->addr.in->sin_port = htons(port); + break; + +#ifdef AF_INET6 + case AF_INET6: + d->addr.in6->sin6_port = htons(port); + break; +#endif + + default: + d->tqinvalidate(); // setting the port on something else + } + + return *this; +} + +KInetSocketAddress& KInetSocketAddress::makeIPv4() +{ + d->makeipv4(); + return *this; +} + +KInetSocketAddress& KInetSocketAddress::makeIPv6() +{ + d->makeipv6(); + return *this; +} + +TQ_UINT32 KInetSocketAddress::flowinfo() const +{ +#ifndef AF_INET6 + return 0; +#else + + if (!d->invalid() && d->addr.in6->sin6_family == AF_INET6) + return d->addr.in6->sin6_flowinfo; + return 0; +#endif +} + +KInetSocketAddress& KInetSocketAddress::setFlowinfo(TQ_UINT32 flowinfo) +{ + makeIPv6(); // must set here + d->addr.in6->sin6_flowinfo = flowinfo; + return *this; +} + +int KInetSocketAddress::scopeId() const +{ +#ifndef AF_INET6 + return 0; +#else + + if (!d->invalid() && d->addr.in6->sin6_family == AF_INET6) + return d->addr.in6->sin6_scope_id; + return 0; +#endif +} + +KInetSocketAddress& KInetSocketAddress::setScopeId(int scopeid) +{ + makeIPv6(); // must set here + d->addr.in6->sin6_scope_id = scopeid; + return *this; +} + +void KInetSocketAddress::update() +{ + if (d->addr.generic->sa_family == AF_INET) + return; +#ifdef AF_INET6 + else if (d->addr.generic->sa_family == AF_INET6) + return; +#endif + else + d->tqinvalidate(); +} + +KUnixSocketAddress::KUnixSocketAddress() +{ +} + +KUnixSocketAddress::KUnixSocketAddress(const sockaddr* sa, TQ_UINT16 len) + : KSocketAddress(sa, len) +{ + if (!d->invalid() && d->addr.un->sun_family != AF_UNIX) + d->tqinvalidate(); +} + +KUnixSocketAddress::KUnixSocketAddress(const KUnixSocketAddress& other) + : KSocketAddress(other) +{ +} + +KUnixSocketAddress::KUnixSocketAddress(const TQString& pathname) +{ + setPathname(pathname); +} + +KUnixSocketAddress::KUnixSocketAddress(KSocketAddressData* d) + : KSocketAddress(d) +{ +} + +KUnixSocketAddress::~KUnixSocketAddress() +{ +} + +KUnixSocketAddress& KUnixSocketAddress::operator =(const KUnixSocketAddress& other) +{ + KSocketAddress::operator =(other); + return *this; +} + +TQString KUnixSocketAddress::pathname() const +{ + if (!d->invalid() && d->addr.un->sun_family == AF_UNIX) + return TQFile::decodeName(d->addr.un->sun_path); + return TQString::null; +} + +KUnixSocketAddress& KUnixSocketAddress::setPathname(const TQString& path) +{ + d->dup(0L, MIN_SOCKADDR_UN_LEN + path.length()); + d->addr.un->sun_family = AF_UNIX; + strcpy(d->addr.un->sun_path, TQFile::encodeName(path)); + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + d->addr.un->sun_len = d->reallen; +#endif + + return *this; +} |