Packets: Add packet diagrams
g0dil [Tue, 27 Jan 2009 01:28:45 +0000 (01:28 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1078 270642c3-0616-0410-b53a-bc976706d245

18 files changed:
Packets/DefaultBundle/EthernetPacket.hh
Packets/DefaultBundle/ICMPv6Packet.hh
Packets/DefaultBundle/ICMPv6TypePacket.hh
Packets/DefaultBundle/IPv4Packet.hh
Packets/DefaultBundle/IPv6Extensions.hh
Packets/DefaultBundle/IPv6Packet.hh
Packets/DefaultBundle/LlcSnapPacket.hh
Packets/DefaultBundle/UDPPacket.hh
Packets/ListParser.hh
Packets/MPEGDVBBundle/DTCPPacket.hh
Packets/Mainpage.dox
Packets/SConscript
Packets/VectorParser.hh
doclib/pkgdraw [new file with mode: 0755]
doclib/textogif [new file with mode: 0755]
senfscons/Dia2Png.py
senfscons/PkgDraw.py [new file with mode: 0644]
senfscons/SENFSCons.py

index cbaf6e3..8286e88 100644 (file)
@@ -96,6 +96,7 @@ namespace senf {
 
         \par Fields:
             \ref EthernetPacketParser
+            \image html EthernetPacket.png
 
         \par Associated registries:
             \ref EtherTypes
@@ -151,7 +152,8 @@ namespace senf {
 
         \par Fields:
             \ref EthVLanPacketParser
-
+            \image html EthVLanPacket.png
+        
         \par Associated registries:
             \ref EtherTypes
 
index 44a4316..715ce21 100644 (file)
@@ -46,7 +46,21 @@ namespace senf
         // ICMP type registry
         typedef boost::uint16_t key_t;
     };
-    
+
+    /** \brief ICMP Packet for IPv6
+
+        \par Packet type (typedef):
+            \ref ICMPv6Packet
+
+        \par Fields:
+            \ref ICMPv6PacketParser
+            \image html ICMPv6Packet.png
+        
+        \par Associated registries:
+            \ref ICMPTypes
+
+        \ingroup protocolbundle_default
+     */
     struct ICMPv6PacketType 
         : public PacketTypeBase,
           public PacketTypeMixin<ICMPv6PacketType, ICMPTypes>
@@ -76,3 +90,15 @@ namespace senf
 }
 
 #endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// comment-column: 40
+// End:
+ss
index 8741909..475b11f 100644 (file)
@@ -43,6 +43,17 @@ namespace senf {
         SENF_PARSER_FINALIZE ( ICMPv6EchoRequestParser );
     };
     
+    /** \brief ICMPv6 Echo Request
+
+        \par Packet type (typedef):
+            \ref ICMPv6EchoRequest
+
+        \par Fields:
+            \ref ICMPv6EchoRequestParser
+            \image html ICMPv6EchoRequest.png
+
+        \ingroup protocolbundle_default
+     */
     struct ICMPv6EchoRequestType
         : public PacketTypeBase,
           public PacketTypeMixin<ICMPv6EchoRequestType>
@@ -70,6 +81,17 @@ namespace senf {
         SENF_PARSER_FINALIZE ( ICMPv6EchoReplyParser );
     };
     
+    /** \brief ICMPv6 Echo Reply
+
+        \par Packet type (typedef):
+            \ref ICMPv6EchoReply
+
+        \par Fields:
+            \ref ICMPv6EchoReplyParser
+            \image html ICMPv6EchoReply.png
+
+        \ingroup protocolbundle_default
+     */
     struct ICMPv6EchoReplyType
         : public PacketTypeBase,
           public PacketTypeMixin<ICMPv6EchoReplyType>
@@ -114,6 +136,17 @@ namespace senf {
         SENF_PARSER_FINALIZE ( ICMPv6ErrDestUnreachableParser );
     };
     
+    /** \brief ICMPv6 Destination unreachable
+
+        \par Packet type (typedef):
+            \ref ICMPv6ErrDestUnreachable
+
+        \par Fields:
+            \ref ICMPv6ErrDestUnreachableParser
+            \image html ICMPv6ErrDestUnreachable.png
+        \ingroup protocolbundle_default
+    */
     struct ICMPv6ErrDestUnreachableType
         : public PacketTypeBase,
           public PacketTypeMixin<ICMPv6ErrDestUnreachableType>
@@ -146,6 +179,17 @@ namespace senf {
         SENF_PARSER_FINALIZE ( ICMPv6ErrTooBigParser );
     };
     
+    /** \brief ICMPv6 Packet to big
+
+        \par Packet type (typedef):
+            \ref ICMPv6ErrTooBig
+
+        \par Fields:
+            \ref ICMPv6ErrTooBigParser
+            \image html ICMPv6ErrTooBig.png
+
+        \ingroup protocolbundle_default
+     */
     struct ICMPv6ErrTooBigType
         : public PacketTypeBase,
           public PacketTypeMixin<ICMPv6ErrTooBigType>
@@ -179,6 +223,17 @@ namespace senf {
         SENF_PARSER_FINALIZE ( ICMPv6ErrTimeExceededParser );
     };
     
+    /** \brief ICMPv6 Time exceeded
+
+        \par Packet type (typedef):
+            \ref ICMPv6ErrTimeExceeded
+
+        \par Fields:
+            \ref ICMPv6ErrTimeExceededParser
+            \image html ICMPv6ErrTimeExceeded.png
+
+        \ingroup protocolbundle_default
+     */
     struct ICMPv6ErrTimeExceededType
         : public PacketTypeBase,
           public PacketTypeMixin<ICMPv6ErrTimeExceededType>
@@ -212,6 +267,17 @@ namespace senf {
         SENF_PARSER_FINALIZE ( ICMPv6ErrParamProblemParser );
     };
     
+    /** \brief ICMPv6 Parameter problem
+
+        \par Packet type (typedef):
+            \ref ICMPv6ErrParamProblem
+
+        \par Fields:
+            \ref ICMPv6ErrParamProblemParser
+            \image html ICMPv6ErrParamProblem.png
+
+        \ingroup protocolbundle_default
+     */
     struct ICMPv6ErrParamProblemType
         : public PacketTypeBase,
           public PacketTypeMixin<ICMPv6ErrParamProblemType>
@@ -260,6 +326,17 @@ namespace senf {
         }
     };
     
+    /** \brief MLDv2 Listener query
+
+        \par Packet type (typedef):
+           \ref MLDv2ListenerQuery
+
+        \par Fields:
+            \ref MLDv2ListenerQueryParser
+            \image html MLDv2ListenerQuery.png
+
+        \ingroup protocolbundle_default
+     */
     struct MLDv2ListenerQueryType
         : public PacketTypeBase,
           public PacketTypeMixin<MLDv2ListenerQueryType>
@@ -308,6 +385,18 @@ namespace senf {
         }
     };
     
