/* The MSDN Library 10/2001 gives this example for the use of dup()/dup2()
to make "stdout" refer to a program-determined file and then restores it
NOTE: All error checking deleted for "clarity" Also, the type and
arguments
of main are not proper either (that's the way it was there) */

#include <io.h>
#include <stdlib.h>
#include <stdio.h>

void main( void )
{
int old;
FILE *new;

old = _dup( 1 ); /* "old" now refers to "stdout" */
/* Note: file handle 1 == "stdout" */

new = fopen( "data", "w" );

/* stdout now refers to file "data" */
_dup2( _fileno( new ), 1 );

puts( "This goes to file 'data'\r\n" );

fclose( new );

/* Restore original stdout */
_dup2( old, 1 );
}




/* BUT I'd like to know what's wrong with using this method instead */

void main(void)
{
FILE *savestdout = stdout;
stdout = fopen( "data" , "w" );
...
fclose( stdout );
stdout = savestdout;
}



TIA
Norm

Re: switching stdout to a file by Doug

Doug
Fri May 06 00:17:17 CDT 2005

On Fri, 06 May 2005 04:03:51 GMT, Norm Dresner wrote:

> /* The MSDN Library 10/2001 gives this example for the use of dup()/dup2()
> to make "stdout" refer to a program-determined file and then restores it
> NOTE: All error checking deleted for "clarity" Also, the type and
> arguments
> of main are not proper either (that's the way it was there) */
>
> #include <io.h>
> #include <stdlib.h>
> #include <stdio.h>
>
> void main( void )
> {
> int old;
> FILE *new;
>
> old = _dup( 1 ); /* "old" now refers to "stdout" */
> /* Note: file handle 1 == "stdout" */
>
> new = fopen( "data", "w" );
>
> /* stdout now refers to file "data" */
> _dup2( _fileno( new ), 1 );
>
> puts( "This goes to file 'data'\r\n" );
>
> fclose( new );
>
> /* Restore original stdout */
> _dup2( old, 1 );
> }
>
>
>
>
> /* BUT I'd like to know what's wrong with using this method instead */
>
> void main(void)
> {
> FILE *savestdout = stdout;
> stdout = fopen( "data" , "w" );
> ...
> fclose( stdout );
> stdout = savestdout;
> }
>
>
>
> TIA
> Norm

The second method isn't guaranteed to work. First, stdout is only required
to be an *expression* of type FILE*. It needn't be an actual pointer
lvalue. In fact, VC's <stdio.h> defines it like this:

#define stdout (&_iob[1])

That's an rvalue, and you can't assign to it. Second, and this is getting
even more into implementation details, stdout is hardcoded to file
descriptor 1. So code that uses _write(1) wouldn't notice your change to
stdout, nor would code that directly uses &_iob[1], such as one weird
function in <wchar.h>. Your next question should be, "What about *stdout =
*fp?" That's actually suggested in a KB article or two IIRC, but I'm trying
to forget it. As FILE is an "object type", the assignment should work, but
the C Standard says, "The address of the FILE object used to control a
stream may be significant; a copy of a FILE object may not necessarily
serve in place of the original." So I'd regard it as very non-portable at
best. To conclude, use the venerable dup/dup2 method, which is non-standard
but very portable. There's also the standard freopen, but that function
irreversibly closes the stream you're trying to redirect.

--
Doug Harrison
Microsoft MVP - Visual C++

Re: switching stdout to a file by Eugene

Eugene
Fri May 06 03:39:43 CDT 2005

Doug Harrison [MVP] wrote:
> To conclude, use the
> venerable dup/dup2 method, which is non-standard but very portable.
> There's also the standard freopen, but that function irreversibly
> closes the stream you're trying to redirect.

On Windows you might want to add

SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(_fileno(fp)));

in addition to whatever you do to redirect stderr.

Also due to the way DLLs work there could be in fact multiple and distinct
stdouts in one application. In which case redirection made in one module
will not affect others.


--
Eugene
http://www.gershnik.com



Re: switching stdout to a file by Norm

Norm
Fri May 06 09:28:31 CDT 2005

