(If you do not see the frame version of this page with a table of contents in the left frame, click here.)

About the farSlang Language



  1. About the farSlang Language
      1. Introduction
      2. What's in it?
      3. The Language Structure
        1. Modules
        2. Objects
        3. Procs
        4. Types
        5. Variables
        6. Globals
        7. Constants
        8. Statements

        9. Reserved Words
        10. farSlang Constants
        11. Intrinsic Functions
        12. Boolean-in Functions
        13. Integer-in Functions
        14. Real-in Functions
        15. String-in Functions
        16. Miscellaneous
        17. Event Names
      4. Sample farSlang Modules
        1. Communications
        2. Module 1 - SendTest.far
        3. Module 2 - RecvTest.far
      5. Another farSlang Module
      6. farVIEW Library APIs
As the name suggests, "farSlang" stands for "farVIEW's Language." No effort was made to solve every problem that faces a general programming language, since it's intended to make it easy to take advantage of the farVIEW environment. farSlang is most similar to the block structured languages of N. Wirth, but draws from a number of sources, including my own opinions. I don't know if Wirth would agree that it is similar to his languages, though - it does have some somewhat novel ideas in it. Above all, it tries to be as simple as possible, but no simpler, to paraphrase one of my heros.

As a language, farSlang has as few rules as I could get away with. The flavor of it is, after all, of being a "casual" scripting language. However, I don't believe that that means that it should not be rigorous. I believe in letting the tools help as much as possible: As a programmer, I want to focus on the problem of the application, not the language. For example, there are no rules about statement order, only that symbols be defined within scope before they are used (yes, you do have to define them). Also, all container statement types, such as proc, if, while, loop, etc., guard a list, or block, of statements, rather than only a single statement, and, thus, are always terminated with an "end-word." Thus, the need for braces, as in C or C++,  or begin-end blocks, as in Pascal, is reduced or eliminated. This feature is more like Modula, Oberon, or Ada(sorry).

Another important feature is that there is no explicit memory management in farSlang. Every object is created and accessed by reference, without pointers (i.e., the implementation hides the pointers). This is in keeping with the trends we see in other interpreted languages, such as Java.

Like C, farSlang has no ability to communicate with the outside world: it depends upon a library for that. To make this feasible, the farSlang library includes a parser that can extract salient features from a set of C++ interface files to provide information and code for the farSlang compiler and interpreter.

While we are mentioning linking, farSlang links modules at run-time - the programmer never needs to deal with this. The compiler records the resources a modules uses as it is compiled and its dependencies are resolved when the module is loaded. (This makes linking a recursive process.)

farVIEW supports an XML language, called Forms in this documentation, which is a subset of Mozilla's XUL. farVIEW's Forms allows to create complex on-screen forms using a declarative language in a similar manner as with HTML. Where JavaScript provides event handling in HTML, farSlang provides event handling in farVIEW's Forms. The documentation for Forms is, to a large extent, in the Change Log on this website, although you should start with the Mozilla link and the Forms page in this documentation. There are many code samples for farSlang in the fars folder, and there are many Forms/XUL samples showing the use of farSlang in the xul folder.

Well, you'll probably say that the world doesn't need yet another programming language, but, for a number of reasons, as I considered whether to use some other language or to roll my own, I continually returned to the belief that, at least for the time being, the farVIEW project is better served by upgrading the farSlang language from the old DOS farVIEW. The old farSlang is well documented and simple, it has worked well for a number of years within the farVIEW environment, it has no strings attached, and I am not at the mercy of others for what the language is. No committees. It can be adjusted as needed to make it pour into the new farVIEW environment. Nor can anyone design it out from under my application. So, I bit the bullet a couple of Springs ago and upgraded the definition of the language to support, among other things, inter-module calling, runtime compiling and linking, OOP, access to the C run time library, and access to my substantial farVIEW tool libraries. That also involved revamping the compiler and the interpreter, writing a new C++ parser module, an interface module to support load and run by module name, and a few other bells and whistles.

Here is the "Hello World" program in farSlang:  

   module helloWorld
                  "Hello World", 
                  FWW_ATCENTER | FW_OK).Run
      endModule helloWorld

or, in a more compact, simplified form:

   module helloWorld
      alert("Hello World")
      endModule helloWorld