+    /** \brief MLDv2 Listener report
+
+        \par Packet type (typedef):
+           \ref MLDv2ListenerReport
+
+        \par Fields:
+            \ref MLDv2ListenerReportParser
+            \image html MLDv2ListenerReport.png
+            \image html MLDv2AddressRecord.png
+
+        \ingroup protocolbundle_default
+     */
     struct MLDv2ListenerReportType
         : public PacketTypeBase,
           public PacketTypeMixin<MLDv2ListenerReportType>
@@ -325,3 +414,14 @@ namespace senf {
 }
 
 #endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// comment-column: 40
+// End:
index c1e367b..0dc7917 100644 (file)
@@ -158,6 +158,8 @@ namespace senf {
             Set \a protocol from type of next packet if found in \ref IpTypes\n
             Calculate \a checksum
 
+        \image html IPv4Packet.png
+
         \ingroup protocolbundle_default
      */
     struct IPv4PacketType
index 91ac610..d2e9de0 100644 (file)
@@ -71,6 +71,8 @@ namespace senf {
         \par Finalize action:
             Set \a nextHeader from type of next packet if found in \ref IpTypes
 
+        \image html IPv6Extensions_Fragment.png
+
         \ingroup protocolbundle_default
      */
     struct IPv6ExtensionType_Fragment
index 63815d8..97f79ab 100644 (file)
@@ -98,6 +98,8 @@ namespace senf {
             Set \a length from payload size\n
             Set \a nextHeader from type of next packet if found in \ref IpTypes
 
+        \image html IPv6Packet.png
+
         \ingroup protocolbundle_default
      */
     struct IPv6PacketType
index 037b61f..df1d155 100644 (file)
@@ -77,6 +77,8 @@ namespace senf {
         \par Finalize action:
             XXXX
 
+        \image html LlcSnapPacket.png
+
         \ingroup protocolbundle_default
      */
     struct LlcSnapPacketType
index 8b1aa70..fb71f44 100644 (file)
@@ -71,6 +71,8 @@ namespace senf {
             Set \a length from payload size\n
             Calculate \a checksum
 
+        \image html UDPPacket.png
+
         \ingroup protocolbundle_default
      */
     struct UDPPacketType
index 1b0a4f6..8807578 100644 (file)
@@ -58,7 +58,16 @@ namespace senf {
         You will normally not instantiate ListParser directly, you will use the \ref
         SENF_PARSER_LIST() helper macro.
 
-        \see ExampleListPolicy
+        Some basic list access methods are defined as parser members. To access the complete list
+        API however you will need to instantiate a container wrapper for the list. See \ref
+        packet_usage_fields_collection.
+
+        \see 
+            \ref How to access \ref packet_usage_fields_collection
+            SENF_PARSER_LIST() macro used to define list fields \n
+            ListParser_Container list container wrapper API \n
+            ExampleListPolicy
+
         \ingroup parsecollection
       */
     template <class ListPolicy>
@@ -288,6 +297,11 @@ namespace senf {
         \param[in] size name of field giving the list size
         \param[in] elt_type list element type
 
+        \see 
+            How to use \ref packet_usage_fields_collection \n
+            senf::ListParser the list parser API for list field access
+            senf::ListParser_Container the list parser container API for list field access
+
         \hideinitializer
         \ingroup packetparsermacros
      */
index d3a8e05..78f226f 100644 (file)
@@ -44,8 +44,8 @@ namespace senf {
     {
 #       include SENF_PARSER()        
 
-        SENF_PARSER_PRIVATE_FIELD( fbipCount_, UInt8Parser );
-        SENF_PARSER_PRIVATE_FIELD( reserved_, UInt8Parser );
+        SENF_PARSER_PRIVATE_FIELD( fbipCount_, UInt8Parser ); //<pkgdraw: hide
+        SENF_PARSER_PRIVATE_FIELD( reserved_, UInt8Parser ); //<pkgdraw: hide
 
         SENF_PARSER_VECTOR( fbips, fbipCount_, INet4AddressParser );
 
@@ -60,8 +60,8 @@ namespace senf {
     {
 #       include SENF_PARSER()        
 
-        SENF_PARSER_PRIVATE_FIELD( fbipCount_, UInt8Parser );
-        SENF_PARSER_PRIVATE_FIELD( reserved_, UInt8Parser );
+        SENF_PARSER_PRIVATE_FIELD( fbipCount_, UInt8Parser ); //<pkgdraw: hide
+        SENF_PARSER_PRIVATE_FIELD( reserved_, UInt8Parser ); //<pkgdraw: hide
 
         SENF_PARSER_VECTOR( fbips, fbipCount_, INet6AddressParser );
 
@@ -76,26 +76,28 @@ namespace senf {
 
         Parser implementing the DTCP packet according to RFC 3077
         
-        \see DTCPPacketType
+        \see DTCPHelloPacketType
      */
     struct DTCPHelloPacketParser : public PacketParserBase
     {
 #       include SENF_PARSER()
 
+        //>pkgdraw: name=vers
         SENF_PARSER_BITFIELD         ( versionNumber,        4, unsigned );  // must be 1
-        SENF_PARSER_BITFIELD         ( command,              4, unsigned );
+        SENF_PARSER_BITFIELD         ( command,              4, unsigned );  //<pkgdraw: name=cmd
 
         enum Command { JOIN=1, LEAVE=2 };
 
         SENF_PARSER_FIELD            ( interval,             UInt8Parser );  // should be 5
         SENF_PARSER_FIELD            ( sequenceNumber,       UInt16Parser );
 
-        SENF_PARSER_PRIVATE_BITFIELD ( reserved0_,           3, unsigned );
+        SENF_PARSER_PRIVATE_BITFIELD ( reserved0_,           3, unsigned );  //<pkgdraw: name=
         SENF_PARSER_BITFIELD         ( receiveCapableFeed,   1, bool );
         SENF_PARSER_BITFIELD_RO      ( ipVersion,            4, unsigned );  // 4=IPv4, 6=IPv6
 
         SENF_PARSER_FIELD            ( tunnelProtocol,       UInt8Parser ); 
         SENF_PARSER_FIELD_RO         ( fbipCount,            UInt8Parser );
+        //>pkgdraw: name=
         SENF_PARSER_PRIVATE_FIELD    ( reserved1_,           UInt8Parser );  // must be zero 
 
         // Go back to fbipCount so the variant has access to that field
@@ -127,6 +129,8 @@ namespace senf {
         \par Fields:
             \ref DTCPHelloPacketParser
 
+        \image html DTCPPacket.png
+
         \ingroup protocolbundle_mpegdvb
      */
     struct DTCPHelloPacketType
index b8c5ad9..308d9ac 100644 (file)
     invalidate the wrapper if it changes the packets size.
 
     \see 
-        senf::VectorParser_Container Interface of the vector parser container wrapper \n
-        senf::ListParser_Container Interface of the list parser container wrapper
+        senf::VectorParser / senf::VectorParser_Container Interface of the vector parser \n
+        senf::ListParser / senf::ListParser_Container Interface of the list parser
     
 
     \subsubsection packet_usage_collection_variant The Variant Parser
index 64aa7a7..5755ab5 100644 (file)
@@ -11,7 +11,37 @@ SENFSCons.StandardTargets(env)
 SENFSCons.Lib(env, sources)
 SENFSCons.Doxygen(env, extra_sources = [
     env.Dia2Png("structure.dia"),
-    env.Dia2Png("MPEGDVBBundle/TLV.dia")
+    env.Dia2Png("MPEGDVBBundle/TLV.dia"),
+    env.PkgDraw("MPEGDVBBundle/DTCPPacket.hh"),
+    env.PkgDraw("DefaultBundle/EthernetPacket.hh",
+                PKGDRAWPACKETS = "EthernetPacketParser"),
+    env.PkgDraw("DefaultBundle/EthVLanPacket.png", "DefaultBundle/EthernetPacket.hh",
+                PKGDRAWPACKETS = "EthVLanPacketParser"),
+    env.PkgDraw("DefaultBundle/IPv4Packet.hh"),
+    env.PkgDraw("DefaultBundle/IPv6Packet.hh"),
+    env.PkgDraw("DefaultBundle/LlcSnapPacket.hh"),
+    env.PkgDraw("DefaultBundle/UDPPacket.hh"),
+    env.PkgDraw("DefaultBundle/IPv6Extensions_Fragment.png", "DefaultBundle/IPv6Extensions.hh",
+                PKGDRAWPACKETS = "IPv6PacketParserExtension_Fragment"),
+    env.PkgDraw("DefaultBundle/ICMPv6Packet.hh"),
+    env.PkgDraw("DefaultBundle/ICMPv6EchoRequest.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "ICMPv6EchoRequestParser"),
+    env.PkgDraw("DefaultBundle/ICMPv6EchoReply.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "ICMPv6EchoReplyParser"),
+    env.PkgDraw("DefaultBundle/ICMPv6ErrDestUnreachable.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "ICMPv6ErrDestUnreachableParser"),
+    env.PkgDraw("DefaultBundle/ICMPv6ErrTooBig.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "ICMPv6ErrTooBigParser"),
+    env.PkgDraw("DefaultBundle/ICMPv6ErrTimeExceeded.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "ICMPv6ErrTimeExceededParser"),
+    env.PkgDraw("DefaultBundle/ICMPv6ErrParamProblem.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "ICMPv6ErrParamProblemParser"),
+    env.PkgDraw("DefaultBundle/MLDv2ListenerQuery.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "MLDv2ListenerQueryParser"),
+    env.PkgDraw("DefaultBundle/MLDv2ListenerReport.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "MLDv2ListenerReportParser"),
+    env.PkgDraw("DefaultBundle/MLDv2AddressRecord.png", "DefaultBundle/ICMPv6TypePacket.hh",
+                PKGDRAWPACKETS = "MLDv2AddressRecordParser"),
 ])
 SENFSCons.InstallIncludeFiles(env, includes)
 
index 623e18f..db9e0de 100644 (file)
@@ -54,7 +54,16 @@ namespace senf {
         VectorParser makes use of a policy template argument, \a AuxPolicy, to customize the way the
         containers size is obtained. You will normally not instantiate VectorParser directly, you
         will use the \ref SENF_PARSER_VECTOR() helper macro.
+
+        Some basic vector access methods are defined as parser members. To access the complete list
+        API however you will need to instantiate a container wrapper for the vector. See \ref
+        packet_usage_fields_collection.
         
+        \see
+            \ref How to access \ref packet_usage_fields_collection \n
+            SENF_PARSER_VECTOR() macro used to define vector fields \n
+            VectorParser_Container vector container wrapper API
+
         \ingroup parsecollection
      */
     template <class ElementParser, class AuxPolicy>
@@ -276,6 +285,11 @@ namespace senf {
         \param[in] size name of field giving the vector size
         \param[in] elt_type vector element type
 
+        \see 
+            How to use \ref packet_usage_fields_collection \n
+            senf::VectorParser the vector parser API for vector field access
+            senf::VectorParser_Container the vector parser container API for vector field access
+
         \hideinitializer
         \ingroup packetparsermacros
      */
diff --git a/doclib/pkgdraw b/doclib/pkgdraw
new file mode 100755 (executable)
index 0000000..7bacd98
--- /dev/null
@@ -0,0 +1,321 @@
+#!/usr/bin/python
+
+import sys, re, signal, tempfile, os, os.path, shutil, atexit
+
+basedir=os.path.abspath(os.path.split(sys.argv[0])[0])
+
+charsPerBit = 1.4
+
+TEX_HEADER = r"""\documentclass{scrartcl}
+\usepackage[german]{babel}
+\usepackage[latin1]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage{ae,aecompl}
+\usepackage[active]{srcltx}
+
+\usepackage{color}
+\usepackage{bytefield}
+
+\pagestyle{empty}
+
+\begin{document}
+\sffamily
+"""
+
+PACKET_HEADER=r"""\begin{bytefield}{32}
+  \bitheader{0-31} \\
+"""
+
+PACKET_FOOTER=r"""\end{bytefield}
+\bigskip
+
+"""
+
+TEX_FOOTER = r"""\end{document}
+"""
+
+def formatField(width, start, size):
+    areas = []
+    sz = width - start
+    while size > 0:
+        if sz > size:
+            areas.append({'start': start,
+                          'size': size})
+            size = 0
+        else:
+            areas.append({'start': start,
+                          'size': sz})
+            size -= sz
+        sz = width
+        start = 0
+    for i in range(len(areas)-1):
+        if areas[i]['start'] < areas[i+1]['start']+areas[i+1]['size']:
+            areas[i]['bottom'] = False
+            areas[i+1]['top'] = False
+    return areas
+
+def formatSimpleField(width, start, field):
+    areas = formatField(width, start, field['size'])
+    nameix = 0
+    namesz = 0
+    for i in range(len(areas)):
+        if areas[i]['size'] > namesz:
+            namesz = areas[i]['size']
+            nameix = i
+    areas[nameix]['name'] = field['name'][:int(areas[nameix]['size'] * charsPerBit)]
+    if len(areas) == 2 and areas[0].get('bottom',True):
+        if areas[0].get('name','') : ix = 1
+        else                       : ix = 0
+        if 6 <= int(areas[ix]['size'] * charsPerBit):
+            areas[ix]['name'] = '(cont)'
+    return areas
+    
+def formatPacket(width, fields):
+    rows = [ [] ]
+    start = 0
+    for field in fields:
+        areas = []
+        if field.get('repeat', False):
+            if start > 0 and start < width:
+                areas.append({ 'start': start, 'size': width-start, 'bottom': False,
+                               'right': False})
+            start = 0
+        if field.get('size',None):
+            areas.extend(formatSimpleField(width, start, field))
+            start = areas[-1]['start'] + areas[-1]['size']
+        elif field.get('minsize', None):
+            f = dict(field)
+            f['size'] = field['minsize']
+            areas.extend(formatSimpleField(width, start, f))
+            start = areas[-1]['start'] + areas[-1]['size']
+            if start >= width : start = 0
+            addareas = formatField(width, start, field['maxsize'] - field['minsize'])
+            for area in addareas:
+                area['filled'] = True
+            areas += addareas
+            start = areas[-1]['start'] + areas[-1]['size']
+            if start > 0 and start < width:
+                areas.append({ 'start': start, 'size': width-start, 'bottom': False,
+                               'right': False})
+            start = 0
+        else:
+            if start > 0 and start < width:
+                areas.append({ 'start': start, 'size': width-start, 'bottom': False,
+                               'right': False})
+            areas.extend([ { 'start': 0, 'size': width, 'bottom': False,
+                             'name': field['name'] },
+                           { 'start': 0, 'size': width, 'skip': True },
+                           { 'start': 0, 'size': width, 'top': False } ])
+            start = 0
+        if field.get('repeat'):
+            if start > 0 and start < width:
+                areas.append({ 'start': start, 'size': width-start, 'bottom': False,
+                               'right': False})
+            start = 0
+            areas.append({ 'start': 0, 'size': width, 'dots': True })
+            da = areas[(areas[0].get('right', True) and (0,) or (1,))[0]:-1]
+            for i in range(len(da)):
+                if da[i].get('name','') :
+                    da[i] = dict(da[i])
+                    del da[i]['name']
+            areas.extend(da)
+        if start == width : start = 0
+        
+        while areas:
+            while areas and (not(rows[-1]) or rows[-1][-1]['start'] + rows[-1][-1]['size'] < width):
+                if areas[0].get('right', True) == False:
+                    # This is a fillup field. Check, wether to draw top:
+                    if len(rows) <= 1:
+                        areas[0]['top'] = False
+                    elif rows[-2][-1].get('bottom', True):
+                        areas[0]['top'] = False
+                rows[-1].append(areas.pop(0))
+            if areas:
+                rows.append([])
+    return rows
+
+def texquote(s):
+    s = s.replace('_', '\\_')
+    return s
+
+def makeTex(rows):
+    lines = []
+    for row in rows:
+        line = []
+        for area in row:
+            sides=""
+            if area.get('left',   True) : sides += "l"
+            if area.get('right',  True) : sides += "r"
+            if area.get('top',    True) : sides += "t"
+            if area.get('bottom', True) : sides += "b"
+            if sides == "lrtb" : sides = ""
+            else               : sides = "[%s]" % sides
+            if area.get('filled', False):
+                line.append(r"\bitbox%s{%s}{\color[gray]{0.7}\rule{\width}{\height}}" % (sides, area['size']))
+            elif area.get('skip', False):
+                line.append(r"\skippedwords")
+            elif area.get('dots', False):
+                line.append(r"\wordbox[]{1}{$\vdots$\\[1ex]}")
+            else:
+                line.append(r"\bitbox%s{%s}{\strut %s}"
+                            % (sides, area['size'], texquote(area.get('name',''))))
+        lines.append(" & ".join(line))
+    return " \\\\\n".join(lines) + "\n"
+
+FIELD_TYPES = {
+    'UInt8Parser' :  {'size': 8 },
+    'UInt16Parser' : {'size': 16 },
+    'UInt24Parser' : {'size': 24 },
+    'UInt32Parser' : {'size': 32 },
+    'UInt64Parser' : {'size': 64 },
+    'Int8Parser' : {'size': 8 },
+    'Int16Parser' : {'size': 16 },
+    'Int24Parser' : {'size': 24 },
+    'Int32Parser' : {'size': 32 },
+    'Int64Parser' : {'size': 64 },
+    'MACAddressParser': {'size': 48 },
+    'INet4AddressParser' : {'size': 32 },
+    'INet6AddressParser' : {'size': 128 },
+    }
+    
+def parse_FIELD(args, flags):
+    args = [ arg.strip() for arg in args.split(',') ]
+    if len(args) != 2:
+        sys.stderr.write("Failed to parse FIELD: %s" % args)
+        return None
+    field = dict(FIELD_TYPES.get(args[1].split(':')[-1], {}))
+    field['name'] = args[0]
+    return field
+
+def parse_PRIVATE_FIELD(args, flags):
+    return parse_FIELD(args, flags)
+
+def parse_FIELD_RO(args, flags):
+    return parse_FIELD(args, flags)
+
+def parse_BITFIELD(args, flags):
+    args = [ arg.strip() for arg in args.split(',') ]
+    if len(args) != 3:
+        sys.stderr.write("Failed to parse BITFIELD: %s" % args)
+        return None
+    try:
+        size = int(args[1])
+    except ValueError:
+        sys.stderr.write("Failed to parse BITFIELD: %s" % args)
+        return None
+    return { 'size' : size, 'name' : args[0] }
+
+def parse_PRIVATE_BITFIELD(args, flags):
+    return parse_BITFIELD(args, flags)
+
+def parse_BITFIELD_RO(args, flags):
+    return parse_BITFIELD(args, flags)
+
+def parse_SKIP(args, flags):
+    args = args.split(',')[0]
+    try:
+        bytes = int(args.strip())
+    except ValueError:
+        sys.stderr.write("Failed to parse SKIP: %s" % args)
+        return None
+    return { 'size': 8*bytes, 'name': '' }
+
+def parse_SKIP_BITS(args, flags):
+    try:
+        bits = int(args.strip())
+    except ValueError:
+        sys.stderr.write("Failed to parse SKIP_BITS: %s" % args)
+        return None
+    return { 'size': bits, 'name': '' }
+
+def parse_VECTOR(args, flags):
+    args = [ arg.strip() for arg in args.split(',') ]
+    if len(args) < 3:
+        sys.stderr.write("Failed to aprse VECTOR: %s" % args)
+        return None
+    field = dict(FIELD_TYPES.get(args[-1].split(':')[-1], {}))
+    field['name'] = args[0]
+    field['repeat'] = True
+    return field
+
+def parse_LIST(args, flags):
+    return parse_VECTOR(args, flags)
+
+def parse_VARIANT(args, flags):
+    return { 'name': args.split(',',1)[0].strip() }
+
+def parse_INIT(args, flags):
+    return None
+
+PARSER_START_RE = re.compile(r"#\s*include\s+SENF_(FIXED_)?PARSER\s*\(\s*\)")
+PARSER_END_RE = re.compile(r"SENF_PARSER_FINALIZE\s*\(([^)]*)\)\s*;")
+PARSER_FIELD_RE = re.compile(r"(?://>pkgdraw:(.*)$\s*)?SENF_PARSER_([A-Z_]+)\s*\(([^;]*)\)\s*;(?:\s*//<pkgdraw:(.*)$)?", re.M)
+
+def scanPackets(data):
+    packets = {}
+    end = 0
+    while True:
+        start =  PARSER_START_RE.search(data, end)
+        if not start: return packets
+        start = start.end(0)
+        end = PARSER_END_RE.search(data, start)
+        if not end: return packets
+        name=end.group(1).strip()
+        end = end.start(0)
+        packets[name] = scanFields(data[start:end])
+
+def scanFields(data):
+    fields = []
+    for match in PARSER_FIELD_RE.finditer(data):
+        tp = match.group(2)
+        flags = dict([ ([ arg.strip() for arg in flag.strip().split('=',1) ]+[True])[:2]
+                       for flag in ((match.group(1) or '')+(match.group(4) or '')).split(',') ])
+        if flags.has_key('hide') : continue
+        parser = globals().get("parse_%s" % tp, None)
+        if parser:
+            field = parser(match.group(3).strip(), flags)
+            if field:
+                if flags.has_key('name') : field['name'] = flags['name']
+                field['name'] = field['name'].strip('_')
+                fields.append(field)
+        else:
+            sys.stderr.write("Unknown parser type: %s\n" % tp)
+    return fields
+
+tmpdir = tempfile.mkdtemp(prefix="pkgdraw_")
+
+def cleanup():
+    global tmpdir
+    shutil.rmtree(tmpdir)
+
+signal.signal(signal.SIGINT, cleanup)
+signal.signal(signal.SIGTERM, cleanup)
+signal.signal(signal.SIGHUP, cleanup)
+#atexit.register(cleanup)
+
+data = scanPackets(sys.stdin.read())
+
+texf = file(os.path.join(tmpdir, "fields.tex"),"w")
+texf.write(TEX_HEADER)
+
+if len(sys.argv) > 1:
+    names = sys.argv[1:]
+else:
+    names = data.keys()
+    names.sort()
+
+for name in names:
+    texf.write("\\textbf{%s}\n\\bigskip\n\n" % texquote(name))
+    texf.write(PACKET_HEADER)
+    texf.write(makeTex(formatPacket(32, data[name])))
+    texf.write(PACKET_FOOTER)
+    
+texf.write(TEX_FOOTER)
+texf.close()
+
+if os.system("cd %s; %s/textogif -png -dpi 80 -res 0.25 fields >pkgdraw.log 2>&1"
+             % (tmpdir, basedir)) != 0:
+    sys.stderr.write("Conversion failed. See %s\n" % tmpdir)
+    os._exit(1)
+
+sys.stdout.write(file(os.path.join(tmpdir, "fields.png")).read())
diff --git a/doclib/textogif b/doclib/textogif
new file mode 100755 (executable)
index 0000000..e8df834
--- /dev/null
@@ -0,0 +1,235 @@
+#! /usr/bin/perl
+#
+#                          T E X T O G I F
+#
+#                          by John Walker
+#                      http://www.fourmilab.ch/
+#
+                    $version = '1.1 (2003-11-07)';
+#
+#
+#   Converts a LaTeX file containing equations(s) into a GIF file for
+#   embedding into an HTML document.  The black and white image of the
+#   equation is created at high resolution and then resampled to the
+#   target resolution to antialias what would otherwise be jagged
+#   edges.
+#
+#   Online documentation with sample output is available on the Web
+#   at http://www.fourmilab.ch/webtools/textogif/
+#
+#   Write your equation (or anything else you can typeset with LaTeX)
+#   in a file like:
+#
+#       \documentclass[12pt]{article}
+#       \pagestyle{empty}
+#       \begin{document}
+#
+#       \begin{displaymath}
+#       \bf  % Compiled formulae often look better in boldface
+#       \int H(x,x')\psi(x')dx' = -\frac{\hbar2}{2m}\frac{d2}{dx2}
+#                                 \psi(x)+V(x)\psi(x)
+#       \end{displaymath}
+#
+#       \end{document}
+#
+#   The "\pagestyle{empty}" is required to avoid generating a huge
+#   image with a page number at the bottom.
+#
+#   Then (assuming you have all the software described below installed
+#   properly), you can simply say:
+#
+#       textogif [options] filename ...
+#
+#   to compile filename.tex to filename.gif, an interlaced,
+#   transparent background GIF file ready to use an an inline image.
+#   You can specify the base name, for example, "schrod", rather than
+#   the full name of the TeX file ("schrod.tex").  TeX requires the
+#   input file to have an extension of ".tex".  The command line
+#   options are described in the help text at the end of this program
+#   and in the "Default Configuration" section below.
+#
+#   A sample IMG tag, including the image width and height is printed
+#   on standard error, for example:
+#
+#       <img src="schrod.gif" width=508 height=56>
+#
+#                         Required Software
+#
+#   This script requires the following software to be installed
+#   in the standard manner.  Version numbers are those used in the
+#   development and testing of the script.
+#
+#   Perl        5.8.0 (anything later than 4.036 should work)
+#   TeX         3.14159 (Web2C 7.3.1)
+#   LaTeX2e     <2000/06/01>
+#   dvips       dvipsk 5.86
+#   Ghostscript 6.52 (2001-10-20)
+#   Netpbm      9.24
+#
+#
+#                       Default Configuration
+#
+#   The following settings are the defaults used if the -dpi and
+#   -res options are not specified on the command line.
+#
+#   The parameter $dpi controls how large the equation will appear
+#   with respect to other inline images and the surrounding text.
+#   The parameter is expressed in "dots per inch" in the PostScript
+#   sense.  Unfortunately, since there's no standard text size in
+#   Web browsers (and most allow the user to change fonts and
+#   point sizes), there's no "right" value for this setting.  The
+#   default of 150 seems about right for most documents.  A setting
+#   of 75 generates equations at half the normal size, while 300
+#   doubles the size of equations.  The setting of $dpi can always be
+#   overridden by specifying the "-dpi" command line option.
+#
+    $dpi = 150;
+#
+#   The parameter $res specifies the oversampling as the ratio
+#   of the final image size to the initial black and white image.
+#   Smaller values produce smoothing with more levels of grey but
+#   require (much) more memory and intermediate file space to create
+#   the image.  If you run out of memory or disc space with the
+#   default value of 0.5, try changing it to 0.75.  A $res setting of
+#   1.0 disables antialiasing entirely.  The setting of $res can
+#   always be overridden by specifying the "res" command line option.
+#
+    $res = 0.5;
+#
+#   The $background parameter supplies a command, which may be
+#   void, to be inserted in the image processing pipeline to
+#   adjust the original black-on-white image so that its background
+#   agrees with that of the document in which it is to be inserted.
+#   For a document with the default grey background used by Mosaic
+#   and old versions of Netscape, use:
+#
+#       $background = "ppmdim 0.7 |";  $transparent = "b2/b2/b2";
+#
+#   If your document uses a white background, the void specification:
+#
+#       $background = "";  $transparent = "ff/ff/ff";
+#
+#   should be used.  For colour or pattern backgrounds, you'll have
+#   to hack the code.  The reason for adjusting the background is to
+#   ensure that when the image is resampled and then output with a
+#   transparent background the edges of the characters will fade
+#   smoothly into the page background.  Otherwise you'll get a
+#   distracting "halo" around each character.  You can override this
+#   default specification with the -grey command line option.
+#
+    $background = "";  $transparent = "ff/ff/ff";
+#
+#   Image generation and decoding commands for GIF and PNG output.
+#
+    $cmdGIF = 'ppmtogif';
+    $cmdGIFdecode = 'giftopnm';
+    $cmdPNG = 'pnmtopng';
+    $cmdPNGdecode = 'pngtopnm';
+#
+#   Default image creation modes
+#
+    $imageCmd = $cmdGIF;
+    $imageCmdD = $cmdGIFdecode;
+    $imageExt = 'gif';
+
+    #
+    #   Command line option processing
+    #
+    while ($ARGV[0] =~ m/^-/) {
+        $_ = shift(@ARGV);
+       s/^--/-/;                     # Allow GNU-style -- options
+        if (m/^-d/) {                 # -dpi nnn
+            $dpi = shift(@ARGV);
+        } elsif (m/^-gi/) {           # -gif
+           $imageCmd = $cmdGIF;
+           $imageCmdD = $cmdGIFdecode;
+           $imageExt = 'gif';
+        } elsif (m/^-gr/) {           # -grey n
+           $grey = shift(@ARGV);
+           $background = "ppmdim $grey | ";
+           $greylev = int(255 * $grey);
+           $transparent = sprintf("%02x/%02x/%02x", $greylev, $greylev, $greylev);
+        } elsif (m/^-h/) {            # -help
+           &help();
+           exit(0);
+        } elsif (m/^-p/) {            # -png
+           $imageCmd = $cmdPNG;
+           $imageCmdD = $cmdPNGdecode;
+           $imageExt = 'png';
+        } elsif (m/^-r/) {            # -res nnn
+            $res = shift(@ARGV);
+        } elsif (m/^-v/) {            # -version
+           print("Version $version\n");
+           exit(0);
+        }
+    }
+    #
+    #   Main file processing loop
+    #
+    foreach $f (@ARGV) {
+        $f =~ s/(.*)\.tex$/$1/;
+        &syscmd("echo x | latex  $f \n");
+        &syscmd("dvips -f $f >_temp_$$.ps\n");
+           
+       #   Assemble and execute the command pipeline which generates the image.
+
+       #   Start by invoking Ghostscript with the pbmraw output device and
+       #   output file set to standard output ("-") and the requested resolution.
+       #   The -q (Quiet) option is required; otherwise Ghostscript will send
+       #   processing information to standard output and corrupt transmission
+       #   of the bitmap to the next component in the pipeline.
+       $cmd = "echo quit | gs -q -dNOPAUSE  -r" . int($dpi / $res). "x". int($dpi / $res) .
+               " -sOutputFile=- -sDEVICE=ppmraw _temp_$$.ps | " .
+               
+       #   Next we crop white space surrounding the generated text, promote
+       #   the monochrome bitmap to a grey scale image with 8 bits per pixel,
+       #   apply whatever background adjustment transform is requested, and
+       #   scale the image to the desired size.
+               "pnmcrop -white | pnmpad -white -left " . int(10/$res) . 
+               " -right " . int(10/$res) . " -top " . int(10/$res) . 
+               " -bottom " . int(10/$res) . " | " .
+               " $background pnmscale " . $res . " | " .
+               
+       #   Finally, convert the image to the desired output format and write
+       #   the output file.
+               "$imageCmd -interlace -transparent rgb:$transparent >$f.$imageExt";
+       &syscmd($cmd);
+
+       #   Sweep up debris left around by the various intermediate steps
+        &syscmd("rm $f.dvi $f.aux $f.log _temp_$$.ps");
+
+       #   Print the reference to include this figure, including width and height,
+       #   to standard error.
+        $r = `$imageCmdD $f.$imageExt | pnmfile`;
+        $r =~ m/(\d+) by (\d+)/;
+        print(STDERR "<img src=\"$f.$imageExt\" width=\"$1\" height=\"$2\">\n");
+    }
+    
+    #  Echo and execute a system command
+    
+    sub syscmd {
+       local ($cmd) = @_;
+       
+       print(STDERR "$cmd\n");
+       system($cmd) == 0 || die("Error processing command:\n\t$cmd\n\t");
+    }
+
+    #  Print help text
+    
+    sub help {
+       print <<"EOD"
+usage: textogif [ options ] texfile...
+    Options:
+        -dpi n          Set rendering dots per inch to n (default 150)
+        -gif            Generate GIF image (default)
+        -grey           Grey scale background level: 0 = black, 1 = white (default)
+        -help           Print this message
+        -png            Generate PNG image
+        -res n          Set oversampling ratio, smaller = finer (default 0.5)
+        -version        Print version number
+For documentation and the latest version of this program
+please visit the Web page:
+    http://www.fourmilab.ch/webtools/textogif/
+EOD
+;
+    }
index 3e5f0fe..a8000be 100644 (file)
@@ -1,5 +1,5 @@
 ## \file
-# \brief Bia2Png builder
+# \brief Dia2Png builder
 
 ## \package senfscons.Dia2Png
 # \brief Build a PNG file from a DIA file
@@ -11,7 +11,7 @@
 #
 # \par Construction Envrionment Variables:
 # <table class="senf">
-# <tr><td>\c DIACOM</td><td>dia command, defaults to \c diak</td></tr>
+# <tr><td>\c DIACOM</td><td>dia command, defaults to \c dia</td></tr>
 # <tr><td>\c DIA2PNGDPI</td><td>resolution of converted image, defaults to 115</td></tr>
 # <tr><td>\c DIA2PNGMAXWIDTH</td><td>maximum image width, defaults to 800</td></tr>
 # </table>
diff --git a/senfscons/PkgDraw.py b/senfscons/PkgDraw.py
new file mode 100644 (file)
index 0000000..81309b2
--- /dev/null
@@ -0,0 +1,34 @@
+## \file
+# \brief PkgDraw builder
+
+## \package senfscons.PkgDraw
+# \brief Generate network packet diagram
+#
+# This builder will call pkgdraw to scan a header file and create network package diagrams
+# from SENF_PARSER macro calls.
+#
+# \par Construction Envrionment Variables:
+# <table class="senf">
+# <tr><td>\c PKGDRAWCOM</td><td>pkgdraw command, defaults to \c #/doclib/pkgdraw</td></tr>
+# <tr><td>\c PKGDRAWPACKETS</td><td>list packet types to include in
+#            the diagram, defaults to empty (include all packets)</td></tr>
+# </table>
+#
+# \ingroup builder
+
+import os
+import SCons.Builder, SCons.Action
+
+PkgDraw = SCons.Builder.Builder(
+    suffix = ".png",
+    src_suffix = ".hh",
+    action = SCons.Action.Action("$PKGDRAWCOM $PKGDRAWPACKETS <$SOURCE >$TARGET"),
+    single_source = 1)
+
+def generate(env):
+    env['BUILDERS']['PkgDraw'] = PkgDraw
+    env['PKGDRAWCOM'] = env.Dir('#').abspath + "/doclib/pkgdraw"
+    env['PKGDRAWPACKETS'] = ''
+
+def exists(env):
+    return env.Detect("#/doclib/pkgdraw")
index 32a5cc7..fc81537 100644 (file)
@@ -50,6 +50,7 @@ from SCons.Script import *
 SCONS_TOOLS = [
     "Doxygen",
     "Dia2Png",
+    "PkgDraw",
     "CopyToDir",
     "ProgramNoScan",
     "CompileCheck",