Closed Bug 104529 Opened 23 years ago Closed 21 years ago

PR_fprintf I/O function misbehaves with standard output

Categories

(NSPR :: NSPR, defect, P2)

4.1.2
x86
OS/2
defect

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: julien.pierre, Assigned: julien.pierre)

References

Details

(Keywords: arch)

Attachments

(2 files, 4 obsolete files)

On OS/2, when writing directly to standard output fd, writing \n goes to the
next line but also keeps the cursor at the same column as it was. As a result,
everything is garbled in command-line programs using NSPR. Eg. the following code :
<pre>
	PR_fprintf(PR_STDOUT,
"\nNetscape Cryptographic Module Utility\n"
"Usage: modutil [command] [options]\n\n"
"                            COMMANDS\n"

</pre>
produces the following output
<pre>


Netscape Cryptographic Module Utility
                                     Usage: modutil [command] [options]


                   COMMANDS
</pre>
Basically all \n need to be converted to \n\r in order to let the cursor return
to the beginning of the line before writing further.
Attached patch proposed patch (obsolete) — Splinter Review
I'm just realizing that this may not be the the appropriate patch since multiple
printf will have the effect of doing the conversion over again. In addition, all
printf will do the \n to \n\r , conversion which is really only necessary for
writes to stdout & stderr - ie, writes to the console.
Some applications that use PR_sprintf to put strings in temporary buffers will
be different on OS/2 as a result.

The proper fix is probably an NSPR layer pushed on the _pr_stdout and pr_stderr
PRFileDesc* , so the conversion is only done when wanted. Wan-Teh, do you agree ?
I don't understand what the actual source of the bug is though.

I don't see it as being an OS/2 problem because

fprintf(stdout, "hello\n");
fprintf(stdout, "hello\n");

works fine.

What about on Windows? \n on Windows should have the same behavior as OS/2.
Michael,

Julien was talking about PR_fprintf, not fprintf.
Mike,

When you do a PR_fprintf to stdout, the underlying NSPR call goes to NSPR
PR_Write, which does a DosWrite directly to the file handle . I think the
problem comes from the way the stdout device on OS/2 interprets the \n and \r
characters.

When you go through the runtime library (fprintf) it somehow must be translating
the stream before writing it to the actual stdout file handle. If you have the
source to the C RTL you might be able to see that. It doesn't ship with VACPP as
far as I know, though.

OK, I wrote a testcase and DosWrite and WriteFile definitely treat \n 
differently.

Maybe we should add special casing in the OS/2 code that just checks if the 
destination handle is 1 (stdout) and does the conversion then?
Mike,

I have attached a test case also that shows the differences in OS/2, NT and 
Unix.

Basically NT and Unix automatically translate the \n written to stdout to mean 
"carriage return + line feed". In OS/2 the device does not translate the \n to 
anything so it just skips a line without returning to the beginning of the line 
- that's a line feed without carriage return, like on a typewriter.

I think is it difficult to decide how to deal with this problem within the 
framework of the current NSPR file API, which does not distinguish between text 
and binary modes. Everything is always treated as binary by NSPR currently.

There are a number of places where the fix could go :

1) translate \n to \r\n within the printf functions as I originally proposed.
This is wrong because one could do several iterations of sprintf(newstring, 
"%s", oldstring) , thereby incurring more than one translation, and extra blank 
lines.

2) translate \n to \r\n in a PRIOLayer that would be pushed only on the stdin, 
stdout and stderr layers. Basically, this will trap calls to things like PR_Read 
and PR_Write, and add the proper translation between the input/output/error 
pipes and the application.

The problem with doing that is that it will break any application that try to 
input or output binary data with PR_Write . There is no NSPR API specified today 
to toggle the translation on/off - one would have to explicitly push or pop the 
layer on the fds in the application for OS/2 only, which is ugly and 
non-portable.

3) translate \n to \r\n in PR_sscanf and PR_fprintf

