[comp.lang.c] extern

mccarrol@topaz.rutgers.edu (<MC>) (01/18/88)

I have a couple of questions concerning the extern qualifier in C.

First: Is it possible to put an external declaration outside the body
of any function? I'm writing a large program in several separate
modules, and I'd like to have a listing at the beginning of each file
of what external variables are used in the file. Can extern be used
this way? 

Second: I know that my compiler doesn't complain against extern's
outside of function bodies, but I'm not sure if it does what I want.
Is the compiler allocating space for these variables, or is it just
identifying thier names as a reference to something from another
source file, to be resolved at link-time?

	Just in case it matters ( although I doubt it; this MUST be
standardized, right?) I'm using Lattice C 3.10 on an Amiga.

		<MC>
-- 
"It is a principle of the music/to repeat the theme |Mark C. Carroll
Repeat/and repeat again/as the pace mounts.  /------/Rutgers U CS Student
The theme/is difficult/but no more difficult |ARPA  :CARROLL@AIM.RUTGERS.EDU
than the facts to be/resolved"-WC Williams   |Usenet:mccarrol@topaz.rutgers.edu

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/18/88)

In article <17428@topaz.rutgers.edu> mccarrol@topaz.rutgers.edu (<MC>) writes:
>First: Is it possible to put an external declaration outside the body
>of any function?

Sure,  In fact, that's my usual approach.

Some people who like the idea of an "import" specifier always put their
extern declarations inside their function bodies.

>I'm writing a large program in several separate
>modules, and I'd like to have a listing at the beginning of each file
>of what external variables are used in the file. Can extern be used
>this way? 

Yes.  Note that this conveniently supports having a module-specific
header file that contains extern declarations for all module entry
points and global data (i.e. a module interface specification header),
with the same header #included in the source code for the module itself.

>Second: I know that my compiler doesn't complain against extern's
>outside of function bodies, but I'm not sure if it does what I want.
>Is the compiler allocating space for these variables, or is it just
>identifying thier names as a reference to something from another
>source file, to be resolved at link-time?

Not quite either.  There are two possible methods of implementation
of extern data in C, but the model you should think of is:  If it
says "extern", it is a reference to something allocated elsewhere;
if it doesn't say "extern", it allocates storage, and of course
storage should be allocated for an object precisely once among all
the modules for an application.

The module interface specification header I mentioned above must,
therefore, declare all its global data as "extern", in order to not
allocate it in every file that #includes the header.

"extern" does not necessarily mean "in another source file"; the
actual definition can also occur elsewhere in the same source file.

john13@garfield.UUCP (John Russell) (01/19/88)

In article <7123@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
]>Is the compiler allocating space for these variables, or is it just
]>identifying thier names as a reference to something from another
]>source file, to be resolved at link-time?
]
]Not quite either.  There are two possible methods of implementation
]of extern data in C, but the model you should think of is:  If it
]says "extern", it is a reference to something allocated elsewhere;

That is the way you should think about it, but if you do something like this:

file 1 -
int fou;
...

file 2 -
extern int foo;
...
foo = 1;

then at least some compilers will run this properly (no warnings even).

John
-- 
"A Chinese soldier in Tibet who tried to tear off a British woman's Sergeant
 Bilko T-shirt has become the first known case of someone mistaking Phil
 Silvers for the Dalai Lama."
				-- Toronto Globe & Mail, Nov. 14/87

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/21/88)

In article <4405@garfield.UUCP> john13@garfield.UUCP (John Russell) writes:
-In article <7123@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
-] Not quite either.  There are two possible methods of implementation
-] of extern data in C, but the model you should think of is:  If it
-] says "extern", it is a reference to something allocated elsewhere;
-That is the way you should think about it, but if you do something like this:
-file 1 -
-int fou;
[I assume "foo" was intended]
-file 2 -
-extern int foo;
-foo = 1;
-then at least some compilers will run this properly (no warnings even).

PLEASE don't do this.  A compiler is not obliged to make this example
"work".  That is why you should think the way I said; C compilers are
obliged to always make that approach work right.

john13@garfield.UUCP (John Russell) (01/23/88)

