aboutsummaryrefslogtreecommitdiffstats
path: root/docbook/wsdg_src
diff options
context:
space:
mode:
authorJaap Keuter <jaap.keuter@xs4all.nl>2007-06-17 11:18:29 +0000
committerJaap Keuter <jaap.keuter@xs4all.nl>2007-06-17 11:18:29 +0000
commit8c241db91e73ebc7ccf3afd55eaadb03fa4d2fb4 (patch)
tree1ff27d19028fb21f0b5fb3544599588afba5af87 /docbook/wsdg_src
parent162382dcb1afe2db521cd42f9d5969ccc8af69cb (diff)
Bring developer guide up to date regarding tools and current plugin build infrastructure.
svn path=/trunk/; revision=22117
Diffstat (limited to 'docbook/wsdg_src')
-rw-r--r--docbook/wsdg_src/WSDG_chapter_dissection.xml305
-rw-r--r--docbook/wsdg_src/WSDG_chapter_quick_setup.xml2
-rw-r--r--docbook/wsdg_src/WSDG_chapter_tools.xml11
3 files changed, 105 insertions, 213 deletions
diff --git a/docbook/wsdg_src/WSDG_chapter_dissection.xml b/docbook/wsdg_src/WSDG_chapter_dissection.xml
index 19cd6a78b5..da7ce25a3d 100644
--- a/docbook/wsdg_src/WSDG_chapter_dissection.xml
+++ b/docbook/wsdg_src/WSDG_chapter_dissection.xml
@@ -57,14 +57,12 @@
With a little care, the plugin can be made to run as a built in
easily too - so we haven't lost anything.
</para>
- <example><title>Basic Plugin setup.</title>
- <programlisting>
- <![CDATA[
-#ifdef HAVE_CONFIG_H
+ <example><title>Dissector Initialisation.</title>
+ <programlisting>
+<![CDATA[#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <gmodule.h>
#include <epan/packet.h>
#include <epan/prefs.h>
@@ -73,112 +71,64 @@ void proto_register_foo();
void proto_reg_handoff_foo();
void dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
-/* Define version if we are not building Wireshark statically */
-#ifndef ENABLE_STATIC
-G_MODULE_EXPORT const gchar version[] = "0.0";
-#endif
-
static int proto_foo = -1;
static int global_foo_port = 1234;
static dissector_handle_t foo_handle;
-
-#ifndef ENABLE_STATIC
-G_MODULE_EXPORT void
-plugin_register(void)
+
+
+void
+proto_register_foo(void)
{
- /* register the new protocol, protocol fields, and subtrees */
- if (proto_foo == -1) { /* execute protocol initialization only once */
- proto_register_foo();
+ if (proto_foo == -1) {
+ proto_foo = proto_register_protocol (
+ "FOO Protocol", /* name */
+ "FOO", /* short name */
+ "foo" /* abbrev */
+ );
}
-}
-
-G_MODULE_EXPORT void
-plugin_reg_handoff(void){
- proto_reg_handoff_foo();
-}
-#endif]]>
- </programlisting>
- </example>
+}]]>
+ </programlisting></example>
<para>
Lets go through this a bit at a time. First we have some boiler plate
include files. These will be pretty constant to start with. Here we also
pre-declare some functions that we'll be writing shortly.
</para>
<para>
- Next we have a section surrounded by #ifdef ENABLE_STATIC. This is what
- makes this a plugin rather than a built in dissector.
- </para>
- <para>
- The version is a simple string that is used to report on the version of this
- dissector. You should increase this number each time you make changes that you
- need to keep track of.
- </para>
- <para>
Next we have an int that is initialised to -1 that records our protocol.
- This will get updated when we register this plugin with the main program.
+ This will get updated when we register this dissector with the main program.
We can use this as a handy way to detect if we've been initialised yet.
Its good practice to make all variables and functions that aren't exported
- static to keep name space pollution. Normally this isn't a problem unless your
+ static to keep name space pollution down. Normally this isn't a problem unless your
dissector gets so big it has to span multiple files.
</para>
<para>
- Then a global variable which contains the UDP port that we'll assume we are dissecting traffic for.
+ Then a module variable which contains the UDP port that we'll assume we are dissecting traffic for.
</para>
<para>
Next a dissector reference that we'll initialise later.
</para>
<para>
- Next, the first plugin entry point. The function plugin_register() is called
- when the plugin is loaded and allows you to do some initialisation stuff,
- which will include communicating with the main program what you're plugins
- capabilities are.
- </para>
- <para>
- The plugin_reg_handoff routine is used when dissecting sub protocols. As our
- hypothetical protocol will be hypothetically carried over UDP then we will
- need to do this.
- </para>
- <para>
Now we have the basics in place to interact with the main program, we had
better fill in those missing functions. Lets start with register function.
</para>
- <example><title>Plugin Initialisation.</title>
- <programlisting>
-<![CDATA[void
-proto_register_foo(void)
-{
- module_t *foo_module;
-
- if (proto_foo == -1) {
- proto_foo = proto_register_protocol (
- "FOO Protocol", /* name */
- "FOO", /* short name */
- "foo" /* abbrev */
- );
- }
- foo_module = prefs_register_protocol(proto_foo, proto_reg_handoff_foo);
-}]]>
- </programlisting></example>
<para>
- First a call to proto_register_protocol that
- registers the protocol. We can give it three names that
- will be used in various places to display it.
- - XXX explain where, this can be confusing
+ First a call to proto_register_protocol that registers the protocol.
+ We can give it three names that will be used in various places to display it.
+ The full and short name are used in the eg. the "Preferences" and "Enabled protocols"
+ dialogs as well as the generated field name list in the documentation.
+ The abbreviation is used as display filter name.
</para>
<para>
- Then we call the preference register function. At the moment we have
- no specific protocol preferences so this will be all that we need.
- This takes a function parameter which is our handoff function.
- I guess we'd better write that next.
+ Next we need a handoff routine.
</para>
- <example><title>Plugin Handoff.</title>
+ <example><title>Dissector Handoff.</title>
<programlisting>
<![CDATA[void
proto_reg_handoff_foo(void)
{
- static int Initialized=FALSE;
+ static int initialized=FALSE;
- if (!Initialized) {
+ if (!initialized) {
foo_handle = create_dissector_handle(dissect_foo, proto_foo);
dissector_add("udp.port", global_foo_port, foo_handle);
}
@@ -196,7 +146,7 @@ proto_reg_handoff_foo(void)
Now at last we finally get to write some dissecting code. For the moment we'll
leave it as a basic placeholder.
</para>
- <example><title>Plugin Dissection.</title>
+ <example><title>Dissection.</title>
<programlisting>
<![CDATA[static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
@@ -230,68 +180,39 @@ dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
<para>
At this point we should have a basic dissector ready to compile and install.
It doesn't do much at present, than identify the protocol and label it.
- Compile the dissector to a dll or shared library, and copy it into the plugin
- directory of the installation. To finish this off a Makefile of some sort will be
- required. A Makefile.nmake for Windows platforms and a Makefile.am for unix/linux
- types.
</para>
-
- <example><title>Makefile.nmake for Windows.</title>
- <programlisting>
-<![CDATA[
-include ..\..\config.nmake
-
-############### no need to modify below this line #########
-
-CFLAGS=/DHAVE_CONFIG_H /I../.. /I../../wiretap $(GLIB_CFLAGS) \
- /I$(PCAP_DIR)\include -D_U_="" $(LOCAL_CFLAGS)
-
-LDFLAGS = /NOLOGO /INCREMENTAL:no /MACHINE:I386 $(LOCAL_LDFLAGS)
-
-!IFDEF ENABLE_LIBWIRESHARK
-LINK_PLUGIN_WITH=..\..\epan\libwireshark.lib
-CFLAGS=/DHAVE_WIN32_LIBWIRESHARK_LIB /D_NEED_VAR_IMPORT_ $(CFLAGS)
-
-OBJECTS=foo.obj
-
-foo.dll foo.exp foo.lib : $(OBJECTS) $(LINK_PLUGIN_WITH)
- link -dll /out:foo.dll $(LDFLAGS) $(OBJECTS) $(LINK_PLUGIN_WITH) \
- $(GLIB_LIBS)
-
-!ENDIF
-
-clean:
- rm -f $(OBJECTS) foo.dll foo.exp foo.lib *.pdb
-
-distclean: clean
-
-maintainer-clean: distclean]]>
- </programlisting></example>
- <example><title>Makefile.am for unix/linux.</title>
- <programlisting>
-<![CDATA[
-INCLUDES = -I$(top_srcdir)
-
-plugindir = @plugindir@
-
-plugin_LTLIBRARIES = foo.la
-foo_la_SOURCES = foo.c moduleinfo.h
-foo_la_LDFLAGS = -module -avoid-version
-foo_la_LIBADD = @PLUGIN_LIBS@
-
-# Libs must be cleared, or else libtool won't create a shared module.
-# If your module needs to be linked against any particular libraries,
-# add them here.
-LIBS =
-
-CLEANFILES = \
- foo \
- *~
-
-EXTRA_DIST = \
- Makefile.nmake
-]]>
- </programlisting></example>
+ <para>
+ In order to compile this dissector and create a plugin a couple of support files
+ are required, besides the dissector source in packet-foo.c:
+ <itemizedlist>
+ <listitem><para>
+ Makefile.am - This is the UNIX/Linux makefile template
+ </para></listitem>
+ <listitem><para>
+ Makefile.common - This contains the file names of this plugin
+ </para></listitem>
+ <listitem><para>
+ Makefile.nmake - This contains the Wireshark plugin makefile for Windows
+ </para></listitem>
+ <listitem><para>
+ moduleinfo.h - This contains plugin version info
+ </para></listitem>
+ <listitem><para>
+ moduleinfo.nmake - This contains DLL version info for Windows
+ </para></listitem>
+ <listitem><para>
+ packet-foo.c - This is your dissector source
+ </para></listitem>
+ <listitem><para>
+ plugin.rc.in - This contains the DLL resource template for Windows
+ </para></listitem>
+ </itemizedlist>
+ You can find a good example for these files in the h223 plugin directory. Makefile.common
+ and Makefile.am have to be modified to reflect the relevant files and dissector name.
+ moduldeinfo.h and moduleinfo.nmake have to be filled in with the version information.
+ Compile the dissector to a DLL or shared library and copy it into the plugin
+ directory of the installation.
+ </para>
</section>
<section id="ChDissectDetails">
@@ -313,8 +234,7 @@ EXTRA_DIST = \
</para>
<example><title>Plugin Packet Dissection.</title>
<programlisting>
-<![CDATA[
-static void
+<![CDATA[static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
@@ -355,7 +275,7 @@ dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
This needs some changes to proto_register_foo. First a couple of statically
declare arrays.
</para>
- <example><title>Plugin Registering data structures.</title>
+ <example><title>Registering data structures.</title>
<programlisting>
<![CDATA[static hf_register_info hf[] = {
{ &hf_foo_pdu_type,
@@ -374,7 +294,7 @@ static gint *ett[] = {
<para>
Then, after the registration code, we register these arrays.
</para>
- <example><title>Plugin Registering data structures.</title>
+ <example><title>Registering data structures.</title>
<programlisting>
<![CDATA[proto_register_field_array(proto_foo, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));]]>
@@ -383,7 +303,7 @@ static gint *ett[] = {
The variables hf_foo_pdu_type and ett_foo also need to be declared
somewhere near the top of the file.
</para>
- <example><title>Plugin data structure globals.</title>
+ <example><title>Dissector data structure globals.</title>
<programlisting>
<![CDATA[
static int hf_foo_pdu_type = -1;
@@ -394,18 +314,16 @@ static gint ett_foo = -1;
<para>
Now we can enhance the protocol display with some detail.
</para>
- <example><title>Plugin starting to dissect the packets.</title>
+ <example><title>Dissector starting to dissect the packets.</title>
<programlisting>
- <![CDATA[
- if (tree) { /* we are being asked for details */
+<![CDATA[if (tree) { /* we are being asked for details */
proto_item *ti = NULL;
proto_tree *foo_tree = NULL;
ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);
foo_tree = proto_item_add_subtree(ti, ett_foo);
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, 0, 1, FALSE);
- }
-]]>
+ }]]>
</programlisting></example>
<para>
Now the dissection is starting to look more interesting. We have picked apart
@@ -459,10 +377,9 @@ static gint ett_foo = -1;
Now lets finish off dissecting the simple protocol. We need to add a few
more variables to the hf array, and a couple more procedure calls.
</para>
- <example><title>Plugin wrapping up the packet dissection.</title>
+ <example><title>Wrapping up the packet dissection.</title>
<programlisting>
- <![CDATA[
-static int hf_foo_flags = -1;
+<![CDATA[static int hf_foo_flags = -1;
static int hf_foo_sequenceno = -1;
static int hf_foo_initialip = -1;
...
@@ -492,8 +409,7 @@ static int hf_foo_initialip = -1;
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, FALSE); offset += 1;
proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, FALSE); offset += 1;
proto_tree_add_item(foo_tree, hf_foo_sequenceno, tvb, offset, 2, FALSE); offset += 2;
- proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, FALSE); offset += 4;
-]]>
+ proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, FALSE); offset += 4;]]>
</programlisting></example>
<para>
This dissects all the bits of this simple hypothetical protocol. We've introduced a new
@@ -510,14 +426,12 @@ static int hf_foo_initialip = -1;
</para>
<example><title>Naming the packet types.</title>
<programlisting>
- <![CDATA[
-static const value_string packettypenames[] = {
+<![CDATA[static const value_string packettypenames[] = {
{ 1, "Initialise" },
{ 2, "Terminate" },
{ 3, "Data" },
{ 0, NULL }
-};
-]]>
+};]]>
</programlisting></example>
<para>
This is a handy data structure that can be used to look up value to names.
@@ -527,14 +441,12 @@ static const value_string packettypenames[] = {
</para>
<example><title>Adding Names to the protocol.</title>
<programlisting>
- <![CDATA[
- { &hf_foo_pdu_type,
+<![CDATA[{ &hf_foo_pdu_type,
{ "FOO PDU Type", "foo.type",
FT_UINT8, BASE_DEC,
VALS(packettypenames), 0x0,
NULL, HFILL }
- }
-]]>
+ }]]>
</programlisting></example>
<para>
This helps in deciphering the packets, and we can do a similar thing for the
@@ -542,8 +454,7 @@ static const value_string packettypenames[] = {
</para>
<example><title>Adding Flags to the protocol.</title>
<programlisting>
- <![CDATA[
-#define FOO_START_FLAG 0x01
+<![CDATA[#define FOO_START_FLAG 0x01
#define FOO_END_FLAG 0x02
#define FOO_PRIORITY_FLAG 0x04
@@ -573,8 +484,7 @@ static int hf_foo_priorityflag = -1;
proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, FALSE);
proto_tree_add_item(foo_tree, hf_foo_startflag, tvb, offset, 1, FALSE);
proto_tree_add_item(foo_tree, hf_foo_endflag, tvb, offset, 1, FALSE);
- proto_tree_add_item(foo_tree, hf_foo_priorityflag, tvb, offset, 1, FALSE); offset += 1;
-]]>
+ proto_tree_add_item(foo_tree, hf_foo_priorityflag, tvb, offset, 1, FALSE); offset += 1;]]>
</programlisting></example>
<para>
Some things to note here. For the flags, as each bit is a different flag, we use
@@ -624,9 +534,7 @@ dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));
foo_tree = proto_item_add_subtree(ti, ett_foo);
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, FALSE);
- offset += 1;
-...
-]]>
+ offset += 1;]]>
</programlisting></example>
<para>
So here, after grabbing the value of the first 8 bits, we use it with one of the
@@ -752,8 +660,7 @@ dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
followed by an ID of the sequence and a packet sequence number.
</para>
<programlisting>
-<![CDATA[
-msg_pkt ::= SEQUENCE {
+<![CDATA[msg_pkt ::= SEQUENCE {
.....
flags ::= SEQUENCE {
fragment BOOLEAN,
@@ -763,13 +670,11 @@ msg_pkt ::= SEQUENCE {
msg_id INTEGER(0..65535),
frag_id INTEGER(0..65535),
.....
-}
-]]>
+}]]>
</programlisting>
<example><title>Reassembling fragments - Part 1</title>
<programlisting>
- <![CDATA[
-#include <epan/reassemble.h>
+<![CDATA[#include <epan/reassemble.h>
...
save_fragmented = pinfo->fragmented;
flags = tvb_get_guint8(tvb, offset); offset++;
@@ -786,8 +691,7 @@ if (flags & FL_FRAGMENT) { /* fragmented */
msg_reassembled_table, /* list of reassembled messages */
msg_num, /* fragment sequence number */
tvb_length_remaining(tvb, offset), /* fragment length - to the end */
- flags & FL_FRAG_LAST); /* More fragments? */
-]]>
+ flags & FL_FRAG_LAST); /* More fragments? */]]>
</programlisting></example>
<para>
We start by saving the fragmented state of this packet, so we can restore it later.
@@ -876,8 +780,7 @@ pinfo->fragmented = save_fragmented;
</para>
<example><title>Reassembling fragments - Initialisation</title>
<programlisting>
- <![CDATA[
-static GHashTable *msg_fragment_table = NULL;
+<![CDATA[static GHashTable *msg_fragment_table = NULL;
static GHashTable *msg_reassembled_table = NULL;
@@ -886,8 +789,7 @@ msg_init_protocol(void)
{
fragment_table_init (&msg_fragment_table);
reassembled_table_init(&msg_reassembled_table);
-}
-]]>
+}]]>
</programlisting></example>
<para>
First a couple of hash tables are declared, and these are initialised
@@ -901,8 +803,7 @@ msg_init_protocol(void)
</para>
<example><title>Reassembling fragments - Data</title>
<programlisting>
- <![CDATA[
-...
+<![CDATA[...
static int hf_msg_fragments = -1;
static int hf_msg_fragment = -1;
static int hf_msg_fragment_overlap = -1;
@@ -968,8 +869,7 @@ static gint *ett[] =
...
&ett_msg_fragment,
&ett_msg_fragments
-...
-]]>
+...]]>
</programlisting></example>
<para>
These hf variables are used internally within the reassembly routines
@@ -1007,12 +907,10 @@ static gint *ett[] =
<example>
<title>Reassembling TCP fragments</title>
<programlisting>
-<![CDATA[
-#ifdef HAVE_CONFIG_H
+<![CDATA[#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <gmodule.h>
#include <epan/packet.h>
#include <epan/emem.h>
#include <epan/dissectors/packet-tcp.h>
@@ -1042,8 +940,7 @@ static guint get_foo_message_len(packet_info *pinfo, tvbuff_t *tvb, int offset)
return (guint)tvb_get_ntohl(tvb, offset+4); /* e.g. length is at offset 4 */
}
-...
-]]>
+...]]>
</programlisting>
</example>
<para>
@@ -1084,8 +981,7 @@ static guint get_foo_message_len(packet_info *pinfo, tvbuff_t *tvb, int offset)
</para>
<example><title>Initialising a tap</title>
<programlisting>
- <![CDATA[
-#include <epan/tap.h>
+<![CDATA[#include <epan/tap.h>
static int foo_tap = -1;
@@ -1095,8 +991,7 @@ struct FooTap {
...
};
...
- foo_tap = register_tap("foo");
-]]>
+ foo_tap = register_tap("foo");]]>
</programlisting></example>
<para>
Whilst you can program a tap without protocol specific data, it
@@ -1117,7 +1012,7 @@ struct FooTap {
</para>
<example><title>Calling a protocol tap</title>
<programlisting>
- <![CDATA[
+<![CDATA[
static struct FooTap pinfo;
pinfo.packet_type = tvb_get_guint8(tvb, 0);
@@ -1149,8 +1044,7 @@ struct FooTap {
</para>
<example><title>Initialising a stats interface</title>
<programlisting>
- <![CDATA[
-/* register all http trees */
+<![CDATA[/* register all http trees */
static void register_foo_stat_trees(void) {
stats_tree_register("foo","foo","Foo/Packet Types",
foo_stats_tree_packet, foo_stats_tree_init, NULL );
@@ -1163,8 +1057,7 @@ G_MODULE_EXPORT void plugin_register_tap_listener(void)
register_foo_stat_trees();
}
-#endif
-]]>
+#endif]]>
</programlisting></example>
<para>
Working from the bottom up, first the plugin interface entry point is defined,
@@ -1200,8 +1093,7 @@ G_MODULE_EXPORT void plugin_register_tap_listener(void)
</para>
<example><title>Initialising a stats session</title>
<programlisting>
- <![CDATA[
-static const guint8* st_str_packets = "Total Packets";
+<![CDATA[static const guint8* st_str_packets = "Total Packets";
static const guint8* st_str_packet_types = "FOO Packet Types";
static int st_node_packets = -1;
static int st_node_packet_types = -1;
@@ -1209,8 +1101,7 @@ static int st_node_packet_types = -1;
static void foo_stats_tree_init(stats_tree* st) {
st_node_packets = stats_tree_create_node(st, st_str_packets, 0, TRUE);
st_node_packet_types = stats_tree_create_pivot(st, st_str_packet_types, st_node_packets);
-}
-]]>
+}]]>
</programlisting></example>
<para>
In this case we create a new tree node, to handle the total packets,
@@ -1219,16 +1110,14 @@ static void foo_stats_tree_init(stats_tree* st) {
</para>
<example><title>Generating the stats</title>
<programlisting>
- <![CDATA[
-static int foo_stats_tree_packet(stats_tree* st, packet_info* pinfo,
+<![CDATA[static int foo_stats_tree_packet(stats_tree* st, packet_info* pinfo,
epan_dissect_t* edt, const void* p) {
struct FooTap *pi = (struct FooTap *)p;
tick_stat_node(st, st_str_packets, 0, FALSE);
stats_tree_tick_pivot(st, st_node_packet_types,
val_to_str(pi->packet_type, msgtypevalues, "Unknown packet type (%d)"));
return 1;
-}
-]]>
+}]]>
</programlisting></example>
<para>
In this case the processing of the stats is quite simple.
@@ -1243,7 +1132,7 @@ epan_dissect_t* edt, const void* p) {
<title>How to use conversations</title>
<para>
Some info about how to use conversations in a dissector can be
- found in the file doc/README.developer.
+ found in the file doc/README.developer chapter 2.2.
</para>
</section>
diff --git a/docbook/wsdg_src/WSDG_chapter_quick_setup.xml b/docbook/wsdg_src/WSDG_chapter_quick_setup.xml
index c4fc436c4a..851e993fac 100644
--- a/docbook/wsdg_src/WSDG_chapter_quick_setup.xml
+++ b/docbook/wsdg_src/WSDG_chapter_quick_setup.xml
@@ -7,7 +7,7 @@
developer machine.</para>
<para>If a tool is not already installed on your system, you
will typically use the installation package from your
- distribution (by your favourite package manager: apt, yum,
+ distribution (by your favourite package manager: aptitude, yum,
synaptics, ...).</para>
<para>If an install package is not available, or you have a
reason not to use it (maybe because it's simply too old), you
diff --git a/docbook/wsdg_src/WSDG_chapter_tools.xml b/docbook/wsdg_src/WSDG_chapter_tools.xml
index 54d1e4e1cb..29d03c920b 100644
--- a/docbook/wsdg_src/WSDG_chapter_tools.xml
+++ b/docbook/wsdg_src/WSDG_chapter_tools.xml
@@ -1343,9 +1343,12 @@ Copyright (C) 2000-2006 CollabNet.
least the major/minor versions (e.g. 1.4).</para>
</warning>
<section id="ChToolsUnixSVNGUI">
- <title>UNIX or Win32 Cygwin: -</title>
- <para>XXX - could someone recommend a good UNIX GUI client
- for subversion?</para>
+ <title>UNIX or Win32 Cygwin: rapidSVN, subcommander</title>
+ <para>RapidSVN is a cross platform subversion frontend based on wxWidgets.
+ It can be found at: <ulink url="http://rapidsvn.tigris.org/" />.
+ Subcommander is another cross platform subversion frontend. It can
+ be found at: <ulink url="http://subcommander.tigris.org/" />.
+ </para>
<para>Cygwin don't provide any GUI client for
subversion.</para>
</section>
@@ -1513,7 +1516,7 @@ written by Larry Wall and Paul Eggert]]>
files needed to be installed, including all required DLL's and
such.</para>
<para>To install it, simply download the latest released
- version (currently: 2.22) from
+ version (currently: 2.28) from
<ulink url="http://nsis.sourceforge.net" />and start the
downloaded installer. You will need NSIS version 2 final or
higher.</para>