This would affect all PRFileDesc, not just _pr_stdin, _pr_stdout and _pr_stderr 
. My guess is that this is the closest to what the OS/2 C runtime library does 
currently. Since sscanf and fprintf always deal with text, the change shouldn't 
impact any applications using binary data. However, it will introduce a 
difference in the content of text files created by NSPR applications on an OS/2 
platform vs text files created by NSPR applications on a Unix and NT platform. 
Also, some files created on other platforms may not be read properly on OS/2, 
and vice-versa.

I'm afraid this could cause problems with applications that use temporary files 
as text, write them with PR_fprintf, and then send them to the network. In that 
case it could show some odd problems.

As you see none of the proposed fixes are perfect. If you have better ideas, let 
me know. Wan-Teh, please feel free to jump in and comment on this issue.
Just FYI the output of the program on Unix and NT is :

hello
world

and on OS/2
hello
     world
Talked to Wan-Teh.

He suggested we use some environment variables to set translation on stdin 
/stderr / stdout fds . The implementation would probably be in a PRIOLayer that 
would be pushed only on those fds.

However, I just realized that using environment variables has drawbacks. For 
example if one has an NSPR text-mode application, and an NSPR binary compression 
stream application, and wants to do :
nsprtextapp | nsprzipapp > test.zip

In that case we want the output from nsprtextapp to be translated, and the 
output from nsprzipapp to be binary.

Ultimately I think the application needs to know what it is doing - whether it 
is text or binary I/O, and should have a way of specifying it.
The problem is that there is no such concept in NSPR.

Here is a proposal to change that :

I suggest that a "translation" bit could be added in the public PRFileDesc 
structure. To preserve compatibility (and performance) with all existing NSPR 
applications, the default would be binary mode.

The application could then set the translation mode by a call to 
PR_SetTextFileTranslation(PRFileDesc*) . This function might return an error 
code if the descriptor does not support translation (eg. we probably don't care 
to do this on sockets or other types of PRFileDesc*, only on actual files).

It would also be possible to pass PR_OTEXT as one of the flags to PR_Open to 
open a file in translated mode .

Environment variables for stdin/stdout/stderr translation could still exist so 
that quick tests could be done on existing NSPR applications that don't call 
PR_SetTextFileTranslation on the _pr_stdin / _pr_stdout _pr_stderr that are 
returned by PR_GetSpecialFD , but ultimately the application code should be 
modified and these variables would become deprecated.
Marking NEW.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: patch, review
Keywords: patch, reviewarch
Priority: -- → P2
Target Milestone: --- → 4.2
Blocks: 126923
Perhaps there is a fix after all that doesn't involve a new API.
In the case of PR_fprintf, we can assume we always want to write to a text file,
so the conversion is needed. So the conversion could be done in that function.
My previous patch was doing it in the another layer, which also affected other
functions such as PR_sprintf . We don't want these functions to change.

The fix would be to have PR_fprintf do an extra parsing pass on the string just
before writing it to the file descriptor. If any \n are encountered, it would
need to insert a \r . This might not be the most efficient thing to do, as the
I/O would be broken up into multiple pieces at that point (unless we make a copy
of the whole string, which I don't think is a good idea). The right thing to do
is probably to use scatter/gather if we can. I know OS/2 supports it in some way
but I don't see a call for it (there is no DosWriteV, only DosWrite).
This patch solves the problem much better as it doesn't affect intermediate
functions (PR_sprintf). It fixes the biggest part of the display problem of the
NSS command-line tools on OS/2.
Attachment #53411 - Attachment is obsolete: true
This latest patch still has one flaw - if a program reads and then writes the
data back to the file, it will keep growing. There needs to be an additional
check to see if there is already a \r immediately preceding or following the \n
so that we don't add one in that case.

As far as scatter-gather in OS/2 is concerned, Warp Server for e-business added
a function called DosListIO to do this. We might be able to take advantage of
this in NSPR, but it would require runtime loading of the function, as it is not
available in older Warp 3.0 & Warp 4.0 kernels.
Attached patch better patch (obsolete) — Splinter Review
Attachment #96552 - Attachment is obsolete: true
Wan-Teh,

Could you please review the patch and check it in if appropriate ? Thanks.
Comment on attachment 98015 [details] [diff] [review]
better patch

We should do this only if the fd represents
a console window.  Is it possible to detect
whether an fd is a console on OS/2?
There is a runtime library function in VACPP that peforms this test, but it
doesn't take a system FD. I'll look up the OS/2 API doc to find out how it might
be implemented.

However, I'm not sure if we want to do this test. If somebody is redirecting the
output, say piping it to another program, then the fd might no longer be a tty,
but the \r would still be a problem.
This is to be consistent with the other platforms,
especially Windows, which also uses CRLF as line
endngs in text files.  The current implementation
is only broken for console windows.  If the output
is redirected via a pipe to another program, your
proposed change will cause that other program to
receive different input from what it would receive
if the current version of NSPR were used.

The need to maintain backward compatibility restricts
what we can do here.  This is why I think we should
only perform the translation when writing to a console.
Wan-Teh,

The only released NSPR application for OS/2 is the browser.
I don't think there is an existing dependency of any command-line OS/2
application based on NSPR. So, changing the behavior for such programs should be
OK. The main applications I'm trying to get to work correctly with this patch
indeed are the NSS command-line tools.

I can see that the change may be a problem if PR_fprintf is used to write data
files in the browser, though, as it would change its format.

Actually, I just found out tha the C runtime "isatty" function works on a native
handle, so we can use it here. I have tried it and it works fine.
Attached patch updated patch using isatty (obsolete) — Splinter Review
Attachment #98015 - Attachment is obsolete: true
Comment on attachment 100500 [details] [diff] [review]
updated patch using isatty

Does "\r\n" also produce the correct output
in a console on OS/2?  Your patch avoids adding
an extra '\r' after "\r\n".
Yes, it does.
\r resets the cursor to column 0. So it can be done before or after a \n, and
doesn't need to be done more than once.
Moving to 4.3 , since this didn't get checked in before.
Target Milestone: 4.2 → 4.3
Wan-Teh,

Could you please check this into the tip of NSPR ? This patch will not affect
the Mozilla browser as isatty is used to check if we are on a terminal.

Thanks.
Comment on attachment 100500 [details] [diff] [review]
updated patch using isatty

Is this patch only necessary for the VACPP runtime
library?  I am wondering if the gcc builds also need
this patch.
Wan-Teh,

In the PR_Write code path, we write directly to the native handle with a
DosWrite. So this patch is independent of the runtime library. This is why I
used #ifdef XP_OS2 rather than #ifdef XP_OS2_VACPP .
I built NSPR with gcc successfully on OS/2 today. 

[on a different subject : I could not use the tip but rather a branch,
NSPRPUB_PRE_4_2_CLIENT_BRANCH . Please check in the changes to the tip of NSPR
as soon as possible ]

I verified that this fix was still necessary with the gcc build, and worked with
it as intended.

Comment on attachment 100500 [details] [diff] [review]
updated patch using isatty

>+            if ('\n' == *(msg+curofs) && /* current character is \n */
>+                ( (0 == curofs)   || ('\r' != *(msg+curofs-1)) ) && /* previous character isn't \r */
>+                ( (len == curofs) || ('\r' != *(msg+curofs+1)) )    /* next character isn't \r */

The (len == curofs) test is not needed because it must
be false if the current character is \n.  (The character
at offset 'len' is the terminating null byte.)

Agreed?
I edited the patch to conform to NSPR's coding style.

The only real difference from Julien's patch is that
the useless (len == curofs) test that I pointed out
was removed.
Attachment #100500 - Attachment is obsolete: true
Fixed checked into the NSPR tip (4.4) and the
NSPRPUB_PRE_4_2_CLIENT_BRANCH (mozilla 1.4 beta).
Status: NEW → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
Whiteboard: [4.4]
Whiteboard: [4.4]
Target Milestone: 4.3 → 4.4
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: