Skip to content
Snippets Groups Projects
Commit 32d5f7ca authored by g0dil's avatar g0dil
Browse files

Packets: Additional example documentation on the mainpage

Packets: Documentation for PacketData and updates to PacketParsera and Packdocumentation
doclib: Add additional formating to member documentation via html-munge.xsl and senf.css (enhance brief descriptions and make auto-generated docs less obstrusive)
Add flyspell-prog-mode to project.el
parent 4823d298
No related branches found
No related tags found
No related merge requests found
......@@ -26,7 +26,8 @@
/** \defgroup protocolbundle_default The Default Bundle
The default bundle combines a set of basic low level protocols like Ethernet or IP.
The default bundle combines a set of basic low level protocols like Ethernet or IP. Find the
list of packet types implemented below under 'Typedefs'.
\ingroup protocolbundles
*/
......@@ -39,8 +40,10 @@
// c-file-style: "senf"
// indent-tabs-mode: nil
// ispell-local-dictionary: "american"
// compile-command: "scons -u test"
// mode: flyspell
// compile-command: "scons -u doc"
// mode: auto-fill
// ispell-local-dictionary: "american"
// End:
// LocalWords: Fraunhofer Institut fuer offene Kommunikationssysteme FOKUS de
// LocalWords: Kompetenzzentrum Satelitenkommunikation SatCom Bund berlios IP
// LocalWords: defgroup protocolbundles protocolbundle ingroup dil
......@@ -10,29 +10,110 @@
parsing the packet information into meaningful values.
\li The \ref protocolbundles provide concrete implementations for interpreting packets of
some protocol. The Protocol Bundles are built on top of the basic packet library.
*/
/*
All these components work together to provide a hopefully simple and intuitive interface to
packet parsing and creation.
\section intro Introduction
Most every use of the packet library starts with some concrete packet typedef. Some fundamental
packet typedefs are provided by \ref protocolbundle_default. The first example will build a
complex packet: This will be an Ethernet packet containing an IPv4 UDP packet. We begin by
building the raw packet skeleton:
\code
senf::EthernetPacket eth (senf::EthernetPacket::create());
senf::IpV4Packet ip (senf::IpV4Packet::createAfter(ethernet));
senf::UDPPacket udp (senf::UDPPacket::createAfter(ip));
senf::DataPacket payload (senf::DataPacket::createAfter(udp,
std::string("Hello, world!")));
\endcode
These commands create what is called an interpreter chain. This chain consists of four
interpreters. All interpreters reference the same data storage. This data storage is a random
access sequence which contains the data bytes of the packet.
\note The data structures allocated are automatically managed using reference counting. In this
example we have four packet references each referencing the same underlying data
structure. This data structure will be freed when the last reference to it goes out of
scope.
The packet created above already has the correct payload however all protocol fields are
empty. We need to set those protocol fields:
\code
udp->source() = 2000u;
udp->destination() = 2001u;
ip->ttl() = 255u;
ip->source() = senf::INet4Address("192.168.0.1"); // (*)
ip->destination() = senf::INet4Address("192.168.0.2"); // (*)
eth->source() = senf::MACAddress("00:11:22:33:44:55");
eth->destination() = senf::MACAddress("00:11:22:33:44:66");
eth.finalize(); // (*)
\endcode
As seen above, packet fields are accessed using the <tt>-></tt> operator whereas other packet
facilities (like \c finalize()) are directly accessed using the member operator. The field
values are simple set using appropriately named accessors. As a last step, the \c finalize()
call will update all calculated fields (fields like next-protocol, header or payload length,
checksums etc). Now the packet is ready. We may now send it out using a packet socket
\code
senf::PacketSocketHandle sock ("eth0");
sock.write(eth.data());
\endcode
The packet library also provides lot's of facilities to navigate the packet chain:
- ParseInt.hh: Lots of parsers for integer numbers like senf::Parse_UInt8, for integer
bitfields like senf::Parse_UIntField and senf::Parse_Flag to parse boolean flags.
\code
eth.next() == ip; // true
eth.next().is<IpV4Packet>(); // true
eth.next().next() == udp; // true
eth.next().is<UDPPacket>(); // false
eth.next<UDPPacket>() == udp; // true
- ParseArray.hh: The senf::Parse_Array parser to parse arbitrary fixed-size arrays of
fixed-size elements (that is sub-parsers).
udp.next<UDPPacket>(); // throws InvalidPacketChainException
udp.next<UDPPacket>(senf::nothrow); // a senf::Packet testing as false
udp.findNext<UDPPacket()> == udp; // true
udp.first<IpV4Packet>() == ip; // true
- ParseVec.hh: The senf::Parse_Vector parser to parse dynamically sized arrays of fixed-size
elements (that is sub-parsers).
udp.prev() == ip; // true
udp.prev<EthernetPacket>() == eth // true
\endcode
See senf::ParserBase for further information.
... and so on. It is therefore not necessary to stash away a reference for every interpreter (as
each of the sub-packets are called) as long as at least one reference is available.
\section stuff Other Utilities
These chain navigation functions are also used to parse a packet. Let's read an Ethernet packet
from a packet socket handle:
\code
senf::PacketSocketHandle sock ("eth0");
senf::EthernetPacket packet (senf::EthernetPacket::create(senf::Packet::noinit));
sock.read(packet.data(),0u);
\endcode
This first creates an uninitialized Ethernet packet and then reads into this packet. We can now
parse this packet. Let's find out, whether this is a UDP packet destined to port 2001:
The pkf also comprises some additional utilities to support the development of packet classes.
\code
try {
senf::UDPPacket udp (packet.findNext<UDPPacket>(senf::nothrow));
if (udp && udp->destination() == 2001u) {
// Voila ...
}
} catch (senf::TruncatedPacketException const &) {
std::cerr << "Ooops !! Broken packet received ...\n"
}
\endcode
The senf::PacketRegistry implements a registry of packets keyed by an arbitrary type. The
registry is used to find a packet type given some kind of id (like the ethertype value from the
ethernet header). Together with it's support classes (especially senf::PacketRegistryMixin) this
class greatly simplifies implementing the needed table lookups.
TruncatedPacketException is thrown by <tt>udp->destination()</tt> if that field cannot be
accessed. More generally, whenever a field cannot be accessed because it would be out of bounds
of the data read, this exception is generated.
This is only a very short introduction to the library to give a feel for the implementation. For
a detailed discussion see the respective reference documentation.
*/
......@@ -42,6 +123,12 @@
// c-file-style: "senf"
// indent-tabs-mode: nil
// ispell-local-dictionary: "american"
// mode: flyspell
// mode: auto-fill
// compile-command: "scons -u doc"
// End:
// LocalWords: mainpage SENF packetparser protocolbundles protocolbundle IPv4
// LocalWords: udp endcode li senf EthernetPacket eth IpV createAfter ip std
// LocalWords: ethernet UDPPacket DataPacket ttl INet MACAddress nothrow prev
// LocalWords: PacketSocketHandle InvalidPacketChainException findNext noinit
// LocalWords: tt TruncatedPacketException const cerr Ooops
......@@ -104,7 +104,7 @@ namespace senf {
Packet is protocol agnostic. This class only provides non-protocol dependent members. To
access the protocol specific features of a packet (like header fields) the ConcretePacket
class extending Packet is povided.
class extending Packet is provided.
\section packet_semantics Semantics
......@@ -128,6 +128,11 @@ namespace senf {
(protocol). For each implemented protocol, typedefs should be provided for these
instantiations (Example: \ref EthernetPacket is a typedef for
\ref ConcretePacket < \ref EthernetPacketType >).
\see
\ref ConcretePacket for the type specific interface\n
\ref PacketData for the sequence interface\n
\ref packetparser for a specification of the parser interface
*/
class Packet
: public SafeBool<Packet>,
......@@ -317,7 +322,7 @@ namespace senf {
/**< \see valid() */
bool valid() const; ///< Check, whether the packet is valid()
/**< An in-valid() packet does not allow any operation
except checking vor validity and assignment. in-valid()
except checking for validity and assignment. in-valid()
packets serve the same role as 0-pointers. */
......@@ -337,7 +342,7 @@ namespace senf {
/**< This method is provided mostly to help debugging packet
problems. Each concrete packet should implement a dump
method writing out all fields of the packet in a
readable reresentation. dump() will call this member
readable representation. dump() will call this member
for each packet/header/interpreter in the chain from \c
this packet up to the end of the chain. */
......@@ -483,7 +488,7 @@ namespace senf {
\param[in] noinit This parameter must always have the
value \c senf::noinit. */
static ConcretePacket createAfter(Packet packet, size_type size);
///< Create default initializzed packet after \a packet
///< Create default initialized packet after \a packet
/**< This member will create a default initialized packet
with the given size. If the size parameter is smaller
than the minimum allowed packet size an exception will
......@@ -590,3 +595,13 @@ namespace senf {
// compile-command: "scons -u test"
// comment-column: 40
// End:
// LocalWords: defgroup li html png STL ConcretePacket PacketInterpreterBase
// LocalWords: PacketInterpreter PacketImpl OtherPacket EthernetPacket param
// LocalWords: EthernetPacketType PacketData packetparser nothrow senf prev
// LocalWords: InvalidPacketChainException findNext findPrev parseNextAs tt
// LocalWords: PacketType SomePacketType createAfter createBefore noinit href
// LocalWords: PacketTypeBase TruncatedPacketException http www org Institut
// LocalWords: Fraunhofer fuer offene Kommunikationssysteme FOKUS SatCom Bund
// LocalWords: Kompetenzzentrum Satelitenkommunikation berlios de hh
// LocalWords: addtogroup Structors PacketType dil
......@@ -36,9 +36,33 @@
namespace senf {
/** \brief
/** \brief Packet data STL-sequence view
PacketData only exists to separate out the container interface from PacketInterpreter.
The PacketData class provides an STL-sequence compatible view of the raw packet data. Each
packet/header/interpreter in the chain references the same storage area, presenting a
different (but nested/overlapping) section of the data.
Whenever the data is manipulated through PacketData, the change is assumed to be within the
data range of that packet: All insertions take place \e inside \c this packet and \e outside
any following packets in the packet chain.
\warning It is not permissible to change data belonging to a following
packet/header/interpreter even though this data is part of \c this sequence. Doing so
will corrupt the packet data.
\par
\warning When accessing packet data via the PacketData interface you are on your own: The
packet is not validated in any way, you bypass all parsers.
All public members are those of an STL random-access sequence.
\implementation This class is very tightly integrated with PacketInterpreterBase /
PacketInterpreter. It is separated out of those classes primarily to provide a clean
sequence interface to the library user and not for implementation reasons (it would have
been simpler to implement all these members in PacketInterpreterBase).
\ingroup packet_module
*/
class PacketData
: boost::noncopyable
......@@ -47,8 +71,6 @@ namespace senf {
///////////////////////////////////////////////////////////////////////////
// Types
typedef senf::detail::packet::smart_pointer<PacketData>::ptr_t ptr;
typedef senf::detail::packet::iterator iterator;
typedef senf::detail::packet::const_iterator const_iterator;
typedef senf::detail::packet::size_type size_type;
......@@ -99,15 +121,16 @@ namespace senf {
///@}
bool valid();
protected:
PacketData(size_type b, size_type e);
/// Need to make this protected so we can change it in the derived class
detail::PacketImpl * impl_;
detail::PacketImpl & impl() const;
bool valid();
private:
size_type begin_;
size_type end_;
......@@ -180,3 +203,8 @@ namespace senf {
// compile-command: "scons -u test"
// comment-column: 40
// End:
// LocalWords: Fraunhofer Institut fuer offene Kommunikationssysteme FOKUS de
// LocalWords: Kompetenzzentrum Satelitenkommunikation SatCom Bund berlios dil
// LocalWords: PacketData hh STL PacketInterpreterBase PacketInterpreter
// LocalWords: ingroup Structors
......@@ -87,7 +87,7 @@ prefix_ senf::PacketParserBase::size_type senf::detail::packetParserSize(Parser
template <class Parser>
prefix_ senf::SafePacketParser<Parser>::SafePacketParser()
: i_()
: parser_(), i_()
{}
template <class Parser>
......
This diff is collapsed.
......@@ -47,80 +47,94 @@
# define SENF_PACKET_PARSER_I_GET_TYPE_1(x) SENF_PACKET_PARSER_I_GET_TYPE_2
# define SENF_PACKET_PARSER_I_GET_TYPE_2(name,type) type
#
# define SENF_PACKET_PARSER_I_DEFINE_INIT_C(_0,_1,elt) \
# define SENF_PACKET_PARSER_I_DEFINE_INIT_C(_0,_1,elt) \
SENF_PACKET_PARSER_I_GET_NAME(elt) ().init();
#
# define SENF_PACKET_PARSER_I_DEFINE_INIT(fields) \
void defaultInit() const { \
BOOST_PP_SEQ_FOR_EACH( SENF_PACKET_PARSER_I_DEFINE_INIT_C, _, fields) \
# define SENF_PACKET_PARSER_I_DEFINE_INIT(fields) \
void defaultInit() const { \
BOOST_PP_SEQ_FOR_EACH( SENF_PACKET_PARSER_I_DEFINE_INIT_C, _, fields) \
}
#
# define SENF_PACKET_PARSER_I_FIELD_DISPATCH(n,t,name,type) \
# define SENF_PACKET_PARSER_I_FIELD_DISPATCH(n,t,name,type) \
SENF_PACKET_PARSER_I_ ## t(n,name,type)
#
# define SENF_PACKET_PARSER_I_FIELD_C(_0,_1,n,elt) \
BOOST_PP_EXPAND( \
# define SENF_PACKET_PARSER_I_FIELD_C(_0,_1,n,elt) \
BOOST_PP_EXPAND( \
SENF_PACKET_PARSER_I_FIELD_DISPATCH SENF_PACKET_PARSER_I_UNWRAP(n,elt))
#
# define SENF_PACKET_PARSER_I_INITSIZE_C(_0,_1,n,elt) \
# define SENF_PACKET_PARSER_I_INITSIZE_C(_0,_1,n,elt) \
BOOST_PP_IF(n,+,) senf::init_bytes< SENF_PACKET_PARSER_I_GET_TYPE(elt) >::value
#
# define SENF_PACKET_PARSER_I_DEFINE_FIELDS(fields) \
size_type offset_0_() const { return 0; } \
BOOST_PP_SEQ_FOR_EACH_I(SENF_PACKET_PARSER_I_FIELD_C, _, fields) \
size_type bytes() const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_SEQ_SIZE(fields),_)) (); \
} \
SENF_PACKET_PARSER_I_DEFINE_INIT(fields) \
static const size_type init_bytes = \
# define SENF_PACKET_PARSER_I_DEFINE_FIELDS(fields) \
size_type offset_0_() const { return 0; } \
BOOST_PP_SEQ_FOR_EACH_I(SENF_PACKET_PARSER_I_FIELD_C, _, fields) \
size_type bytes() const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_SEQ_SIZE(fields),_)) (); \
} \
SENF_PACKET_PARSER_I_DEFINE_INIT(fields) \
static const size_type init_bytes = \
BOOST_PP_SEQ_FOR_EACH_I(SENF_PACKET_PARSER_I_INITSIZE_C, _, fields) ;
#
# define SENF_PACKET_PARSER_I_FIXED_FIELD_DISPATCH(n,t,name,type) \
# define SENF_PACKET_PARSER_I_FIXED_FIELD_DISPATCH(n,t,name,type) \
SENF_PACKET_PARSER_I_FIXED_ ## t(n,name,type)
#
# define SENF_PACKET_PARSER_I_FIXED_FIELD_C(_0,_1,n,elt) \
BOOST_PP_EXPAND( \
# define SENF_PACKET_PARSER_I_FIXED_FIELD_C(_0,_1,n,elt) \
BOOST_PP_EXPAND( \
SENF_PACKET_PARSER_I_FIXED_FIELD_DISPATCH SENF_PACKET_PARSER_I_UNWRAP(n,elt))
#
# define SENF_PACKET_PARSER_I_DEFINE_FIXED_FIELDS(fields) \
static const size_type offset_0_ = 0; \
BOOST_PP_SEQ_FOR_EACH_I(SENF_PACKET_PARSER_I_FIXED_FIELD_C, _, fields) \
static const size_type fixed_bytes = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_SEQ_SIZE(fields),_)); \
# define SENF_PACKET_PARSER_I_DEFINE_FIXED_FIELDS(fields) \
static const size_type offset_0_ = 0; \
BOOST_PP_SEQ_FOR_EACH_I(SENF_PACKET_PARSER_I_FIXED_FIELD_C, _, fields) \
static const size_type fixed_bytes = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_SEQ_SIZE(fields),_)); \
SENF_PACKET_PARSER_I_DEFINE_INIT(fields)
#
# ///////////////////////////////////////////////////////////////////////////
# // Definition of the field types
#
# define SENF_PACKET_PARSER_I_Field(n,name,type) \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) () ); \
} \
size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) () const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) () + senf::bytes( name () ); \
# define SENF_PACKET_PARSER_I_Field(n,name,type) \
typedef type BOOST_PP_CAT(name,_t) ; \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) () ); \
} \
size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) () const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) () + senf::bytes( name () ); \
} \
size_type BOOST_PP_CAT(name, _offset) () const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) () ; \
}
#
# define SENF_PACKET_PARSER_I_FIXED_Field(n,name,type) \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) ); \
} \
static const size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) + type::fixed_bytes;
#
# define SENF_PACKET_PARSER_I_OverlayField(n,name,type) \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) () ); \
} \
size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) () const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) (); \
# define SENF_PACKET_PARSER_I_FIXED_Field(n,name,type) \
typedef type BOOST_PP_CAT(name,_t) ; \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) ); \
} \
static const size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) + type::fixed_bytes; \
static const size_type BOOST_PP_CAT(name,_offset) = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_));
#
# define SENF_PACKET_PARSER_I_OverlayField(n,name,type) \
typedef type BOOST_PP_CAT(name,_t) ; \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) () ); \
} \
size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) () const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) (); \
} \
size_type BOOST_PP_CAT(name, _offset) () const { \
return BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) () ; \
}
#
# define SENF_PACKET_PARSER_I_FIXED_OverlayField(n,name,type) \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) ); \
} \
static const size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_));
# define SENF_PACKET_PARSER_I_FIXED_OverlayField(n,name,type) \
typedef type BOOST_PP_CAT(name,_t) ; \
type name () const { \
return parse< type >( BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)) ); \
} \
static const size_type BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_)) = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(n,_)); \
static const size_type BOOST_PP_CAT(name,_offset) = \
BOOST_PP_CAT(offset_,BOOST_PP_CAT(BOOST_PP_INC(n),_));
#
# ///////////////////////////////mpp.e///////////////////////////////////////
# endif
......
......@@ -114,6 +114,42 @@
<xsl:with-param name="class">anchor</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="div[@class='memdoc']/p[1]">
<xsl:call-template name="add-class">
<xsl:with-param name="class">memtitle</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="div[@class='memdoc']/p[starts-with(text(),'Definition at line ')]">
<xsl:call-template name="add-class">
<xsl:with-param name="class">sourceline</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="div[@class='memdoc']/p[starts-with(text(),'References ')]">
<xsl:call-template name="add-class">
<xsl:with-param name="class">references</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="div[@class='memdoc']/p[starts-with(text(),'Referenced by ')]">
<xsl:call-template name="add-class">
<xsl:with-param name="class">referencedby</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="div[@class='memdoc']/p[starts-with(text(),'Reimplemented from ')]">
<xsl:call-template name="add-class">
<xsl:with-param name="class">reimplementedfrom</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="div[@class='memdoc']/p[starts-with(text(),'Reimplemented in ')]">
<xsl:call-template name="add-class">
<xsl:with-param name="class">reimplementedin</xsl:with-param>
</xsl:call-template>
</xsl:template>
<!-- Remove [external] references from the modules page -->
<xsl:template match="div[@id='content2']/ul/li[a/@class='elRef'][a/@doxygen][code/text()='[external]'][not(ul)]">
......
......@@ -258,7 +258,7 @@ dl.xref-bug, dl.xref-fix, dl.xref-todo, dl.xref-idea {
margin: 4px 8px 4px 2px;
background-color: #FFEEEE;
color: #666666;
font-size: 9px;
font-size: 75%;
overflow: hidden;
}
......@@ -310,7 +310,27 @@ div.idea {
dl.implementation {
color: #666666;
font-size: 9px;
font-size: 75%;
}
p.memtitle {
color: #1a41a8;
font-weight: bold;
margin-right: 14px;
border-bottom: 1px solid #84b0c7;
}
p.sourceline, p.references, p.referencedby, p.reimplementedfrom, p.reimplementedin {
color: #666666;
font-size: 75%;
margin-left: 10em;
margin-bottom: .2em;
margin-top: .2em;
text-indent: -4em;
}
div.memproto {
margin-bottom: .5em;
}
table {
......
......@@ -55,3 +55,26 @@
(let ((local-conf (ccide-project-search-upwards "project-local.el")))
(if local-conf
(load-file local-conf)))
(defun flyspell-cc-progmode-verify ()
"Replacement for standard flyspell-generic-progmode-verify which
checks for C/C++ preproc directives. Additionally, anything after ^L
is ignored (Those are the file local variables and local words)."
(let ((f (get-text-property (point) 'face)))
(and (memq f flyspell-prog-text-faces)
(not (save-excursion
(beginning-of-line)
(looking-at "\\(//\\)?#")))
(not (let ((l (max (point-min) (- (point-max) 4096))))
(and (< l (point))
(save-excursion (search-backward " " l t))))))))
(defun flyspell-cc-mode ()
"Torn on `flyspell-mode` for comments and strings in C/C++ mode."
(interactive)
(setq flyspell-generic-check-word-p 'flyspell-cc-progmode-verify)
(flyspell-mode 1))
;; Better set this here than in the file variables since the setting
;; is only valid if project.el is loaded ...
(flyspell-cc-mode)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment