Bug 8730 – writeln stops on a nul character, even if passed a D string

Status
RESOLVED
Resolution
FIXED
Severity
minor
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-09-26T13:12:00Z
Last change time
2014-07-17T17:14:56Z
Keywords
pull
Assigned to
nobody
Creator
destructionator

Comments

Comment #0 by destructionator — 2012-09-26T13:12:00Z
import std.stdio; void main() { writeln("test\0gone"); } only prints "test". writefln("%s") prints the whole thing. $ ./test | xxd 0000000: 7465 7374 0a as you can see the data is indeed not being printed; it isn't just invisible on my screen.
Comment #1 by andrej.mitrovich — 2012-10-04T17:11:33Z
I'm just guessing, but: void writeln(T...)(T args) { static if (T.length == 0) { enforce(fputc('\n', .stdout.p.handle) == '\n'); } else static if (T.length == 1 && is(typeof(args[0]) : const(char)[]) && !is(typeof(args[0]) == enum) && !is(typeof(args[0]) == typeof(null)) && !isAggregateType!(typeof(args[0]))) { // Specialization for strings - a very frequent case enforce(fprintf(.stdout.p.handle, "%.*s\n", cast(int) args[0].length, args[0].ptr) >= 0); } else { // Most general instance stdout.write(args, '\n'); } } The specialization is probably to blame. I think 'args[0].length' probably sets the max limit rather than min, but I don't know enough about fprintf internals. :)
Comment #2 by destructionator — 2012-10-09T19:24:49Z
(In reply to comment #1) > The specialization is probably to blame. I think 'args[0].length' probably sets > the max limit rather than min, but I don't know enough about fprintf internals. > :) Yeah, you're right. The man page for printf says "the maximum number of characters to be printed from a string for s and S conversions." I'm not sure what is best here. I really think it should work, but the specialization has got to be there for a reason too.
Comment #3 by andrej.mitrovich — 2012-10-09T19:38:15Z
(In reply to comment #2) > (In reply to comment #1) > > The specialization is probably to blame. I think 'args[0].length' probably sets > > the max limit rather than min, but I don't know enough about fprintf internals. > > :) > > > Yeah, you're right. The man page for printf says "the maximum number of > characters to be printed from a string for s and S conversions." > > > I'm not sure what is best here. I really think it should work, but the > specialization has got to be there for a reason too. It could me off guard just recently. E.g. git uses two strings separated by nul in it's object format, and when I've tried to print out the contents as a char[] using writeln it would only print out a small portion of it, even though printing it as a byte[] would print much more. Anyway this *is* a bug. We can't have it both ways: writeln("bla\0bla"); // bla writefln("%s", "bla\0bla"); // bla[NUL]bla
Comment #4 by braddr — 2012-10-09T20:33:42Z
Shouldn't this be trivial to fix: Replace: fprintf(.stdout.p.handle, "%.*s\n", cast(int) args[0].length, args[0].ptr) with: fwrite(args[0].ptr, 1, args[0].length, .stdout.p.handle)
Comment #5 by braddr — 2012-10-09T20:36:53Z
oops, followed by the same code as in the length == 0 code to get the \n. At which point it's questionable that specialization is all that special.
Comment #6 by yuri.musashi.miwa.tamura — 2013-12-15T03:53:31Z
write("test\0gone\n"); write("test\0gone", '\n'); writeln("test\0gon", "e"); All of the above code outputs NUL. I expect writeln("test\0gone") has the same behavior.
Comment #7 by monarchdodra — 2013-12-15T10:25:36Z
Has anybody done anything to fix this? If not, then IMO, it's simply invalid. I think this is "just" an OS output issue: When printing a null character to console, the console seizes to print for the current line. For example, on windows, writeln("test\0gone"); prints test Yet writeln("test\0gone\n"); prints test gone and writeln("test\0gon", 'e'); prints test gone Weird, right? So I decided to simply print to file, and check what is actually being *passed* to the stream (NOT what the console prints). Sure enough, everything is correctly placed in the stream, null and all. Conclusion => Output stream is the one to blame; D passes everything correctly to the stream. Gonna see how this behaves on linux next.
Comment #8 by yuri.musashi.miwa.tamura — 2013-12-16T03:13:29Z
This problem is only in "std.stdio.writeln". "std.stdio.File.writeln" doesn't have it. // abc.d import std.stdio; void main() { writeln("test\0gone"); // output "test\n" stdout.writeln("test\0gone"); // output "test\0gone\n" } $ rdmd abc | od -a 0000000 t e s t nl t e s t nul g o n e nl 0000017
Comment #9 by bearophile_hugs — 2014-07-07T11:14:37Z
*** Issue 13065 has been marked as a duplicate of this issue. ***
Comment #10 by hsteoh — 2014-07-15T04:10:15Z
Wow, has this trivial bug really been around for 2 years?! Anyway, here's the fix: https://github.com/D-Programming-Language/phobos/pull/2334
Comment #11 by github-bugzilla — 2014-07-17T16:51:36Z
Commit pushed to master at https://github.com/D-Programming-Language/phobos https://github.com/D-Programming-Language/phobos/commit/4f81791438d283edbe508125bbc13ff00e959dd2 Merge pull request #2334 from quickfur/issue8730 std.stdio.writeln should write strings with embedded nulls correctly
Comment #12 by hsteoh — 2014-07-17T17:14:56Z
Verified fixed in git HEAD.