In article <7150@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <4405@garfield.UUCP> john13@garfield.UUCP (John Russell) writes:
>-file 1 -
>-int fou;

>[I assume "foo" was intended]

Not the point. This exact situation happened to me (with different variable
names of course). DAMN tough to track down! Luckily I noticed the typo
shortly after it occurred (after running the program once).

>-file 2 -
>-extern int foo;
>-foo = 1;
>-then at least some compilers will run this properly (no warnings even).

>PLEASE don't do this.  A compiler is not obliged to make this example
>"work".  

No kidding :-). But I do think it's important to know of this peculiar
behaviour, which could easily cause hard-to-find errors when using "extern".
The original article seemed to be asking *how* extern storage was treated,
and this example shows how the compiler doesn't always behave as you would
assume under an "ideal" model of C.

This was the C on our 4.3BSD system. I was shocked that the linker didn't
complain at all that I was extern'ing a variable, then referring to it, when
it didn't exist in any other module.

John
-- 
"Am I dreaming, or was there a show on this weekend called 'Jimmy the Greek:
 Live at the Apollo'?"
					-- David Letterman

tainter@ihlpg.ATT.COM (Tainter) (01/24/88)

In article <7150@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes:
> In article <4405@garfield.UUCP> john13@garfield.UUCP (John Russell) writes:
> -file 1 -
> int fou;
> [I assume "foo" was intended]
I assume your assumption also.
> -file 2 -
> extern int foo;
> foo = 1;

I assume this last line was supposed to be a static initialization.

No C compiler accepts this.  If you have something, supposedly a C compiler,
that accepts this then you have been lied to.  Clearly, that compiler is for
some language very similar to C, but not C.

> PLEASE don't do this.  A compiler is not obliged to make this example
> "work".

In fact, C compilers should be obliged to complain about this as it does not
conform to the syntax of C.

--j.a.tainter

john13@garfield.UUCP (John Russell) (01/25/88)

In article <4694@ihlpg.ATT.COM> tainter@ihlpg.ATT.COM (Tainter) writes:
>> In article <4405@garfield.UUCP> john13@garfield.UUCP (John Russell) writes:
>> -file 1 -
>> int fou;

[      ^^^ this is a combination intentional typo and French pun ]

>> -file 2 -
>> extern int foo;
>> foo = 1;
>
>I assume this last line was supposed to be a static initialization.

Oops, no, not a static initialization but one somewhere later on, within a
function.

The point I wanted to make was that even if the variable is _never_ declared
except as extern, the compiler may in fact allocate it for you and let you
refer to it without complaining. The code even works properly.

However if you had 100 references to the variable "long_variable_name" in
file1, and 100 references to the variable "long_varaible_name" (sic!) in
file2, you could easily be operating under the assumption that they were
the same variable if you were using extern.

John
-- 
"You are lying scum!"
	-- member of Parliament James Fulton was expelled for saying this to
	   Canadian Prime Minister Brian Mulroney; use of the word "lying"
	   was deemed unparliamentary, although the "scum" was acceptable

jbeard@quintus.UUCP (Jeff Beard) (01/30/88)

Note however, that the practice of multiple definitions of a specific
global variable is extreamly poor, even IF corrected by a link/loader.

Some have been so lax as to forget that one defines ONLY once, and references
all other occurances.  Using this model, one would never attempt to
use compile time initializer for a reference and good coding practice
yeilds a win win!

emz@bjcong.bj.co.uk (ED ZIETARSKI) (06/28/90)

In article <111254@linus.mitre.org>, cookson@helios.mitre.org (Dean Cookson) writes:
> I know that if you have an extern that is an array, you don't have to use
> the size of the array in the extern declaration, you only need it in
> the original declaration.  But is it an error to include it in the
> extern declaration??
> 
> ie:
> file1.c
> int	myarray[10];
> 
> file2.c
> extern int	myarray[10];
I would say it is an error because if you or someone else changes the array size
at a later stage, (s)he would have to also know/remember that the external
declaration needs changing. Even if you use a #define for the array size, you
could still have the situation arising of someone deciding to use a *different*
preprocessor definition name. The compiler cannot pick up differences in array
sizes in a set of objects and neither can the linker (as far as I know !)
---
Ed Zietarski, Boldon James Limited, Congleton, CHESHIRE CW12 1JN, UK.
emz@bj.co.uk (UK only)
emz@boldon-james-limited.co.uk (Internet)
..!mcsun!ukc!pyrltd!bjcong!emz