To run helloworld.far, click the farSlang/Run menu item to obtain the Run Module dialog. Enter "helloworld" in the Module Name field. farVIEW will compile and run the module when you click the Ok button. The message box will appear in the center of your screen. Click the Ok-button or press the Enter-key to close the box. farVIEW will take awhile the first time you run a farSlang module because a bunch of files has to be loaded and linked before the first compile. After the first compile, performance is more as what you would expect.

Note that farSlang programs and scripts are maintained in the fars subfolder. 

Also note that a complete list of all APIs available to the farSlang coder in the farVIEW library are available via the link in item 6 above. 

What's in it?

The farSlang library is a set of components that are compiled together to form a dynamic link library (DLL). The library contains the following primary components: There are a few additional modules that support these functions, which are included in the release file. Additionally, this DLL depends on some tools found in another DLL, called farBase, which I am not describing here.

There are three applications that use the DLL:

These applications are compiled and run separately, each using the farSlang DLL to carry out their tasks. Because the Slanger C++ parser generates actual code that must be incorporated into the interpreter, after running that, the DLL must be recompiled to incorporate the Slanger results.

The Language Structure

farSlang statements are terminated as in the style of C and C++; viz., with a semicolon. Don't worry about this, though, because the compiler will insert the semicolons for you if you never place more than a single statement on a line. Just remember that if you want more than one statement on the line, you have to insert the semicolon between statements on the same line.

You can continue a statement to the next line if the last character on the the line is the backslash character ("\"). You can also continue a statement using a binary operator as in the following example:  

   x := "this string is " +
      "continued on the next line"

(Comma (",") and Dot-connector (".") works to continue a statement, too.)

farSlang comments are the same as C++, except you use two dashes instead of two slashes, as in the following example: 

   -- this is a comment

Note that the --comment extends only to the end of the line. You can also use curly braces to delimit comments - they span lines - as in the following example:  

   { this is a
      comment spanning
      three lines }

farSlang doesn't currently support conditional compilation, but it does support an include directive.

   --#i include.inc

will include the file include.inc. The directive must reside wholly on a single line,  must start in column one, but you can place any amount of space between the directive and the file name.

farSlang is not a case sensitive language, however, C++ is. The run-time library is a collection of classes and is implemented in C++ so you must be conscious of case when interacting with the libary. In consequence, I recommend that you always practice case sensitivity, even though I don't like it much, myself. Oh well.

BTW: The notation I am using here is not rigourous, I know it isn't, but I'm dashing this off (buy that?). Besides, there is example code to help fill in the gaps I have left. (If you see some, though, bring them to my attention, I'll try to fix them.)


The farSlang compiler expects to find farSlang code inside a module-statement, which has the following form:
   module <moduleName> [ <baseTypeDesignation>]
      -- implementation of <moduleName>
      endModule [<moduleName>]

(The form of a type designation is given later in the section on Types.)

For example, in one of the code samples included later in this note, its module-statement is  

   module xml
      endModule xml

farSlang allows one and only one module statement within a module source file, and the name of the file must be the name of the module with the extension ".FAR." When the compiler emits the object file, it uses the module name with the extension ".FOB."  For the example above, the source file name is xml.far and the object file name is xml.fob.

To assist the farSlang programmer, farVIEW deletes all FOB-files for which there is a FAR-file when you start a farVIEW session, and it recompiles them as needed during the session.

When you declare the event handlers in the events list of a farVIEW object, you identify the code to be executed to handle an event using the module name and the proc name of the handler, as in the following example:  

   LeftButtonUp = commMod.SendFile

where commMod is the name of the module in the library, and SendFile is the name of a parameter-less proc contained in commMod. LeftButtonUp  is the name of the Left-mouse-button-up event.

When you write farSlang code that calls a proc in another module, or in a C++ class in the library, you use the same notation (though the call can pass parameters, of course), as in the following example:  

   result := myModule.myProc(param1, param2)

You can write code in a farSlang module in any order that is convenient for you to solve the problem, as long as you declare symbols before you use them and as long as the symbols are within the appropriate scope (as in Pascal). This means that you can mix declaration statements, such as var-declarations, con-declarations, glb-declarations, and procs interspersed with active statements such as assignment statements any way you want. Unlike C and C++, you can also nest procs, and you should do that since nested procs are hidden from the outside world. For you C and C++ programmers, a proc that you write as a child of another proc is hidden, while a proc written as the child of the module is exposed and can be called from the outside world. (You can hide a module proc with a special marking (tilde "~"), however, which I'll mention when I discuss procs later.) There are several farSlang modules at the end of this note that you can look over to get a more visceral sense of the language.


farSlang supports Object-Oriented Programming (OOP), using a very simple yet novel approach; viz., instead of supporting OOP as an extension of the C struct, or the Pascal record, for example, farSlang uses the module itself as the object type. After all, the farSlang module is a container of variables and procs, just like a C++ class. (This was the easiest and simplest way I could figure to have objects.) You make a module into an object type simply by referencing it in other modules, rather than by any special declaration in the module. For example, to declare a variable (say, x) as an object type that you have implemented (in another module), write the following:
   var x: myType

where myType is the name of your object type module in the library. You can declare a variable and initialize it at the same time as in the following example: 

   var x := myType

To derive an object type (say, myDerivedType) from an existing type-module (a module called myType, say), write the following:  

   module myDerivedType isa myType
      -- implementation of myDerivedType
      endModule myDerivedType

Under certain conditions, you can also change the type of a value in a farSlang statement

Several problems that crop up in the way C++ implements OOP are eliminated with this approach. For example, in farSlang, all procs are virtual, so you don't have to predict the future by preregistering a proc as virtual as in C++. If you override the proc in a derived module, it becomes virtual. You can access the code of an overridden proc using the same nomenclature that you use to call any proc that is outside of a module, which I described earlier.


You use procs the same way and for the same reasons you would use a Pascal function/procedure, or a C function, or a C++ method. The form of a proc-statement is
   proc ["~"] <procName> [ <returnTypeDesignation> ] [ ( <formalParameterList> ) ]
      endProc [ <procName>]

   <formalParameterList> ::= [ <typedSymbol> { "," <typedSymbol> }

(See Types below for what a typedVariable is.

Use the tilde ("~") to hide the proc if it is a module proc.

The rules for how you pour farSlang code into a proc are the same as they are for a module; i.e., not much. You can code any farSlang statement, including other procs, (except for module statements, of course) inside a proc.


farSlang supports three types(?) of types:
Intrinsic types simple types defined and supported by the language
Module types object types associated with farSlang modules
Library types object types associated with the C++ library

There are four intrinsic types:  

have values of "true" and "false"
is a 32-bit number
is a double
uses matching quotes, single-quotes, or back-quotes (whatever they are called:-

We discussed module types (i.e., classes) earlier. Library types are the object types exported by the C++ library, which serves as the farSlang run-time library. The compiler and interpreter interfaces to the library are generated with the Slanger tool mentioned earlier. farSlang supports module types and library types in the same way from the programmer's point of view. Memory management is transparent to the programmer, so there is little to say to the experienced OOP programmer about how all that works. Its pretty standard: you don't use the "->" symbol as in C++, of course, rather, you use the dot-connector. (if you have a question, you can email me from the front page of this site.)

Symbol types are generally known at compile-time, except for derived types, of course. But the interpreter tracks the type of a variable at run-time and uses that information when resolving virtual proc calls.

You designate the type of a symbol in a var-declaration, glb-declaration, or con-declaration according to the following form:  

   <typedSymbol>     ::= <symbol> [ <typeDesignation> ] [ ":=" <typedExpression> ]
   <typeDesignation> ::= "?" | "#" | "%" | "$" | [ ":" | isa <type> ]
   <typedExpression> ::= if you omit the <typeDesignation>,
                         the symbol obtains the expression type.

Here are some correctly coded examples:  

   x: myType
   y isa mytype
   s := "this is a string"
   b? := true


I'll assume you know what a variable is, this isn't a programming lesson. What this section does is show you how to declare a variable.
   var <typedSymbol> { "," <typedSymbol> }

Here is a sample variable declaration list using the examples in the Types section above:   

   var x: myType, s$, i#, b? := true

On second thought, here is something I should mention: there is a distinction in farSlang between declaring a variable and initializing it, which may not be obvious. Use the ":" typing operator to declare a variable or global, and use the assignment operator (":=") to initialize them. Consider the following examples:

   var x: myType
   var y := myType

The first example declares the variable x, while the second example both declares and initializes y. See the difference? Obvious enough, until you try to find it in a bunch of code.

Ok, that's it. Now on to globals.


While variables have value and are visible only to the code within the module and scope in which they are declared, globals persist through the life of the thread in which they are declared. On the other hand, a global has the same visibility within a module in which it is declared as does a variable: that is, its visibility follows the same scope rules as does a variable, proc, or any other named object. You expose one or more globals within a module according to the following form:
   glb <typedSymbol> { "," <typedSymbol> }

Here is the sample variable declaration list converted to a global declaration list:  

   glb x: myType, s$, i#, b? := true

Note that the first four declarations do not change the (existing) value of the globals they expose, while the fifth exposes b and sets it to true. if a global already exists when it is exposed in a module, its value is inherited by the code in the module. If it does not already exist, it is instantiated and its value is set to the default for its type. Of course, code in the module can change the value of a exposed global as it can a variable.


Declare constants according to the following form:
   con <typedSymbol> { "," <typedSymbol> }

Here is a sample:  

   con myName := "Paul Medlock", myStreeNumber := 1234

The compiler doesn't support constant expressions right now. I have some code with which to do it, so maybe I'll throw it in and allow that later.


This section is a brief list of the the various statements you use to write a farSlang program.

Module statement:    The container for other farSlang statements. Only one module statement per file.  

   module <moduleName> [ <baseTypeDesignation> ]
      endModule [<moduleName>]

Proc statement:
    The container for other farSlang statements, including other proc statements.  

   proc ["~"] <procName> [ <returnTypeDesignation> ] [ ( <formalParameterList> ) ]
      endProc [ <procName>]

    where <formalParameterList> ::= [ <typedSymbol> { "," <typedSymbol> }

Variable declaration statement
:    Declares and optionally initializes variables.  

   var <typedSymbol> { "," <typedSymbol> }

Global declaration statement
:    Declares and optionally initializes globals.

    glb <typedSymbol> { "," <typedSymbol> }

Constant declaration statement
:    Declares and initializes constants.  

    con <typedSymbol> { "," <typedSymbol> }

Loop statement
:    Iterates a statement list without termination. Use the Continue and Exit statement types to control the loop.


Continue statement:
Unconditionally repeats a loop statement, or terminates a begin-end block.  


ContinueIf statement:
Conditionally repeats a loop statement, or terminates a begin-end block.  

   continueIf <booleanExpression>

Exit statement
:    Unconditionally terminates a loop statement, or begin-end block.  


ExitIf statement:
    Conditionally terminates a loop statement.  

    exitIf <booleanExpression>

While statement:
    Iterates a statement list while a condition is true. 

    while <booleanExpression> do

Repeat statement:
    Iterates a statement list until a condition is true. 

        until <booleanExpression>

If-Elsif-Else statement:
    Alternates among a set of guarded statement lists according to expressions.  

    if <booleanExpression> then
    { elsif <booleanExpression> then
        <elsifStatementList> }
    [ else
        <elseStatementList> ]

Assignment statement:
    Evaluates an expression and assigns the result to a variable or global symbol. 

    <symbol> := <expression>

ProcCall statement:
    Calls a proc that doesn't return a value. 

    <procName> [ (<actualParameterList> ) ]

Return statement:
    Returns control from a proc, optionally returning a value according to the type of the proc.  

    return [ <expression> ]

Begin-end statement:
    Used to group code because you want to.  Use the Continue and Exit statement ty0pes to control the block.


Statement list:

    { <statement> ";" }

Reserved Words


 farSlang Operators
   :     (colon) typing operator
   ;     (semicolon) statement terminator
   (     (left parenthesis) begins an expression
   )     (right parenthesis) ends an expression
   {     (left brace) begins a comment
   }     (right brace) ends a comment
   --    begins a single-line comment
   [     (left bracket] not currently used
   ]     (right bracket) not currently used
   +     (plus) addition/concatenation operator
   -     (minus) subtraction operator
   *     (asterisk) multiplication operator
   /     (slash) division operator
   =     (equal) comparison operator
   ~     (tilde) proc hiding operator
   <     (less than) comparison operator
   <=    (less than or equal) comparison operator
   >     (greater than) comparison operator
   >=    (greater than or equal) comparison operator
   ,     (comma) list item separator
   .     (period) dereferencing operator


farSlang Constants

   maxNr = 2147483647

Intrinsic Functions

farSlang takes advantage of the C runtime library. Heck, it was there; how could I refuse? The following sections identify and will eventually describe the farSlang intrinsics runtime library. The description uses a simplified version of standard farSlang notation, whereby the parameters are listed by their types only.

You can use a couple of notational methods to invoke a library function, depending on your mindset and mood. The first method is the traditional method as in the following example:

   var s := "after the fall"
   s := after(s, "after ")

or you can annotate the same call as in the following example:

   var s := "after the fall"
   s := s.after("after ")

which allows a little of the object-oriented flavor to seep into the more mundane code we have to write. Don't do that with the Miscellaneous functions, though.

Boolean-in Functions

   proc string$(?)

Integer-in Functions

   proc abs#(#)
   proc char$(#)
   proc dec#(#)
   proc inc#(#)
   proc max#(#, #)
   proc min#(#, #)
   proc odd?(#)
   proc random#(#, #)
   proc string$(#, #)

Real-in Functions

   proc abs%(%)
   proc acos%(%)
   proc asin%(%)
   proc atan%(%)
   proc atan2%(%)
   proc cos%(%)
   proc cosh%(%)
   proc exp%(%)
   proc log%(%)
   proc power%(%)
   proc sin%(%)
   proc sinh%(%)
   proc sqr%(%)
   proc sqrt%(%)
   proc tan%(%)
   proc tanh%(%)
   proc ceiling%(%)
   proc floor%(%)
   proc string$(%)

String-in Functions

   proc after$($, $)
   proc before$($, $)
   proc boolean?($)
   proc copy$($, #, #)
   proc delete$($, #, #)
   proc insert$($, $, #)
   proc integer#($)
   proc isalnum?($, #)
   proc isalpha?($, #)
   proc iscntrl?($, #)
   proc isdigit?($, #)
   proc islower?($, #)
   proc isprint?($, #)
   proc ispunct?($, #)
   proc isspace?($, #)
   proc isupper?($, #)
   proc length#($)
   proc locase$($)
   proc ord#($)
   proc pack$($)
   proc pad$($, #, #)
   proc pos#($, $, #)
   proc real%($)
   proc trim$($, #)
   proc upcase$($)


   proc beep(#)
   proc catFileName$(%, $, $)
   proc changeDirectory?($))
   proc changeDisk?($)
   proc currentDirectory$
   proc cutExtension$($)
   proc cutPath$($)
   proc extension$($)
   proc fileExists?($)
   proc format$($,#)
   proc beep(#)
   proc GMT$
   proc log?($)
   proc makeDirectory?($)
   proc path$($)
   proc removeFile?($)
   proc renameFile?($, $)
   proc sleep#(#)
   proc time$
   proc uniqueID$

Event Names

Here is a list of the names for most of the events to which a farSlang module can respond. Review the short discussion of events in the Module section of this manual.

This list is not final.

Sample farSlang Modules


I wrote the following two modules to test the farVIEW communication facilities. One module runs on Paul's machine and sends a file to the second module, which is running on Dave's machine. They give some of the flavor of how two farVIEW machines will communicate at the farSlang level. For the test situation, they are written to connect over the LAN between hard-coded ip-addresses. The low-level farVIEW communication protocol is slightly modified HTTP. The farVIEW communication engine handles the protocol-level activities such as message validation and acknowledgement responses. It also handles the security functionalities. farSlang modules don't deal with those issues.
Note: The current release of farVIEW does not have encryption and authentication facilities, as it is an early alpha release and cannot be trusted.

Note: in the following examples, types beginning with the letter "C" are C++ library classes and are not documented on this web site. You will find a complete list of the library interface in the files farslang.sym and farslang.xml. Warning: there is no natural-language discussion of the interfaces in either of these files.

Another warning: Do not change farslang.sym. It is used by the compiler to define the libraries.

Module 1 - SendTest.far

-- This module runs on Paul's computer. It opens a session with Dave's
-- computer, then sends him a copy of sendTest.far. It is a simple
-- example of Remote Procedure Calling (RPC), since it calls a proc
-- on Dave's computer (recvTest.recvFromPaul) to receive the file.
-- To run, click the farSlang/Run menu item to obtain the Run Module
-- dialog. Enter "sendTest" in the Module Name field, and "sendToDave"
-- in the Proc Name field. farVIEW will compile and run the sendToDave
-- proc when you click the Ok button.

module sendTest

   -- Send a file to Dave
   proc sendToDave
      glb System: CSystem

      var Dave   := UniqueID
      var result := CCommResult(true) -- set to wait for completion

      -- Open a session with Dave
      System.Comm.OpenSession(Dave, '', 1336 , result)

      -- Load a text file into a string list
      var body := CStrList
      body.ReadFile('fars\sendTest.far', '')

      -- Send the string list to Dave

      -- Check the result
      if result.GetResult <> COMM_OK
         CMessageBox("Sending to Dave", 
                     "Send failed", 
                     FWW_ATCENTER | FWW_OK).Run

      -- Close the session with Dave
      System.Comm.CloseSession(Dave, result)
      endProc sendToDave

   endModule sendTest

Module 2 - RecvTest.far

-- This module runs on Dave's computer. It is executed by the sendTest
-- module on Paul's computer as specified in the fourth parameter of the
-- SendMessage command. It displays a message box, then stores the file
-- on Dave's permission, then quits.
-- For this program to run, Dave must make sure that his farVIEW is
-- listening to incoming requests by setting /preferences/comm/listen=yes
-- in his preferences file farview.fix before starting farVIEW. (And, of
-- course, there must be a network connection between the two
-- computers.)

module recvTest

   -- Receive the file
   proc recvFromPaul
      glb System: CSystem

      if CMessageBox('Receive From Paul', 
                     'Dave, do you want to save the file?'
                     FWW_ATCENTER | FWW_YESNO).Run = NT_YES then
         -- Save the file
         System.Body.WriteFile("fars\sendTest.far", "")
      endProc recvFile

   endModule recvTest

Another farSlang Module

This module is a farSlang version of sample code in an MSDN article written by Markus Bosshard. It demonstrates how to use the XML DOM that is a part of the farVIEW library. (the URL no longer seems to work.)
-- This farSlang module is a translation of JavaScript code in an
-- article written by Markus Bosshard.
-- The original article can be found at
--    http://msdn.Microsoft.com/peerjournal/vc/g121699b.asp
-- The farSlang code in this sample module assumes that the XML sample
-- file contains the following:
--    <COMPANY>
--       <EMPLOYEE>
--         <NAME>Markus Bosshard</NAME>
--         <DESCR>Software Design Engineer</DESCR>
--       </EMPLOYEE>
--     </COMPANY>

module xml

   var doc := CDocument

   -- Reading an XML source
   proc readXML(name$)
      if not doc.Load(name)
          -- Tell the user about the the problem
          var err := doc.ParseError
          CMessageBox("Error reading " + name,
                      FWW_ATCENTER | FWW_OK).Run
      endProc readXML

   -- Read an XML file like this

   -- Traversing an XML DOM
   proc traverseXML(nodes: CNodeList, indent#)
      -- have we run out of nodes?
      if nodes = 0 then

      var idx := 0
      while idx < nodes.Length do
         var node := nodes.Item(idx)
         var str  :=             node.NodeName       +
                     ' Type= ' + node.NodeTypeString +
                     ' Text= ' + node.Text
         var log  := CFile("debug.log", "", false)

         traverseXML(node.ChildNodes, ident + 2)
     endProc traverseXML

   -- Call traverseXML like this
   traverseXML(doc.ChildNodes, 0)

   -- Appending elements to an XML DOM
   proc AppendNode(doc: CDocument, name$, descr$)

      proc NewTextElement: CElement(doc: CDocument, tagName$, tagText$)
         var node := doc.CreateElement(tagName)
         return node
         endProc NewTextElement

      -- Append a NAME and a DESCRIPTION element
      var node := doc.CreateElement("EMPLOYEE")
      node.AppendChild(NewTextElement(doc, "NAME",  name))
      node.AppendChild(NewTextElement(doc, "DESCR", descr))

      -- Append at the end of the list
      endProc AppendNode

   -- Add two new entries
   AppendNode(doc, "John Doe", "Programmer")
   AppendNode(doc, "Mister T", "Actor")

   -- and save the document

   endModule xml

Copyright 2000-2003, Paul J. Medlock. All Rights Reserved.
Software downloaded from this site is also copyright 1991-2003, Paul J. Medlock. All Rights Reserved