"Doug Harrison [MVP]" <dsh@mvps.org> wrote in message
news:1kt93kexpmf5v.7y9p2t2lfhct.dlg@40tude.net...
> On Fri, 06 May 2005 04:03:51 GMT, Norm Dresner wrote:
>
> > /* BUT I'd like to know what's wrong with using this method instead */
> >
> > void main(void)
> > {
> > FILE *savestdout = stdout;
> > stdout = fopen( "data" , "w" );
> > ...
> > fclose( stdout );
> > stdout = savestdout;
> > }
> >
> >
> >
> > TIA
> > Norm
>
> The second method isn't guaranteed to work. First, stdout is only required
> to be an *expression* of type FILE*. It needn't be an actual pointer
> lvalue. In fact, VC's <stdio.h> defines it like this:
>
> #define stdout (&_iob[1])
>
> That's an rvalue, and you can't assign to it. Second, and this is getting
> even more into implementation details, stdout is hardcoded to file
> descriptor 1. So code that uses _write(1) wouldn't notice your change to
> stdout, nor would code that directly uses &_iob[1], such as one weird
> function in <wchar.h>. Your next question should be, "What about *stdout =
> *fp?" That's actually suggested in a KB article or two IIRC, but I'm
trying
> to forget it. As FILE is an "object type", the assignment should work, but
> the C Standard says, "The address of the FILE object used to control a
> stream may be significant; a copy of a FILE object may not necessarily
> serve in place of the original." So I'd regard it as very non-portable at
> best. To conclude, use the venerable dup/dup2 method, which is
non-standard
> but very portable. There's also the standard freopen, but that function
> irreversibly closes the stream you're trying to redirect.

Thanks much for the detailed explanation. I knew that stdout might not be
an lvalue in every implementation and I distinctly remember doing something
very much like this in the distant past but I absolutely cannot recall what
system it was in.

The context in which I want to do this is a program that cycles through a
binary file produced when a particular system goes through a certain phase
and records some details about the internal program data -- but the file
contains many data sets and I wanted to write each one to a different file
(indexed by set#) and the program already writes to stdout in many places so
I figured that simply redirecting it for each set separately would be easier
than going through and changing upwards of several dozen write statements.
It'll probably be done that way but the redirection becomes a little less
transparent to subsequent generations of program maintainers.


Norm


Re: switching stdout to a file by Norm

Norm
Fri May 06 09:29:39 CDT 2005

"Eugene Gershnik" <gershnik@hotmail.com> wrote in message
news:uvZPlchUFHA.3544@TK2MSFTNGP10.phx.gbl...
> Doug Harrison [MVP] wrote:
> > To conclude, use the
> > venerable dup/dup2 method, which is non-standard but very portable.
> > There's also the standard freopen, but that function irreversibly
> > closes the stream you're trying to redirect.
>
> On Windows you might want to add
>
> SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(_fileno(fp)));
>
> in addition to whatever you do to redirect stderr.
>
> Also due to the way DLLs work there could be in fact multiple and distinct
> stdouts in one application. In which case redirection made in one module
> will not affect others.
>
>

Fortunately, in my particular case, the program is a "simple" console app
with no DLLs -- but I'll file this away for future reference since I just
know it'll come up later.

Thanks
Norm


Re: switching stdout to a file by Doug

Doug
Fri May 06 09:53:10 CDT 2005

On Fri, 6 May 2005 01:39:43 -0700, Eugene Gershnik wrote:

> Doug Harrison [MVP] wrote:
>> To conclude, use the
>> venerable dup/dup2 method, which is non-standard but very portable.
>> There's also the standard freopen, but that function irreversibly
>> closes the stream you're trying to redirect.
>
> On Windows you might want to add
>
> SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(_fileno(fp)));
>
> in addition to whatever you do to redirect stderr.

Look at the source code to dup2; this is done automagically for console
apps (but correctly :). I can think of two things it enables:

1. It lets you use the Windows API directly for I/O, which for a program
that uses stdio, should be rare.

2. It helps with handle inheritance if:

a. You start other console programs by using CreateProcess or other
Windows API.

b. You start non-VC console programs using _spawn and similar functions,
and those programs don't know about those function's use of
StartupInfo.lpReserved2.

> Also due to the way DLLs work there could be in fact multiple and distinct
> stdouts in one application. In which case redirection made in one module
> will not affect others.

