home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
bsbrowse.zip
/
intro.txt
next >
Wrap
Text File
|
1994-02-28
|
33KB
|
611 lines
The Browser
1. Introduction
Welcome to the Browser. This introduction is intended as a "tour d'horizon" for
programmers who are already familiar with object oriented programming in C++.
It is neither a tutorial nor a marketing brochure. Prepare to read a lot of technical
information.
The first thing to learn about the system is that it does a lot more than just browsing.
The Browser is a full scale C++ development system that covers the following
programming tasks:
Source Management (browse, edit and document C++ sources)
Change Management (keep track of class changes and versions)
Application Building (build and test *.obj, *.exe and *.dll files)
The support for each of these tasks is described in the following section. Please use
the screen shots in combination with the text.
With respect to the screen shots, it is worth to mention a few things that are characteristic
for all windows / views of the Browser:
most views can have multiple instances
drag and drop is used as primary interaction scheme for various views
almost all panes of the described windows have own popup (context) menus
These things are easy to use and demonstrate but hard to show in a "static display"
of screen shots.
2. Supported Programming Tasks
2.1 Browse, Edit and Document C++ Classes
To support this task, it is most important to provide a mechanism that
enables the user to program in classes and not files. The source code packaging
should be as transparent as possible. All sources should be processed in logical
units (e.g. C++ functions) and should not be treated as raw text. Object oriented
programming with C++ provides some powerful abstraction mechanisms (e.g.
inheritance). But without appropriate tool support it is possible to get lost in this
"3rd dimension". If the user has to remember class definitions, class to file mapping,
inheritance graphs etc., it is most likely that productivity will even suffer compared with
simpler programming paradigms. To be a successful OO programmer, you have to
understand both the programming language and your class library.
SCR_1.BMP : Navigate through your classes
┌──────────┐
│ 3 │ 1: Controller
│ │ 2: HierarchyViewer
│ ┌────────┐ 3: ClassEditor
└────────│ 2 │
┌───────┐│ │
│ 1 │└────────┘
└───────┘
This picture already shows the most frequently used views of the Browser. The first
window that comes up after starting the system is the Controller. It logs all programming
activities (login, function changes etc.) , can be used to start additional tools and terminates
a Browser session if it is closed. This is the only window that can not have multiple instances
concurrently.
The first thing to do after the system is initialized would be to locate the classes to do
some work on. This is done with the HierarchyViewer. It is subdivided into four panes:
AttributePane : upper left
DirectoryPane: upper right
GraphPane : lower right
ClassPane : lower left
The DirectoryPane shows the directories where the Browser is looking for C++ sources. These
directories are searched from top to bottom (i.e. if a class is found in more than one dir,
the first occurrence will be used). This directory list - like many other things - can be
set in a user configuration file. If a particular class is selected (by mouse click in the ClassPane) ,
the selection in the DirectoryPane is set on the directory where this class resides. If a directory is
selected (by mouse click in the DirectoryPane), a small mark appears in front of this line and the
ClassPane shows only the classes residing in the selected dir.
The AttributePane can be used to specify attributes that are used to filter the ClassPane
contents. Only classes that have these attributes are displayed (works also with negation).
Attributes are user defined names (e.g. "PM" for Presentation Manager related class) that
can be attached to classes.
The ClassPane shows a list of all matching classes (with appropriate attributes or with
specified location) in alphabetical order. Most functions used for practical work can be
triggered via the popup menu of this pane. There are functions that work on the selected
class only (e.g. start ClassEditor) as well as functions that work on all displayed classes
(e.g. build / update archive).
The GraphPane shows the inheritance relationships of the class selected in the ClassPane.
There are several display options to control how the inheritance graph is build.
If the "bases" option is set, all bases of the ClassPane selection are displayed. If "childs"
is set, all classes derived from the ClassPane selection are displayed. Most of the (single
or group) class manipulation functions mentioned in the ClassPane can be activated from the
GraphPane too. This time, the group is the number of classes displayed in the graph.
The ClassEditor might be the most important view of all. This window is used to display
and edit a single class. It consists of three panes:
DataPane (upper left)
FunctionPane (upper right)
FuncDefPane (lower)
The DataPane shows all data members of the class. Each line starts with symbols for the
standard attributes of this data member. The first symbol shows the association (instance,
class or global). The second symbol is used for the access level (private, protected or public).
There is an optional third symbol used for combinations of other attributes (e.g. extern). Next
is the type of the data member followed by the (boldfaced) name of the member. If a line
is selected, the FunctionPane shows only the function members that use the selected
data member. Entering new data members as well as modifying existing ones is done via
a special dialog launched from the DataMembers popup menu. There is also a dialog to
specify which members should be displayed (e.g. to filter out class members). Like the
whole class, members can have user defined attributes too.
The FunctionPane does the same job for the function members of a class. . In addition,
TextWindows showing the selected function can be launched via the popup menu of this
pane. This feature enables the user to work on different functions of a class simultaneously
(can be also done with a second ClassEditor on ths same class). All relevant views are updated
automatically in this case.
The third attribute symbol mentioned in the Data Pane is used more frequently in the
FunctionPane (e.g. for inline or virtual attributes). If a function is selected in this pane, the
corresponding function definition is displayed in the FuncDefPane.
The FuncDefPane is used to display and edit a particular function definition. It always holds
just one function definition at a time. Nothing outside of this function can be manipulated by
this pane. Because it just works on a small piece of text, this editor (which currently is an
enhanced MLE) can be kept very simple and is consistently used in the whole system.
Changes are saved via the popup menu. There are also some hypertext facilities (show
C++ or OS/2 *.INF help or header definitions for the selected word) available via this menu.
There is another important aspect on the ClassEditor that has to be mentioned. Users
do not have to deal with the C++ class definition code because it is completely managed by
the ClassEditor. Based on the member definitions (name, type and attributes) the class
definition is generated automatically.
There are two more things that can be specified with dedicated dialogs: Imports and Inserts.
Imports map to C++ #includes. They can be entered by drag and drop and are displayed with
class names instead of filenames (ordinary header files can be specified by using their filenames).
Inserts are simply pieces of (unparsed) text that can be merged in the class. The user has complete
control about order and position of imports / inserts (in front of class def, behind class def or in front
of implementation section).
SCR_2.BMP : more views to analyze and edit a class
┌──────────┐
│ 1 ┌────────┐ 1: ClassEditor
┌────┐ │ 2 ┌─────┐ 2: FunctionDialog
│ 4 │ │ │ 3 │ 3: HierarchyViewer
┌────┐─│ │ │ 4: Statistics - Report
│ 5 │ └──── ┌────┐┘ 5: Import - Report
│ │ │ 6 │ 6: Users - Report
└────┘ └────┘
This screen shot shows some more windows that can be spawned from a ClassEditor. The
FunctionDialog (already mentioned above) is used to enter new functions and to modify existing
ones. It shows that for many C++ tasks there are no needs to remember the syntactic details
because there is a point-and-click way to do it (e.g. to set a function to private).
There are various reports that can be generated for a class. All these reports are displayed
using the same type of TextWindow that can be used to print or modify the report text.
Among the available reports are:
The Import Report. This report shows all imports of the class either hierachically (in order of
preprocessor #include) or alphabetically sorted (with all users of the particular import appended).
This is a very useful report to detect import problems (e.g. infinite recursive mutual imports). The
example shows a hierarchical (tree) report.
The Users Report. This report shows all users of a class together with the particular import
position (pre, post or implementation).
The Statistics Report. This report lists various statistic information for the class (number of
bases, of derived classes, data members, function members, lines of code, number of comments etc.).
The HierarchyViewer shows two different display options compared with the first screen shot. It has
a zoomed GraphPane and a "detailed" class display. One of the class group functions of the
GraphPane is to display alphabetically all data and all function members in spawned ListPrompters
from which the user can pick the members that should be displayed in the graph. This is not a very
useful way to display all members in a class hierarchy, but it is a good feature when focusing on a
specific functionality that works across multiple inheritance levels.
2.2 Keep Track of Class Changes and Versions
As the number of classes is growing, it becomes more important to keep track of changes.
This is even more interesting with object oriented programming. Since one of the OOP aims is to
increase code reuse, there are many classes that are used in different projects. Changes of
a reused class caused by one project could have an effect on the other users of this class. If the
class is a potential base class, things may be even more complicated because changes of
implementation details may have an effect too. There is only one way to handle such problems:
a "tight" tracking of all changes. And because changes have to be understood afterwards, it is
again important that the logging is done in logical units (class, member etc.) and not on a simple
file basis (filename, lines).
Basis for the Browser logging mechanism is the strict class identification scheme. Each class
file holds information about the location (machine and path) and revision number of the particular
class. Each time a class is modified (e.g. by saving a function definition), the revision number is
increased automatically. This means that every change can be identified by a location specification
and a corresponding revision number (e.g. "BsaEntry [MyMachine e:\mypath 123]"). This
information is displayed in various views (e.g. ClassEditor title, Logfile entries etc.).
SCR_3.BMP : Basic logging mechanisms
┌──────────┐
│ 2 │ 1: Controller
│ │ 2: LogBrowser
│ ┌────────┐ 3: ChangeBrowser
└────────│ 3 │
┌───────┐│ │
│ 1 │└────────┘
└───────┘
As mentioned above, every programming action (saving a function, adding an import etc.) is logged
in the Controller. It is not only displayed but also written to a logfile. The system can be even
configured to use multiple logfiles simultaneously (e.g. one that is private to the user and one that is
shared by a programming group). Each log entry includes information for date, time, user id, operation
(add, modify, remove), class, class location and revision number and the type (e.g. function) and name
of the changed item (e.g. a function name). Even the symbolic machine name is logged to enable
a precise subsequent log analysis. To increase robustness, the logfile is kept open just for the duration
of the write operation.
The tool for logfile analysis is the LogfileBrowser. It displays the contents of a logfile in a structured
way suitable for very specific questions (e.g. all function changes of a particular class done by a
particular programmer since a specified date). The panes in the upper half display all change dates,
all changed classes with locations and revision number, all programmers that made changes and
all change operations. Each selection is a filter for all information to the right. Date filtering is done
on a "since" basis (i.e. all changes since that date are displayed). The pane in the lower half lists
all matching log events (in a slightly different order than the Controller). Below the menubar, there
is a list of CheckBoxes for each item type to display (e.g. functions, datas). The number of entries
of this particular type is appended in brackets.
For very sensitive change operations (usually for classes with a high fan out), the changed item
description (e.g. function text) can be logged too. This can be done on a per class basis (all changes
for a class are logged to a class related file) and on a session basis (all changes done in a session
are logged in a user selectable file). The class logging can be enabled by a ClassEditor menuitem,
the session logging can be activated by a Controller menuitem. Class logging may also be automated
by giving that class a special attribute (monitor). Each time a ClassEditor is opened on such a class
it automatically logs all changes.
To browse through such a change file, the ChangeBrowser is used. Its upper three panes show
the dates, users and change operations that appear in the change file. These panes can be used
to filter the entries displayed in the middle pane in a similiar way like the LogBrowser. The middle
pane shows matching change entries like they are displayed in the ClassEditor (with type name
and all appropriate attributes). For selected function or insert change entries, the changed
definition / text is displayed in the lower pane. It is even possible to restore changes back to the
current class by using the popup menu for this pane. But this again increases the revision number,
there is no way to decrease it (except by setting up a new Browser system on a fresh copy
of existing classes from another location).
SCR_4.BMP : Archiving class revisions and change files
┌────────────┐
│ 1 │ 1: ArchiveBrowser
│ ┌───────┐ 2: ArchiveViewer
└───────────│ 2 │ 3: ChangeBrowser
┌──────────┐│ │ 4: ClassEditor (on archived version)
│ 4 ┌───└───────┘
│ │ 3 │
└───────│ │
└─────────┘
Logging to change files is a useful mechanism for sensitive operations / classes, but it produces
some overhead that might not be appropriate for all classes all the time. This is where the archive
mechanism comes in. A Browser archive is a simple ZIP file that can be handled outside of the
Browser too. Archives are kept in user configurable directories (e.g. on a dedicated server). There
are two types of archives:
The Class Archive which is kept on a per class / per location basis (every class in a specific location
has its own class archive). This archive hold two types of files: class revisions and change files.
The first is a complete copy of a revision of the class. The second is a complete copy of a
change file.
Group archives contain complete copies of different classes and are used mainly as a delivery
mechanism (e.g. all classes for a particular application, in a particular location etc).
To view the contents of all available class archives, the ArchiveBrowser is used. It has two
panes. The left pane shows symbolic names that have been attached to class revision saves
(e.g. "rel 1.1"). If such a name is selected, only the archive entries that have this attribute are
displayed. These names are mainly used to identify class library releases (i.e. a snapshot of
all class revisions at a particular date/time). The right pane shows all matching archive entries
alphabetically sorted by class name. There are a number of options available via a set of buttons
at the top of the window. There is a choice if all or just the most recent archived revisions
of a class are displayed. It is also possible to get the "nearest" archived revision of each class
for a particular date (this is useful to get a "best guess" state of the class library that was not a
named release). By using the popup menu of the entry pane, a (readonly) ClassEditor on the
selected archive entry can be opened.
The ArchiveViewer can be used to have a closer look at the contents of a class archive. It
consists of two panes. The upper pane shows all entries (files) contained in the archive.
Class revision entries are shown with a small symbol and a single revision number. Change
file entries are shown with the range of changes (e.g. "6-7") that are covered by this file.
The lower pane shows multi line comments either for the whole archive or for the selected
archive entry. Existing entries can be expanded to a ClassEditor (for a revision entry) or to a
ChangeBrowser (for a change file entry) by popup menu or doubleclicking. New entries can
be added via menubar (archive current version, archive current change file) and a special archive
dialog. This dialog is used to specify symbolic (release) names as well as the multi line comments.
After a change file has been archived, it is emptied to prevent redundant archiving.
To update the class archives for a set of classes, the HierarchyViewer is used instead of opening
an ArchiveViewer for every incorporated class. Therefore it is possible to specify the release
name just once and perform all the archive updates in the background. If the current revision
of a class is found to be already archived, only the release name is appended to the particular
archive entry. There are no mutlpile archive entries for the same revision number of a class.
SCR_5.BMP : Compare and Merge with a different class set
┌───────────────────┐
│ 1 │ 1: DirectoryMerger
│┌───────┐┌────────┐│ 2: DirCompareView
││ 2 ││ 3 ││ 3: ClassCompareView
│└───────┘└────────┘│ 4: ClassEditor (on own class)
│┌───────┐┌────────┐│ 5: ClassEditor (on extern class)
││ 4 ││ 5 ││
│└───────┘└────────┘│
└───────────────────┘
This screen shot shows the DirectoryMerger which is used to compare the current class
library with the contents of a different directory holding class sources. It is the main tool
to merge different class libraries. The DirectoryMerger has four child windows.
The DirCompareView consists of three panes. The upper pane lists the classes that
are found in the library and in the external directory but which are different. For each
different entry, the location, revision and date is displayed for both the library and the
external version of the class. The most recently changed version is marked.
The middle pane lists all classes found only in the external directory, the lower pane
lists the classes that are present just in the current class library. Via popup menu, it is
possible to copy a class (or a group of classes) from the external dir to a library location.
If a class is updated from an external location, the own revision number is kept and a
history record stating the source of the update (external location and revision) is added
to the class file.
If an entry in the upper pane (different classes) is selected, three additional views are
opened: a ClassCompareView, a ClassEditor on the library version of the class and
a ClassEditor on the external version of the class. If an entry in one of the lower panes
is selected, only the particular ClassEditor is opened.
The ClassCompareView has the same functionality like the DirCompareView with respect
to two different versions of a particular class. It again has three panes that hold different,
external only and library only items of that class. The displayed item type (function, data,
import etc.) is controlled with a set of buttons located at the top of the view. Like in the
LogBrowser, for each item type the number of different/external/own entries is appended
in brackets. When selecting an entry in one of the three panes, both ClassEditors are
positioned on the corresponding item (e.g. a function member), differences are marked
(e.g. different function body lines). Again it is possible to update the own class with the
selected item from the external class via popup menu.
There is nothing special about the two ClassEditors except that they are opened in
readonly mode
2.3 Build and Test *.OBJ, *.EXE and *.DLL
SCR_6.BMP : Building an Application
┌──────────┐
│ 3 │ 1: ApplicationBuilder
│ │ 2: MessageViewer
└──────────┘ 3: ClassEditor
┌───────┐┌────────┐
│ 2 ││ 1 │
└───────┘│ │
│ │
└────────┘
The tool used to compile class sources into *.obj files and to link objects into executables (or dynamic
link libraries) is the ApplicationBuilder. It represents an interactive substitute of NMAKE.EXE
and automates all required compiler and linker calls (which are invisible to the user).
The view mainly consists of two panes:
ClassPane (upper)
LibPane (lower)
The ApplicaionBuilder can be launched from a HierarchyViewer or a ClassEditor for all classes that
have appropriate attributes (app or dll). When opened, it determines which classes are required for the
application. Each of these classes is checked if it has an outdated object (older than the source) and
what have been the switches that were used to compile the *.obj.This list is shown in the ClassPane.
The attributes (switches) of each object (e.g. compiled optimized, inline, debug) are shown by several
bitmaps. An already-build-object is marked by the size of the corresponding *.obj file (behind the class
name). If an object has to be be recompiled (e.g. if the class source was changed) it is marked by a cross
in front of the entry. Selective update of one or more entries (e.g. all derived classes of selected one) can
be forced via popup menu. This menu also can be used to launch several analysis and statistic tools
( users, imports, layers, listings of corresponding *.lst and *.asm files ...).
The LibPane shows the list of all *.lib files required for linking the application. This list also is determined
automatically from a (user configurable) library description. The user has complete control over the
library list and can add other libraries as well as remove existing ones via the pane popup menu.
The actual build operations ( compile and link) are started by the barmenu items.
<Compile> causes all entries in the ClassPane that are marked for update (optional and required)
to be recompiled due to the switches specified by the ButtonPane at the top of the window. These
switches affect compiler flags ( debug, profile, optimize, inline), generation of additional information files
( *.map, *.lst, *.asm etc.), and compile / link options for use of several runtime analysis tools
(heaptrace, IXTRA, IPMD). The actual compiler switches (e.g. /O+ for "optimize") can be set in a
configuration file.
<Build> starts the compiler (as mentioned above) and, on successful compile, starts the linker to
bind the objects and specified libs. Linkage type depends on selected linker flags ( pm, vio, full).
Due to the attributes of the project a *.dll or *.exe file will be created.
A started build cycle may be aborted at any time by <Stop>.
When detecting compiler errors the ApplicationBuilder pops up a MessageViewer containing
information about the source error(s). Each entry informs about the file and line number where the
problem is located, as well as the compiler generated error description. Error specific *.INF
information can be shown via the popup menu.
To examine and correct an error, this entry can be dragged on a ClassEditor that will be
automatically positioned on the code that caused the error.
If an *.exe could be build, it can be started via the <App> submenu. If special build options were
specified (e.g. debug or profile), the appropriate tool (e.g. IPMD or IXTRA) is started instead of the
application.
The <Show> submenu is used to list and analyze additional information files( e.g mapfile, heaptrace)
described below.
SCR_7.BMP : More Views on compiled classes
┌───────────┌────────┐
│ 4 │ 1 │ 1: ApplicationBuilder
│ │ │ 2: MapfileViewer
┌──────────┐└────────┘ 3: AsmViewer
│ 2 │ ┌───────┐ 4: ClassEditor
│ │ │ 3 │
│ │ │ │
└──────────┘ └───────┘
As mentioned above, several additional information files may be generated by the ApplicationBuilder.
This screen shot shows the AsmViewer and the MapfileViewer.
Compiling an *.obj with the "asm" attribute will create an *.asm file. This compiler generated assembly
file can be showed in the AsmViewer which consists of two panes. The upper pane lists all public
entries found in the *.asm file. If an entry is selected, the corresponding assembler code is displayed
in the lower pane. This tool is mainly used for optimization problems.
The MapfileViewer analyzes a compressed version of the *.map information created for applications build
with the "map" attribute. From top to bottom, it shows a list of the application segments, exported and
imported functions and a list of all public functions of the application (that can be sorted either by name
or address). Selecting a segment filters the function list to all functions located in this segment. Selecting
a function also marks the segment in which this function resides.
This tool is very useful to detect locations of runtime errors ( e.g. general protection faults) in
applications build without debug info. For large applications, it often is not feasible to use debug
information for every incorporated class because of the compile speed and executable size.
When entering the address of the exception (obtained by the OS/2 exception MessageBox) in the
top left EntryField, the MapfileViewer selects the function where the error occurred. This information often
is appropriate to locate an error in the source code without blowing up the *.exe with debug info. If it is not,
it gives a good starting point for deciding which classes have to be recompiled with debug info.
SCR_8.BMP : Addtional Test Tools
┌────────────┐
│ 3 │┌───────┐ 1: HeapTracer
│ ││ 2 │ 2: MemTileViewer
└────────────┘└───────┘ 3: ClassEditor
┌───────────────┐ 4: ApplicationBuilder
┌─────│ 1 │
│ 4 │ │
│ │ │
└─────└───────────────┘
An application that was built with the "heaptrace" attribute logs all lifetime heap operations ( new, delete)
in a special file. The file contents may be subsequently analyzed by the HeapTracer. This tool is mainly
used to detect memory leaks.
ModulePane ( upper)
TypePane ( middle)
OperationPane ( lower)
The ModulePane shows statistic information about classes that issued heap operations ( e.g. number of
new and delete operations, allocated bytes).
A type - based view is provided by the TypePane. This pane displays how many instances of a
type (e.g. a class) were allocated, how many freed, percentage and total byte allocation.
Perhaps the most important view is the OperationPane. All heap operations ever made during the
lifetime of the application are shown here in the order of their execution. Each entry consists of the
operation type ( new, delete), its address space, the module ( class) which caused that operation, the
storage type and the allocated memory size. The selected entry can be dragged to an open ClassEditor
which is automatically positioned on the code that performed the heap operation.
The OperationPane contents may be filtered due to the RadioButton selection at left. For example,
selection of "alloc" shows only active (not freed) allocation entries. Depending on the state of the
application, such entries might be memory leaks,
Heap space fragmentation can be viewed by the MemTileViewer (which can be launched from
the HeapTracer). This view displays the cluster based memory allocation for a specified memory range.
It is useful for the development of own heap managers.
3. Networking
The Browser can be used in a networking environment. The usual network configuration
(that can be modified by the user) has a set of (server) directories which hold the
classes that are visible to all programmers. In addition, each programmer has one or
more (local) directories for private classes or classes that are not yet ready for an upload to a
public directory.
Classes also can be locked. All users except the owner (who locked it) have just readonly
access to such a class. Lock information (who locked it when for what purpose) is displayed
when accessing a locked class.
4. Requirements
OS/2 2.1
IBM C/Set++
4 MB disk space (for the binaries)
1024 x 768 display (or above) recommended
486 processor recommended
>= 8 MB recommended
Even if this depends on personal preferences, it is strongly felt that an appropriate display
system is among the top priorities of this requirement list. Much of the Browsers productivity
momentum depends on being able to deal with a lot of information simultaneously. This is
simply not possible to achieve on a 640x468 VGA display.
Since many Browser operations make heavy usage of the file system, a fast harddisk should
be installed ( < 20 ms).
5. Experiences
Originally the Browser was intended to be just an internal tool for a large class library
- called the BSA library - which covers all OS/2 programming tasks (lots of GUI classes,
process / thread / file / pipe handling, database access, generic containers etc.). This library
now consists of about 300 classes that have been built by 4 programmers in 2.5 years. All
work on these classes has been and will be done with the Browser. Currently there are about 10
programmers in 3 different companies working with the Browser. A list of reference customers is
available from BISS.
The Browser also has been and currently is used for its own development.
6. Availability
6.1 General Availability
The official product release if scheduled for June 1994. There will be an introductory price of $ 295.
The regular price for the basic version will be $ 495. Site licenses as well as volume discounts will
be available. There will be versions with additional tools (e.g. graphical class editor) as well as
versions with class libraries. Versions for the Borland and Watcom compilers are scheduled for
the second half of the year.
6.2 Beta Testing
Currently there are plans for another beta test cycle starting in April. Beta testers should have
solid C++ skills. To prevent communication delays, the number of additional test sites has to be
restricted ( < 15 ). If you are interested in attending the beta test program, please send a short
profile of your company, a brief description of what you are doing with C++ and the number of
programmers that would be involved in the testing. There are no costs for the test program.
Compuserve will be used for communication purposes.
The main problem for current beta testers is certainly the lack of English documentation. This
will be solved (together with online help) by end of April.
6.3 How to get in Contact with BISS
BISS GmbH
Chaukenweg 12
26388 Wilhelmshaven
Germany
phone: 49 4423 1235 (office, time difference to Eastern time: +6 h)
FAX: 49 4423 2360
Compuserve: 100031,1733 (best way to get in touch with the browser people)