From: g0dil Date: Fri, 27 Jul 2007 13:36:28 +0000 (+0000) Subject: Socket/Protocols/INet: Updated INet4SocketAddress to use INet4Address X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=ca133fb5d47ad2bd90d95e21626df59fdd78571a;p=senf.git Socket/Protocols/INet: Updated INet4SocketAddress to use INet4Address git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@360 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Examples/TCPClientServer/client.cc b/Examples/TCPClientServer/client.cc index 12fb87a..0323d94 100644 --- a/Examples/TCPClientServer/client.cc +++ b/Examples/TCPClientServer/client.cc @@ -31,7 +31,7 @@ int main(int argc, char const * argv[]) try { for (int i=0; i<=1000; i++) { senf::TCPv4ClientSocketHandle sock; - sock.connect(senf::INet4SocketAddress("127.0.0.1", 4243)); + sock.connect(senf::INet4SocketAddress("127.0.0.1:4243")); sock.protocol().linger(true); std::stringstream s; diff --git a/Examples/TCPClientServer/server.cc b/Examples/TCPClientServer/server.cc index dc53e5e..54a2040 100644 --- a/Examples/TCPClientServer/server.cc +++ b/Examples/TCPClientServer/server.cc @@ -38,7 +38,7 @@ class Server senf::TCPv4ServerSocketHandle serverSock; public: - Server(std::string const & host, unsigned int port) + Server(senf::INet4Address const & host, unsigned int port) : serverSock(senf::INet4SocketAddress(host, port)) {} void run() @@ -75,7 +75,7 @@ private: int main(int argc, char const * argv[]) { try { - Server myServer ("127.0.0.1", 4243); + Server myServer (senf::INet4Address::Loopback, 4243); myServer.run(); } catch (std::exception const & ex) { diff --git a/Socket/Protocols/INet/INet4Address.test.cc b/Socket/Protocols/INet/INet4Address.test.cc index 186202f..36e84f4 100644 --- a/Socket/Protocols/INet/INet4Address.test.cc +++ b/Socket/Protocols/INet/INet4Address.test.cc @@ -39,6 +39,7 @@ BOOST_AUTO_UNIT_TEST(inet4Address) { senf::INet4Address addr (senf::INet4Address::from_string("127.0.0.1")); BOOST_CHECK_EQUAL( addr, senf::INet4Address::Loopback ); + BOOST_CHECK( addr != senf::INet4Address::Broadcast ); addr = senf::INet4Address::from_string("localhost"); BOOST_CHECK_EQUAL( addr, senf::INet4Address::Loopback ); diff --git a/Socket/Protocols/INet/INetAddressing.cc b/Socket/Protocols/INet/INetAddressing.cc index 5f58af8..d768c7d 100644 --- a/Socket/Protocols/INet/INetAddressing.cc +++ b/Socket/Protocols/INet/INetAddressing.cc @@ -43,21 +43,26 @@ /////////////////////////////////////////////////////////////////////////// // senf::INet4Address -prefix_ senf::INet4SocketAddress::INet4SocketAddress(std::string const & host, unsigned port) +prefix_ senf::INet4SocketAddress::INet4SocketAddress(std::string const & addr) { clear(); - /** \todo gethostbyname support */ - if (::inet_aton(host.c_str(), &addr_.sin_addr) == 0) - throw InvalidINetAddressException(); - addr_.sin_port = htons(port); + unsigned i = addr.find(':'); + if (i == std::string::npos) + throw SyntaxException(); + address(INet4Address::from_string(std::string(addr,0,i))); + try { + port(boost::lexical_cast< ::u_int16_t >(std::string(addr,i+1))); + } + catch (boost::bad_lexical_cast const &) { + throw SyntaxException(); + } } -prefix_ std::string senf::INet4SocketAddress::str() - const +prefix_ senf::INet4SocketAddress::INet4SocketAddress(INet4Address const & addr, unsigned p) { - std::stringstream s; - s << host() << ':' << port(); - return s.str(); + clear(); + address(addr); + port(p); } prefix_ void senf::INet4SocketAddress::clear() @@ -66,38 +71,19 @@ prefix_ void senf::INet4SocketAddress::clear() addr_.sin_family = AF_INET; } -prefix_ void senf::INet4SocketAddress::assignString(std::string const & address) -{ - clear(); - unsigned i = address.find(':'); - if (i == std::string::npos) - throw InvalidINetAddressException(); - // The temporary string in the next expr is guaranteed to live - // until end-of-statement - if (::inet_aton(std::string(address,0,i).c_str(), &addr_.sin_addr) == 0) - throw InvalidINetAddressException(); - try { - // Replace lexical_cast with strtoul ? - addr_.sin_port = htons(boost::lexical_cast< ::u_int16_t >(std::string(address,i+1))); - } - catch (boost::bad_lexical_cast const &) { - throw InvalidINetAddressException(); - } -} - /////////////////////////////////////////////////////////////////////////// // senf::INet6Address prefix_ senf::INet6Address::INet6Address(std::string const & addr) { if (inet_pton(AF_INET6,addr.c_str(),&addr_) <= 0) - throw InvalidINetAddressException(); + throw SyntaxException(); } prefix_ senf::INet6Address::INet6Address(char const * addr) { if (inet_pton(AF_INET6,addr,&addr_) <= 0) - throw InvalidINetAddressException(); + throw SyntaxException(); } prefix_ void senf::INet6Address::clear() @@ -185,28 +171,28 @@ prefix_ void senf::INet6SocketAddress::assignAddr(std::string const & addr) || inet_pton(AF_INET6, std::string(boost::begin(*token),boost::end(*token)).c_str(), &sockaddr_.sin6_addr) <= 0 || ++token == tokens.end()) - throw InvalidINetAddressException(); + throw SyntaxException(); if (*token == "@") { if (++token == tokens.end()) - throw InvalidINetAddressException(); + throw SyntaxException(); assignIface(std::string(boost::begin(*token),boost::end(*token))); if (++token == tokens.end() || *token != "]") - throw InvalidINetAddressException(); + throw SyntaxException(); } else if (*token != "]") - throw InvalidINetAddressException(); + throw SyntaxException(); if (++token == tokens.end() || *boost::begin(*token) != ':') - throw InvalidINetAddressException(); + throw SyntaxException(); try { sockaddr_.sin6_port = htons( boost::lexical_cast(std::string(boost::next(boost::begin(*token)), boost::end(*token)))); } catch(boost::bad_lexical_cast const &) { - throw InvalidINetAddressException(); + throw SyntaxException(); } if (++token != tokens.end()) - throw InvalidINetAddressException(); + throw SyntaxException(); } prefix_ void senf::INet6SocketAddress::assignIface(std::string const & iface) @@ -216,7 +202,7 @@ prefix_ void senf::INet6SocketAddress::assignIface(std::string const & iface) else { sockaddr_.sin6_scope_id = if_nametoindex(iface.c_str()); if (sockaddr_.sin6_scope_id == 0) - throw InvalidINetAddressException(); + throw SyntaxException(); } } diff --git a/Socket/Protocols/INet/INetAddressing.cci b/Socket/Protocols/INet/INetAddressing.cci index 4b28057..f0e039d 100644 --- a/Socket/Protocols/INet/INetAddressing.cci +++ b/Socket/Protocols/INet/INetAddressing.cci @@ -38,16 +38,6 @@ prefix_ senf::INet4SocketAddress::INet4SocketAddress() clear(); } -prefix_ senf::INet4SocketAddress::INet4SocketAddress(char const * address) -{ - assignString(address); -} - -prefix_ senf::INet4SocketAddress::INet4SocketAddress(std::string const & address) -{ - assignString(address); -} - prefix_ bool senf::INet4SocketAddress::operator==(INet4SocketAddress const & other) const { @@ -55,11 +45,10 @@ prefix_ bool senf::INet4SocketAddress::operator==(INet4SocketAddress const & oth addr_.sin_addr.s_addr == other.addr_.sin_addr.s_addr; } -prefix_ std::string senf::INet4SocketAddress::host() +prefix_ senf::INet4Address senf::INet4SocketAddress::address() const { - char buffer[128]; - return std::string(::inet_ntop(AF_INET,&addr_.sin_addr,buffer,128)); + return INet4Address::from_inaddr(addr_.sin_addr.s_addr); } prefix_ unsigned senf::INet4SocketAddress::port() @@ -68,6 +57,22 @@ prefix_ unsigned senf::INet4SocketAddress::port() return ntohs(addr_.sin_port); } +prefix_ bool senf::INet4SocketAddress::boolean_test() + const +{ + return port() || address(); +} + +prefix_ void senf::INet4SocketAddress::address(INet4Address const & addr) +{ + addr_.sin_addr.s_addr = addr.inaddr(); +} + +prefix_ void senf::INet4SocketAddress::port(unsigned p) +{ + addr_.sin_port = htons(p); +} + prefix_ struct sockaddr * senf::INet4SocketAddress::sockaddr_p() { return reinterpret_cast(&addr_); @@ -87,7 +92,7 @@ prefix_ unsigned senf::INet4SocketAddress::sockaddr_len() prefix_ std::ostream & senf::operator<<(std::ostream & os, INet4SocketAddress const & addr) { - os << addr.str(); + os << addr.address() << ":" << addr.port(); return os; } diff --git a/Socket/Protocols/INet/INetAddressing.ct b/Socket/Protocols/INet/INetAddressing.ct index db187b3..d2782a0 100644 --- a/Socket/Protocols/INet/INetAddressing.ct +++ b/Socket/Protocols/INet/INetAddressing.ct @@ -39,7 +39,7 @@ prefix_ senf::INet6Address::INet6Address(Range const & range) for (; p!=p_end && i!=i_end; ++p, ++i) *p = *i; if (p!=p_end || i!=i_end) - throw InvalidINetAddressException(); + throw SyntaxException(); } ///////////////////////////////ct.e//////////////////////////////////////// diff --git a/Socket/Protocols/INet/INetAddressing.hh b/Socket/Protocols/INet/INetAddressing.hh index e235cd4..79ccb52 100644 --- a/Socket/Protocols/INet/INetAddressing.hh +++ b/Socket/Protocols/INet/INetAddressing.hh @@ -31,10 +31,12 @@ #include #include #include +#include #include "Socket/SocketPolicy.hh" #include "Socket/ClientSocketHandle.hh" #include "Socket/CommunicationPolicy.hh" #include "Socket/Protocols/GenericAddressingPolicy.hh" +#include "INet4Address.hh" //#include "INetAddressing.mpp" ///////////////////////////////hh.p//////////////////////////////////////// @@ -48,42 +50,45 @@ namespace senf { INet4Address wraps the standard sockaddr_in datatype. It provides simple accessor methods to access the host and port. It does \e not integrate \c gethostbyname or DNS lookup. - - \todo Implement real INet4Address datatype and rename this one to INet4SockAddress ... - \todo Implement more complete interface - \todo gethostbyname support ? + + \implementation This implementation is based on sockaddr_in, which is needed since it needs + to provide a non-const struct sockaddr * for legacy compatibility. */ class INet4SocketAddress + : public boost::equality_comparable, + public senf::ComparableSafeBool { public: INet4SocketAddress(); - INet4SocketAddress(char const * address); ///< Set address and port - /**< See INet4SocketAddress(std::string) - \throws InvalidINetAddressException */ - INet4SocketAddress(std::string const & address); ///< Set address and port + explicit INet4SocketAddress(std::string const & address); ///< Set address and port /**< This constructor expects a string of the form - 'xxx.xxx.xxx.xxx:pppp'. The constructor will use this - value to initialize the host and port members. This - constructor does \e only support numeric ip addresses - not hostnames - \param[in] address Address and port - \throws InvalidINetAddressException */ - INet4SocketAddress(std::string const & host, unsigned port); + 'host:port'. The constructor will use this value to + initialize the host and port members. Since it uses the + INet4Address::from_string constructor, this call may + block while waiting for the resolver. + \throws SyntaxException if the 'host:port' syntax is + not obeyed. + \throws INet4Address::SyntaxException if the host part + cannot be converted to an IP address. */ + + INet4SocketAddress(INet4Address const & addr, unsigned port); ///< Set address and port explicitly - /**< \param[in] host ip address in dotted-quad notation - \param[in] port port number - \throws InvalidINetAddressException */ - + /**< \param[in] addr IP address + \param[in] port port number */ bool operator==(INet4SocketAddress const & other) const; ///< Check INet4SocketAddress for equality - std::string str() const; ///< Return "address:port" string - std::string host() const; ///< Return address in doted quad notation - unsigned port() const; ///< Return portnumber + INet4Address address() const; ///< Return address + unsigned port() const; ///< Return port number + + bool boolean_test() const; ///< \c true, if address is empty (i.e. 0.0.0.0:0) void clear(); ///< Clear address/port to 0.0.0.0:0 + void address(INet4Address const & addr); ///< Set address + void port(unsigned p); ///< Set port number + /// \name Generic Address Interface /// @{ @@ -93,15 +98,17 @@ namespace senf { /// @} - private: - void assignString(std::string const & addr); + struct SyntaxException : public std::exception + { virtual char const * what() const throw() + { return "Invalid IpV4 socket address syntax"; } }; + private: struct ::sockaddr_in addr_; }; /** \brief Write address and port to os - \related INet4Address + \related INet4SocketAddress */ std::ostream & operator<<(std::ostream & os, INet4SocketAddress const & addr); @@ -160,6 +167,9 @@ namespace senf { ///< Get const pointer to internal address repr unsigned addr_len() const; ///< Size of an IPv6 address (16 bytes) + struct SyntaxException : public std::exception + { virtual char const * what() const throw() { return "Invalid IpV6 address syntax"; } }; + protected: private: @@ -256,6 +266,10 @@ namespace senf { ///@} + struct SyntaxException : public std::exception + { virtual char const * what() const throw() + { return "Invalid IpV6 socket address syntax"; } }; + protected: private: @@ -269,14 +283,6 @@ namespace senf { */ std::ostream & operator<<(std::ostream & os, INet6SocketAddress const & addr); - /** \brief Signal invalid INet address syntax - - \related INet4Address - \relatesalso INet6Address - */ - struct InvalidINetAddressException : public std::exception - { char const * what() const throw() { return "invalid inet address"; } }; - /// @} /// \addtogroup policy_impl_group diff --git a/Socket/Protocols/INet/INetAddressing.test.cc b/Socket/Protocols/INet/INetAddressing.test.cc index 317c077..c8c8024 100644 --- a/Socket/Protocols/INet/INetAddressing.test.cc +++ b/Socket/Protocols/INet/INetAddressing.test.cc @@ -37,30 +37,33 @@ BOOST_AUTO_UNIT_TEST(inet4Address) { using senf::INet4SocketAddress; - using senf::InvalidINetAddressException; + using senf::INet4Address; { INet4SocketAddress addr; + + BOOST_CHECK( ! addr ); - addr = "127.0.0.1:12345"; + addr = INet4SocketAddress("127.0.0.1:12345"); + BOOST_CHECK ( addr != INet4SocketAddress() ); } { INet4SocketAddress addr1("127.0.0.1:12345"); - INet4SocketAddress addr2(std::string("127.0.0.1:12345")); - INet4SocketAddress addr3("127.0.0.1",12345); + INet4SocketAddress addr3(INet4Address::Loopback,12345); } - BOOST_CHECK_EQUAL( INet4SocketAddress("127.0.0.1:12345"), INet4SocketAddress("127.0.0.1",12345) ); + BOOST_CHECK_EQUAL( INet4SocketAddress("127.0.0.1:12345"), + INet4SocketAddress(INet4Address::Loopback,12345) ); - BOOST_CHECK_THROW( INet4SocketAddress("127.0.0.1"), InvalidINetAddressException ); - BOOST_CHECK_THROW( INet4SocketAddress("foo@bar:12345"), InvalidINetAddressException ); - BOOST_CHECK_THROW( INet4SocketAddress("127.0.0.1:1234a"), InvalidINetAddressException ); - BOOST_CHECK_THROW( INet4SocketAddress("foo@bar",12345), InvalidINetAddressException ); + BOOST_CHECK_THROW( INet4SocketAddress("127.0.0.1"), INet4SocketAddress::SyntaxException ); + BOOST_CHECK_THROW( INet4SocketAddress("foo@bar:12345"), INet4Address::SyntaxException ); + BOOST_CHECK_THROW( INet4SocketAddress("127.0.0.1:1234a"), INet4SocketAddress::SyntaxException ); - BOOST_CHECK_EQUAL( INet4SocketAddress("127.0.0.1:12345").host(), "127.0.0.1" ); + BOOST_CHECK_EQUAL( INet4SocketAddress("127.0.0.1:12345").address(), INet4Address::Loopback ); BOOST_CHECK_EQUAL( INet4SocketAddress("127.0.0.1:12345").port(), 12345u ); - BOOST_CHECK_EQUAL( INet4SocketAddress("127.0.0.1:12345").str(), "127.0.0.1:12345" ); + BOOST_CHECK_EQUAL( boost::lexical_cast(INet4SocketAddress("127.0.0.1:12345")), + "127.0.0.1:12345" ); { INet4SocketAddress addr("127.0.0.1:12345"); @@ -79,7 +82,6 @@ BOOST_AUTO_UNIT_TEST(inet6Address) { using senf::INet6Address; using senf::INet6SocketAddress; - using senf::InvalidINetAddressException; { INet6Address addr1 ("0102:0304:0506:0708:090A:0B0C:0D0E:0F00"); @@ -109,15 +111,15 @@ BOOST_AUTO_UNIT_TEST(inet6Address) addr1.clear(); addr2 = "::"; BOOST_CHECK_EQUAL( addr1, addr2 ); - BOOST_CHECK_THROW( addr1 = "", InvalidINetAddressException ); + BOOST_CHECK_THROW( addr1 = "", INet6Address::SyntaxException ); BOOST_CHECK_EQUAL( boost::lexical_cast(addr1), "::" ); unsigned char data[] = { 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x21, 0 }; INet6Address addr3 (std::make_pair(&data[0],&data[0]+sizeof(data)-1)); BOOST_CHECK_EQUAL( addr3, "1200::21" ); BOOST_CHECK_THROW( INet6Address(std::make_pair(&data[0],&data[0]+sizeof(data))), - InvalidINetAddressException ); + INet6Address::SyntaxException ); BOOST_CHECK_THROW( INet6Address(std::make_pair(&data[0],&data[0]+sizeof(data)-2)), - InvalidINetAddressException ); + INet6Address::SyntaxException ); } { @@ -148,9 +150,9 @@ BOOST_AUTO_UNIT_TEST(inet6Address) BOOST_CHECK_EQUAL( addr.port(), 100u ); addr.host("::2"); BOOST_CHECK_EQUAL( addr.host(), "::2" ); - BOOST_CHECK_THROW( addr = "", InvalidINetAddressException ); - BOOST_CHECK_THROW( addr = "[::1]", InvalidINetAddressException ); - BOOST_CHECK_THROW( addr = "[::1]1234", InvalidINetAddressException ); + BOOST_CHECK_THROW( addr = "", INet6SocketAddress::SyntaxException ); + BOOST_CHECK_THROW( addr = "[::1]", INet6SocketAddress::SyntaxException ); + BOOST_CHECK_THROW( addr = "[::1]1234", INet6SocketAddress::SyntaxException ); addr = "[12::21@lo]:12345"; BOOST_CHECK_EQUAL( addr.address(), "[12::21@lo]:12345" ); BOOST_CHECK_EQUAL( addr.host(), "12::21" ); diff --git a/Socket/Protocols/INet/TCPSocketHandle.test.cc b/Socket/Protocols/INet/TCPSocketHandle.test.cc index 279f4a1..25a476d 100644 --- a/Socket/Protocols/INet/TCPSocketHandle.test.cc +++ b/Socket/Protocols/INet/TCPSocketHandle.test.cc @@ -156,18 +156,20 @@ BOOST_AUTO_UNIT_TEST(tcpv4ClientSocketHandle) { senf::TCPv4ClientSocketHandle sock; - BOOST_CHECK_THROW( sock.connect(senf::INet4SocketAddress("127.0.0.1:12345")), senf::SystemException ); - BOOST_CHECK_THROW( sock.protocol().connect("127.0.0.1:12345"), senf::SystemException ); + BOOST_CHECK_THROW( sock.connect(senf::INet4SocketAddress("127.0.0.1:12345")), + senf::SystemException ); + BOOST_CHECK_THROW( sock.protocol().connect(senf::INet4SocketAddress("127.0.0.1:12345")), + senf::SystemException ); } try { alarm(10); start(server_v4); senf::TCPv4ClientSocketHandle sock; - BOOST_CHECK_NO_THROW( sock.bind("127.0.0.1:23456") ); - BOOST_CHECK_NO_THROW( sock.connect("127.0.0.1:12345") ); - BOOST_CHECK( sock.peer() == "127.0.0.1:12345" ); - BOOST_CHECK( sock.local() == "127.0.0.1:23456" ); + BOOST_CHECK_NO_THROW( sock.bind(senf::INet4SocketAddress("127.0.0.1:23456")) ); + BOOST_CHECK_NO_THROW( sock.connect(senf::INet4SocketAddress("127.0.0.1:12345")) ); + BOOST_CHECK( sock.peer() == senf::INet4SocketAddress("127.0.0.1:12345") ); + BOOST_CHECK( sock.local() == senf::INet4SocketAddress("127.0.0.1:23456") ); BOOST_CHECK( sock.blocking() ); BOOST_CHECK_NO_THROW( sock.rcvbuf(2048) ); BOOST_CHECK_EQUAL( sock.rcvbuf(), 2048u ); @@ -340,7 +342,7 @@ BOOST_AUTO_UNIT_TEST(tcpv4ServerSocketHandle) try { alarm(10); BOOST_CHECKPOINT("Opening server socket"); - senf::TCPv4ServerSocketHandle server ("127.0.0.1:12346"); + senf::TCPv4ServerSocketHandle server (senf::INet4SocketAddress("127.0.0.1:12346")); BOOST_CHECKPOINT("Starting client"); start(client_v4); diff --git a/Socket/Protocols/INet/UDPSocketHandle.test.cc b/Socket/Protocols/INet/UDPSocketHandle.test.cc index f9e937f..f251e45 100644 --- a/Socket/Protocols/INet/UDPSocketHandle.test.cc +++ b/Socket/Protocols/INet/UDPSocketHandle.test.cc @@ -142,16 +142,17 @@ BOOST_AUTO_UNIT_TEST(udpv4ClientSocketHandle) alarm(10); start(server_v4); senf::UDPv4ClientSocketHandle sock; - BOOST_CHECK_NO_THROW( sock.bind("127.0.0.1:23456") ); - BOOST_CHECK( sock.local() == "127.0.0.1:23456" ); + BOOST_CHECK_NO_THROW( sock.bind(senf::INet4SocketAddress("127.0.0.1:23456")) ); + BOOST_CHECK( sock.local() == senf::INet4SocketAddress("127.0.0.1:23456") ); BOOST_CHECK_NO_THROW( sock.rcvbuf(2048) ); BOOST_CHECK_EQUAL( sock.rcvbuf(), 2048u ); BOOST_CHECK_NO_THROW( sock.sndbuf(2048) ); BOOST_CHECK_EQUAL( sock.sndbuf(), 2048u ); - BOOST_CHECK_NO_THROW( sock.writeto("127.0.0.1:12345", std::string("TEST-WRITE")) ); + BOOST_CHECK_NO_THROW( sock.writeto(senf::INet4SocketAddress("127.0.0.1:12345"), + std::string("TEST-WRITE")) ); BOOST_CHECK_EQUAL( sock.read(), "TEST-WRITE" ); BOOST_CHECK_NO_THROW( sock.protocol().timestamp() ); - sock.writeto("127.0.0.1:12345","QUIT"); + sock.writeto(senf::INet4SocketAddress("127.0.0.1:12345"),"QUIT"); sleep(1); stop(); sleep(1); diff --git a/doclib/dot-munge.pl b/doclib/dot-munge.pl index d11a556..550a3a2 100755 --- a/doclib/dot-munge.pl +++ b/doclib/dot-munge.pl @@ -4,9 +4,9 @@ s/fontsize=10/fontsize=8/g; # Wrap long labels (templates) -if (/label=\"([^"]*)\"/) { #"])){ # To make emacs happy ... +if (/label=\"([^"]*)\"/) { #"])){ # To make emacs happy ... $pre=$`; - $post=$'; #' # To make emacs happy ... + $post=$'; #'; # To make emacs happy ... $label=$1; # Break at each komma @@ -14,15 +14,15 @@ if (/label=\"([^"]*)\"/) { #"])){ # To make emacs happy ... # If more than one '<' is in the label, break after each '<' if (($label=~tr/1) { - $label=~s/