As usual with CRT state issues, the solution to that is to link your
modules that expect to share CRT state to the same CRT DLL.

--
Doug Harrison
Microsoft MVP - Visual C++

Re: switching stdout to a file by Eugene

Eugene
Fri May 06 13:24:37 CDT 2005

Doug Harrison [MVP] wrote:
> On Fri, 6 May 2005 01:39:43 -0700, Eugene Gershnik wrote:
>
>> Doug Harrison [MVP] wrote:
>>> To conclude, use the
>>> venerable dup/dup2 method, which is non-standard but very portable.
>>> There's also the standard freopen, but that function irreversibly
>>> closes the stream you're trying to redirect.
>>
>> On Windows you might want to add
>>
>> SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(_fileno(fp)));
>>
>> in addition to whatever you do to redirect stderr.
>
> Look at the source code to dup2; this is done automagically for
> console apps (but correctly :).

Yup. But only for console apps. I got bitten by it once precisely because a
code that used to reside in console app was reused in a windows one.

> I can think of two things it enables:
>
> 1. It lets you use the Windows API directly for I/O, which for a
> program that uses stdio, should be rare.

Depends. You may load third party code that has its own ideas (also see
below)

[...]

>> Also due to the way DLLs work there could be in fact multiple and
>> distinct stdouts in one application. In which case redirection made
>> in one module will not affect others.
>
> As usual with CRT state issues, the solution to that is to link your
> modules that expect to share CRT state to the same CRT DLL.

Unless you load something you don't control. Today I see this situation
quite often because most people still use VC6 but a significant prportion
switched to 7.1. The end result is that it is common to find both versions
of CRT in the same process.


--
Eugene
http://www.gershnik.com



Re: switching stdout to a file by Doug

Doug
Fri May 06 15:03:27 CDT 2005

On Fri, 6 May 2005 11:24:37 -0700, Eugene Gershnik wrote:

>>> On Windows you might want to add
>>>
>>> SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(_fileno(fp)));
>>>
>>> in addition to whatever you do to redirect stderr.
>>
>> Look at the source code to dup2; this is done automagically for
>> console apps (but correctly :).
>
> Yup. But only for console apps. I got bitten by it once precisely because a
> code that used to reside in console app was reused in a windows one.

Having never once wanted to use stdout and friends from a GUI app, I don't
know if what you suggested will work. It's somewhat different than what MS
recommends here:

INFO: Calling CRT Output Routines from a GUI Application
http://support.microsoft.com/kb/q105305

>> I can think of two things it enables:
>>
>> 1. It lets you use the Windows API directly for I/O, which for a
>> program that uses stdio, should be rare.

So the context isn't lost, let me clarify that I was talking about stdout
and friends when I referred to stdio.

> Depends. You may load third party code that has its own ideas (also see
> below)

Like I said, it should be rare. I have no personal experience of such. In
addition to stating the painfully obvious possibility, can you provide an
example? That way, it wouldn't seem so much like FUD, and it would even
help my supposition that it's "rare", as opposed to non-existent. Then
maybe you can explore why writing directly to OS-level standard handles is
an odd, unhelpful thing for anyone to do inside a C or C++ program that
uses stdio, and why it may require still more special measures on the part
of the unwitting user to have a chance of working more or less right.

> [...]
>
>>> Also due to the way DLLs work there could be in fact multiple and
>>> distinct stdouts in one application. In which case redirection made
>>> in one module will not affect others.
>>
>> As usual with CRT state issues, the solution to that is to link your
>> modules that expect to share CRT state to the same CRT DLL.
>
> Unless you load something you don't control. Today I see this situation
> quite often because most people still use VC6 but a significant prportion
> switched to 7.1. The end result is that it is common to find both versions
> of CRT in the same process.

In which case, the program is broken to the extent it requires one copy of
the CRT state, because this sort of DLL usage has to be viewed as
equivalent to using a static library. Many other things won't work right
under these conditions. Again, can you provide a concrete example in
addition to stating the painfully obvious possibility? When I've observed
multiple CRTs in my programs, it's due to loading some DLL which is a black
box and has no expectation of sharing CRT state with my process, so it
doesn't matter. Sometimes, not sharing CRT state is even desirable. That's
particularly true for in-process COM servers, which should not intrude upon
the processes that load them by changing their CRT state.

--
Doug Harrison
Microsoft MVP - Visual C++

Re: switching stdout to a file by Eugene

Eugene
Fri May 06 16:19:34 CDT 2005

Doug Harrison [MVP] wrote:
> On Fri, 6 May 2005 11:24:37 -0700, Eugene Gershnik wrote:
>
>> Depends. You may load third party code that has its own ideas (also
>> see below)
>
> Like I said, it should be rare.

Because you say so?

> I have no personal experience of
> such.

You are luckier than me then.

> In addition to stating the painfully obvious possibility, can
> you provide an example? That way, it wouldn't seem so much like FUD,
> and it would even help my supposition that it's "rare", as opposed to
> non-existent.

What kind of example do you expect? Do you want a dump of some proprietary
code? Sorry can't do that.
If there is any FUD here it is in your assertion that this should be rare.
With all respect I doubt you have seen even 1% of the Windows code out
there. I don't know what is rare or not. Just stating a possibility I have
seen.

>>> As usual with CRT state issues, the solution to that is to link your
>>> modules that expect to share CRT state to the same CRT DLL.
>>
>> Unless you load something you don't control. Today I see this
>> situation quite often because most people still use VC6 but a
>> significant prportion switched to 7.1. The end result is that it is
>> common to find both versions of CRT in the same process.
>

[...]

> In which case, the program is broken to the extent it requires one
> copy of the CRT state, because this sort of DLL usage has to be
> viewed as equivalent to using a static library. Many other things
> won't work right under these conditions.

[...]

> When I've observed multiple CRTs in my programs, it's
> due to loading some DLL which is a black box and has no expectation
> of sharing CRT state with my process, so it doesn't matter.

Who talks about CRT state?? This thread was about redirecting output from a
process. If the dll outputs strings you pretty much would want it to write
to the same destination as the rest of your code.

> Again, can you provide a
> concrete example in addition to stating the painfully obvious
> possibility?

Try to load JVM into your process. It uses (at least the 1.4 versions did)
VC6 CRT. It also prints to stdout and stderr and what it prints is pretty
important.

--
Eugene
http://www.gershnik.com



Re: switching stdout to a file by Doug

Doug
Wed May 11 14:03:23 CDT 2005

On Fri, 6 May 2005 14:19:34 -0700, Eugene Gershnik wrote:

> Doug Harrison [MVP] wrote:
>> On Fri, 6 May 2005 11:24:37 -0700, Eugene Gershnik wrote:
>>
>>> Depends. You may load third party code that has its own ideas (also
>>> see below)
>>
>> Like I said, it should be rare.
>
> Because you say so?

Because I believe that the thing I said should be rare, namely,
WriteFile(hStdout), is a strange thing to do in any code that might be used
by a C or C++ program. Even if I thought I had a reason to write it in a
library, thinking about its implications for my library's users would give
me pause. This consideration, along with the fact I've never encountered
code that does this strange thing, led me to express my opinion that it
should be rare.

> What kind of example do you expect? Do you want a dump of some proprietary
> code? Sorry can't do that.

If it's proprietary, then it is indeed *your* problem. However, if you're
talking about a real library *I or someone else* might use that does
WriteFile(hStdout), it would be interesting to know about it.

> If there is any FUD here it is in your assertion that this should be rare.

Listen, you're the one who suggested but didn't motivate a call to
SetStdHandle, something that clearly wasn't necessary for the OP's example.
I simply described some things that call (done by dup2 for the OP's case)
enables, and I gave my opinion that one of the things it enables
(WriteFile(hStdout)) should be rare. I still think it should be rare, as it
isn't very sensible to do.

In that same message, you also raised the specter of DLL troubles, but you
didn't talk about the conditions necessary for them to arise, nor did you
talk about how to avoid the problem.

If you're going to reply to me and add something you think I missed, fine,
but please explain it in sufficient detail that I don't need to come back
and fill in numerous holes in what you wrote. Also, when your contribution
doesn't directly apply to the OP's question, as was the case here, you
really ought to indicate this. When you don't, you're spreading FUD,
because you're giving enough information to cause someone to worry, but not
enough to ease or address those worries.

> With all respect I doubt you have seen even 1% of the Windows code out
> there.

Of course I haven't.

> I don't know what is rare or not. Just stating a possibility I have
> seen.

In that case, maybe you should try holding yourself to the same standard of
completeness you seem to be trying to impose on everyone else. If you did
that, then instead of saying, "You might want to...", you might have said
the more useful thing, "It doesn't matter here, but under conditions X and
Y, you need to..." That might have been a helpful comment, and perhaps I
would have avoided this entire exchange.

> Who talks about CRT state??

You do. You brought it up in your first reply to me.

<eugene>
Also due to the way DLLs work there could be in fact multiple and distinct
stdouts in one application. In which case redirection made in one module
will not affect others.
</eugene>

The stream stdout is part of CRT state.

> This thread was about redirecting output from a
> process.

Well. In two sentences, you denied something you introduced into the thread
and implied that by responding to it, I'm going off-topic.

> If the dll outputs strings you pretty much would want it to write
> to the same destination as the rest of your code.

I'd also want to make sure those strings arrive in the correct sequence WRT
my writes. If the DLL is doing the WriteFile(hStdout) thing, and my stdout
is buffered, things can get messed up unless I'm diligent about flushing
stdout every time before I call DLL functions that write. It's on the basis
of considerations like that that I rendered my "rare" opinion. If we get
there by using different stdouts, I'd consider that a configuration problem
that can only be fully addressed by making both modules use the same
stdout. The standard model of a C program is one stdout. When multiple
modules want to use stdout, trying to make anything else work right is a
distant second choice.

> Try to load JVM into your process. It uses (at least the 1.4 versions did)
> VC6 CRT. It also prints to stdout and stderr and what it prints is pretty
> important.

OK, thanks. I guess we could say this. If you want to load a component that
uses stdout and friends, and you don't know that it links to the same CRT
DLL as you, but you want its output to go to the same place as yours, then
you must ensure SetStdHandle is called before you load it. That way, it
should pick up the correct OS-level standard handles. For a console app,
dup2 accomplishes this, but for a GUI app, you have to do it yourself,
because dup2 sets the OS-level standard handles only when used in console
apps. If one or both streams are buffered, there may be output sequencing
problems unless it's arranged that the buffered stream in module X is
flushed before module Y performs a write. While not relevant to the OP's
question or my reply to him, at least there's enough detail for this to be
useful to somebody.

--
Doug Harrison
Microsoft MVP - Visual C++

Re: switching stdout to a file by Eugene

Eugene
Wed May 11 18:08:16 CDT 2005

Doug Harrison [MVP] wrote:
> On Fri, 6 May 2005 14:19:34 -0700, Eugene Gershnik wrote:
>
>> Doug Harrison [MVP] wrote:
>>> On Fri, 6 May 2005 11:24:37 -0700, Eugene Gershnik wrote:
>>>
>>>> Depends. You may load third party code that has its own ideas (also
>>>> see below)
>>>
>>> Like I said, it should be rare.
>>
>> Because you say so?
>
> Because I believe that the thing I said should be rare, namely,
> WriteFile(hStdout), is a strange thing to do

[...]

LOL. Of all the code I've ever seen (an insignificatnt fraction of all the
code out there) most did much stranger things than this. Most windows
programmers I've seen don't know what is the difference between fwrite and
WriteFile.
And no it is not WriteFile(hStdout) but rather WriteFile(hFile) where later
hStdout was passed to the library by somebody not familiar with the code.

>> What kind of example do you expect? Do you want a dump of some
>> proprietary code? Sorry can't do that.
>
> If it's proprietary, then it is indeed *your* problem. However, if
> you're talking about a real library *I or someone else* might use
> that does WriteFile(hStdout), it would be interesting to know about
> it.

I have no idea about what you or anybody else may or may not use. FWIW the
number of people who have "my" problem by virtue of working in the same
place is most likely bigger than the number of people you ever worked with.
That's where my knowlege ends.

>> If there is any FUD here it is in your assertion that this should be
>> rare.
>
> Listen, you're the one who suggested but didn't motivate a call to
> SetStdHandle, something that clearly wasn't necessary for the OP's
> example

Two reasons. First this call will never harm anybody. You don't attach FDA
warnings to water bottle the way you do to drugs. Second code tends to
evolve and what was written in one environment tends to end up in another
pretty quickly. You assume that an example somebody posted is the real
target while I just see it as an example of something else he wants to
accomplish now or in the future.

[...]

> I still think it
> should be rare, as it isn't very sensible to do.

This doesn't follow. Most non-sensible things are very frequent.

> In that same message, you also raised the specter of DLL troubles,
> but you didn't talk about the conditions necessary for them to arise,
> nor did you talk about how to avoid the problem.

Pardon me for not being a language/compiler/whatever lawer and not turning
every post into MSDN addendum. Unless there is reason to suspect otherwise I
assume every poster to be smart and able to google details out for himself.
The problem is real and it is up to OP to decide whether it affects him.

> If you're going to reply to me and add something you think I missed,
> fine, but please explain it in sufficient detail that I don't need to
> come back and fill in numerous holes in what you wrote.

You don't need to do anything. If you feel that you must "fill in numerous
holes" this is your problem not mine.

> Also, when
> your contribution doesn't directly apply to the OP's question, as was
> the case here, you really ought to indicate this.

Ought? And again I consider the information to directly apply to OP or
anybody else who want to write reusable "RedirectStdout" function.

> When you don't,
> you're spreading FUD, because you're giving enough information to
> cause someone to worry, but not enough to ease or address those
> worries.

In which case an intelligent person can google/MSDN out the rest.

>> I don't know what is rare or not. Just stating a possibility I have
>> seen.
>
> In that case, maybe you should try holding yourself to the same
> standard of completeness you seem to be trying to impose on everyone
> else. If you did that, then instead of saying, "You might want
> to...", you might have said the more useful thing, "It doesn't matter
> here, but under conditions X and Y, you need to..." That might have
> been a helpful comment, and perhaps I would have avoided this entire
> exchange.

Why do you apply your own rules to me? If you want to write "War and Peace"
in every post by all means do so. I consider the information I have given to
be enough. If the OP or anybody else didn't understand/couldn't find why I
said what I said all they need to do is to ask. If they found out the reason
by themselves and decided for themselves why waste electrons?

[...]

You snipped the sentence I was replying to so here it is again

>>> When I've observed multiple CRTs in my programs, it's
>>> due to loading some DLL which is a black box and has no expectation
>>> of sharing CRT state with my process, so it doesn't matter
>
>> Who talks about CRT state??
>
> You do. You brought it up in your first reply to me.
>
[...]
>
> The stream stdout is part of CRT state.

Yes but "the destination where output goes" isn't. At least not in the mind
of an average user in the context of black-box dlls.

>> This thread was about redirecting output from a
>> process.
>
> Well. In two sentences, you denied something you introduced into the
> thread and implied that by responding to it, I'm going off-topic.

I don't see it but whatever.

>> If the dll outputs strings you pretty much would want it to write
>> to the same destination as the rest of your code.
>
> I'd also want to make sure those strings arrive in the correct
> sequence WRT my writes.

Very true. Do you expect me to mention this too just in case my posts are
too short for "War and Peace"? ;-)

> The
> standard model of a C program is one stdout.

And no DLLs or _dup2. We have left the standard model long ago, don't you
think?

>> Try to load JVM into your process. It uses (at least the 1.4
>> versions did) VC6 CRT. It also prints to stdout and stderr and what
>> it prints is pretty important.
>
> OK, thanks. I guess we could say this. If you want to load a
> component that uses stdout and friends, and you don't know that it
> links to the same CRT DLL as you, but you want its output to go to
> the same place as yours, then you must ensure SetStdHandle is called
> before you load it. That way, it should pick up the correct OS-level
> standard handles. For a console app, dup2 accomplishes this, but for
> a GUI app, you have to do it yourself, because dup2 sets the OS-level
> standard handles only when used in console apps. If one or both
> streams are buffered, there may be output sequencing problems unless
> it's arranged that the buffered stream in module X is flushed before
> module Y performs a write.

Great. I'll certainly use this (with due credit) when I'll decide to write a
book about stdio on Windows. ;-) For an NG post where all posters appear to
be intelligent this is too much IMO.

> While not relevant to the OP's question or
> my reply to him,

according to OP this may be relevant to him

> at least there's enough detail for this to be useful
> to somebody.

As was in the original version.

--
Eugene
http://www.gershnik.com