steve@taumet.com (Stephen Clamage) (06/29/90)

In article <910@bjcong.bj.co.uk> emz@bjcong.bj.co.uk (ED ZIETARSKI) writes:
>In article <111254@linus.mitre.org>, cookson@helios.mitre.org (Dean Cookson) writes:
>> I know that if you have an extern that is an array, you don't have to use
>> the size of the array in the extern declaration, you only need it in
>> the original declaration.  But is it an error to include it in the
>> extern declaration??
>> ...
>I would say it is an error because if you or someone else changes the array size
>at a later stage, (s)he would have to also know/remember that the external
>declaration needs changing.

Well, it may be a poor idea, but that is not the same thing as an error.
It is not an error.

The usual way to avoid the logistical problem of maintaining multiple
declarations is to put declarations needed by more than one file into a
header file and #include the header file where needed.  In a big program,
multiple such header files may be appropriate.

In this example, you would then have the declaration
	extern int foo[20];
in the header file.  A C program which strictly conforms to the ANSI C
standard must have a defining instance of this array as well as this
declaration.  Some C implementations require the separate defining instance
and some do not.

The usual trick is then to do something like this:

header.h:
	#ifndef EXTERN
	#define EXTERN extern
	#endif
	EXTERN int foo[20];
	EXTERN double bar;
	...

In all but the file containing main(), you simple #include header.h, and
in main.c:
	#define EXTERN
	#include "header.h"
	...

Now every file which includes header.h has an extern declaration for the
global identifiers, and main.c has defining instances for them.  This is
the most portable solution I know of, and strictly conforms to ANSI C.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

karl@haddock.ima.isc.com (Karl Heuer) (07/01/90)

In article <277@taumet.com> steve@taumet.UUCP (Stephen Clamage) writes:
>The usual trick is then to do something like this:
>	#define EXTERN extern

In rebuttal, I'd like to mention that many of us find this grotesque, and
prefer to use
	/* header.h */
	extern int foo[20];
	extern double bar;

	/* something.c */
	#include "header.h"
	int foo[20];
	double bar = 3.0;
which is also completely portable, doesn't require preprocessor games, and
allows the objects to have initializers (this can sometimes be done with your
method too, but it makes it even uglier).  Since the defining source file also
includes the header, you can't get them out of sync.

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint

hannum@haydn.psu.edu (Charles Hannum) (07/05/90)

In article <16994@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:

   In article <277@taumet.com> steve@taumet.UUCP (Stephen Clamage) writes:
   >The usual trick is then to do something like this:
   >	#define EXTERN extern

   In rebuttal, I'd like to mention that many of us find this grotesque, and
   prefer to use
	   /* header.h */
	   extern int foo[20];
	   extern double bar;

	   /* something.c */
	   #include "header.h"
	   int foo[20];
	   double bar = 3.0;
   which is also completely portable, doesn't require preprocessor games, and
   allows the objects to have initializers (this can sometimes be done with your
   method too, but it makes it even uglier).  Since the defining source file also
   includes the header, you can't get them out of sync.


Some of us also find your method grotesque, because we have to alter the
definition in *two* places rather than one if we change it.  Speaking of
which, why didn't ANSI specify that initializers on externs should be
ignored?  This would be really keen for the former type of definitions.

"... preprocessor games, ..."?  It's not a game.  It's a perfectly ligitimate
use of the C preprocessor.  If we weren't supposed to use it, it wouldn't be
included (based C's minimalist philosophy).
--
 
Virtually,
Charles Martin Hannum		 "Those who say a thing cannot be done should
Please send mail to:		  under no circumstances stand in the way of
hannum@schubert.psu.edu		  he who is doing it." - a misquote

steve@taumet.com (Stephen Clamage) (07/07/90)

In article <1990Jul5.044045.1534@acc.stolaf.edu> hannum@haydn.psu.edu (Charles Hannum) writes:
>... why didn't ANSI specify that initializers on externs should be
>ignored?  This would be really keen for the former type of definitions.

The problem here is that the keyword "extern" can mean either "external"
(defined elsewhere) or "global" (defined here) depending on context.  To
simplify a bit, for data objects it means external if there is no initializer,
global if there is.  For functions, the external/global meaning is determined
solely by the presence of a function body.

I don't think that anyone will argue that this is a wonderful convention,
but the ANSI committee inherited it from long C practice.  Introducing
extern and global keywords as used in other languages (notably assembler)
would have had the advantage of precision, but would have broken many
(most?) existing programs.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

karl@haddock.ima.isc.com (Karl Heuer) (07/07/90)

In article <1990Jul5.044045.1534@acc.stolaf.edu> hannum@haydn.psu.edu (Charles Hannum) writes:
>In article <16994@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>>[Rather than the `#define EXTERN' hack, many of us prefer to use `extern' in
>>the header and a defining instance in one source file, which also includes
>>the same header.  This] is also completely portable, doesn't require
>>preprocessor games, and allows the objects to have initializers ... [and]
>>you can't get them out of sync.

>Some of us also find your method grotesque, because we have to alter the
>definition in *two* places rather than one if we change it.

If you change something as fundamental as the type of an object, you probably
have to change every reference in *every* source file.  If it's sufficiently
abstract that you don't, then use a typedef, and change only the header.

>"... preprocessor games, ..."?  It's not a game.  It's a perfectly ligitimate
>use of the C preprocessor.  If we weren't supposed to use it, it wouldn't be
>included (based C's minimalist philosophy).

This argument applies equally well to all sorts of preprocessor abuse, all
strictly legal C.  Sorry, I don't buy it.

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint

leo@ehviea.ine.philips.nl (Leo de Wit) (07/07/90)

In article <1990Jul5.044045.1534@acc.stolaf.edu> hannum@haydn.psu.edu (Charles Hannum) writes:
|
|In article <16994@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
|
|   In article <277@taumet.com> steve@taumet.UUCP (Stephen Clamage) writes:
|   >The usual trick is then to do something like this:
|   >	#define EXTERN extern
|
|   In rebuttal, I'd like to mention that many of us find this grotesque, and
|   prefer to use
|	   /* header.h */
|	   extern int foo[20];
|	   extern double bar;
|
|	   /* something.c */
|	   #include "header.h"
|	   int foo[20];
|	   double bar = 3.0;
|   which is also completely portable, doesn't require preprocessor games, and
|   allows the objects to have initializers (this can sometimes be done with your
|   method too, but it makes it even uglier).  Since the defining source file also
|   includes the header, you can't get them out of sync.
|
|
|Some of us also find your method grotesque, because we have to alter the
|definition in *two* places rather than one if we change it.

Apparently another mix-up of declarations (which I feel belong in
header files) and definitions (which I feel belong in source files).
If you find this grotesque, why make a difference for function
declarations/definitions? Be consequent and have the body of the
function conditionally included in the header file ...

I'd also like to know how the 'extern-trick-advocates' handle multiple
include files; each would need its own EXTERN macro (and guess what
mysterious failures happen if you misspell them in the source file).
And then I'm not even talking about nested include files.

    []
|"... preprocessor games, ..."?  It's not a game.  It's a perfectly ligitimate
|use of the C preprocessor.  If we weren't supposed to use it, it wouldn't be
|included (based C's minimalist philosophy).

Whether or not this is a perfectly legitimate use of the C preprocessor
is not the issue. I'm sure the Obfuscated C Code Contest will give us
again lots of perfectly legitimate programs, showing us how NOT to
code.

    Leo.

#define EXTERN GROTESQUE

imp@dancer.Solbourne.COM (Warner Losh) (07/10/90)

In article <1990Jul5.044045.1534@acc.stolaf.edu> hannum@haydn.psu.edu
(Charles Hannum) writes: 
>"... preprocessor games, ..."?  It's not a game.  It's a perfectly
>ligitimate [sic] use of the C preprocessor.  If we weren't supposed
>to use it, it wouldn't be included (based C's minimalist philosophy).

Yah, right.  And I'd suppose that you think the following is also
legal and not a "preprocessor game":

#include "pascal.h"

main()
BEGIN
	int foo;

	IF (foo) THEN
	BEGIN
		WRITE ("Fooing today\n");
	END
	ELSE
	BEGIN
		WRITE ("No foos today\n");
	END
END

The above, while strictly speaking IS 'C' (depending on the defines in
pascal.h, that is), I'd hate to have to maintain something like that.
#define EXTERN extern is a gross preprocessor game that should be
avoided, just as the above cute pascal example should be avoided.

--
Warner Losh		imp@Solbourne.COM
Boycott Lotus.		#include <std/disclaimer>

stever@Octopus.COM (Steve Resnick ) (07/10/90)

In article <1990Jul9.190744.1437@Solbourne.COM> imp@dancer.Solbourne.COM (Warner Losh) writes:
>In article <1990Jul5.044045.1534@acc.stolaf.edu> hannum@haydn.psu.edu
>(Charles Hannum) writes: 
>>"... preprocessor games, ..."?  It's not a game.  It's a perfectly
>>ligitimate [sic] use of the C preprocessor.  If we weren't supposed
>>to use it, it wouldn't be included (based C's minimalist philosophy).
>
>Yah, right.  And I'd suppose that you think the following is also
>legal and not a "preprocessor game":
>
>#include "pascal.h"
>
>main()
>BEGIN
>	int foo;
>
>	IF (foo) THEN
>	BEGIN
>		WRITE ("Fooing today\n");
>	END
>	ELSE
>	BEGIN
>		WRITE ("No foos today\n");
>	END
>END
>
>The above, while strictly speaking IS 'C' (depending on the defines in
>pascal.h, that is), I'd hate to have to maintain something like that.
>#define EXTERN extern is a gross preprocessor game that should be
>avoided, just as the above cute pascal example should be avoided.
>

I wish the boys in Redmond Washington would adhere to that philosophy.
After spending a weekend debugging some OS/2 code I have learned to loathe
dearly MS's macros. In the OS/2 programmers refrences, Quick Help, and other
MS blessed/produced products they all refer to these "improved" types and 
declarations. an example is:

#define EXTERN extern
#define far FAR
#define pascal PASCAL

What's wrong with extern far pascal foo() vs EXTERN FAR PASCAL foo()?
And why should I use "SEL n" when I mean "unsigned n"? I would bitch a lot
less if I didn't have to have MANY books open to figure out what these silly
things meant! (oddly enough, if I ask MSC to generate protos for my EXTERN
FAR PASCAL functions, they get generated correctly..)
 
Seems like we keep working towards standardization and someone in the infinite
glory wants to change it for us.... *sigh*
 
 Steve

-- 
--------------------------------------------------------------------------------
Steve Resnick -<stever@octopus.COM apple!octopus!stever sun!vsi1!octopus!stever>
408/241-1533 Process Scientific, Inc.
"0x2B|~0x2B THAT is the question!"

peter@ficc.ferranti.com (Peter da Silva) (07/10/90)

In article <1990Jul10.004710.20500@Octopus.COM> stever@octopus.UUCP (Steve Resnick ) writes:
> What's wrong with extern far pascal foo() vs EXTERN FAR PASCAL foo()?

Nothing.

> And why should I use "SEL n" when I mean "unsigned n"?

I don't know. Why should you use "FILE *fp" instead of "struct _iob *fp"?
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.
<peter@ficc.ferranti.com>

lerman@stpstn.UUCP (Ken Lerman) (07/11/90)

In article <1990Jul10.004710.20500@Octopus.COM> stever@octopus.UUCP (Steve Resnick ) writes:
[.... stuff deleted ....]
>
>I wish the boys in Redmond Washington would adhere to that philosophy.
>After spending a weekend debugging some OS/2 code I have learned to loathe
>dearly MS's macros. In the OS/2 programmers refrences, Quick Help, and other
>MS blessed/produced products they all refer to these "improved" types and 
>declarations. an example is:
>
>#define EXTERN extern
>#define far FAR
>#define pascal PASCAL
>
>What's wrong with extern far pascal foo() vs EXTERN FAR PASCAL foo()?
>And why should I use "SEL n" when I mean "unsigned n"? I would bitch a lot
>less if I didn't have to have MANY books open to figure out what these silly
>things meant! (oddly enough, if I ask MSC to generate protos for my EXTERN
>FAR PASCAL functions, they get generated correctly..)
> 

>Seems like we keep working towards standardization and someone in the infinite
>glory wants to change it for us.... *sigh*
> 
>Steve Resnick -<stever@octopus.COM apple!octopus!stever sun!vsi1!octopus!stever>
>408/241-1533 Process Scientific, Inc.
>"0x2B|~0x2B THAT is the question!"

What is wrong is that far and pascal aren't ANSI C to begin with.  And
Microsoft has decided that for version 6.0, "__far" is better than "far"
(because ANSI reserves identifiers beginning with "__" to the compiler
writer.)

So if you followed the Microsoft "standard", all you have to do is use
the new set of macros and your code works.

Then if you would like to port your code to some other machine, you
can #define FAR to nothing.

At least Microsoft has an excuse for the near/far crap we have to put
up with.  They can blame it on the wizards at Intel who decided that
compatibility with previous chips is more important than usability.

The jerks at DEC screwed up external references all by themselves.
Rather than change some of their other software, they decided to foist
off their poor excuse for a compiler as C under VMS.

Ken

bright@Data-IO.COM (Walter Bright) (07/14/90)

In article <5353@stpstn.UUCP> lerman@stpstn.UUCP (Ken Lerman) writes:
<In article <1990Jul10.004710.20500@Octopus.COM> stever@octopus.UUCP (Steve Resnick ) writes:
<<In the OS/2 programmers refrences, Quick Help, and other
<<MS blessed/produced products they all refer to these "improved" types and 
<<declarations. an example is:
<<
<<#define EXTERN extern
<<#define far FAR
<<#define pascal PASCAL
<<
<<What's wrong with extern far pascal foo() vs EXTERN FAR PASCAL foo()?
<Microsoft has decided that for version 6.0, "__far" is better than "far"
<So if you followed the Microsoft "standard", all you have to do is use
<the new set of macros and your code works.
<Then if you would like to port your code to some other machine, you
<can #define FAR to nothing.

The FAR macro is still entirely unnecessary. To port to a machine that does
not have far:

#define far

To port to the new _far keyword:

#define far _far

There is no point to the FAR macro (and the other similar ones).

david@csource.oz.au (david nugent) (07/15/90)

In <2569@dataio.Data-IO.COM> bright@Data-IO.COM (Walter Bright) writes:

><<#define EXTERN extern
><<#define far FAR
><<#define pascal PASCAL
><<
><<What's wrong with extern far pascal foo() vs EXTERN FAR PASCAL foo()?
><Microsoft has decided that for version 6.0, "__far" is better than "far"
><So if you followed the Microsoft "standard", all you have to do is use
><the new set of macros and your code works.
><Then if you would like to port your code to some other machine, you
><can #define FAR to nothing.

>The FAR macro is still entirely unnecessary. To port to a machine that does
>not have far:

>#define far

>To port to the new _far keyword:

>#define far _far

>There is no point to the FAR macro (and the other similar ones).


While technically that's quite correct, I use uppercased macros for
type declarators and moderators purely as a reminder that it isn't
supported in all environments in which the code may be compiled.  I
even use EXTERN, VOLATILE, STATIC and a few others, with one include
file "environ.h" which takes care of the specific environment.

I find that it helps in reminding me not to rely on environment
and/or compiler specific features unless I specifically #ifdef the
code.  It probably helps make the code more readable too.

-- 
_______________________________________________________________________________
 Unique Computing Pty Ltd  Melbourne  Australia  -  Communications Specialists 
        david@csource.oz.au    3:632/348@fidonet    28:4100/1@signet