phobos/std/stdio.d
2007-10-05 01:13:14 +00:00

597 lines
13 KiB
D

// Written in the D programming language.
/* Written by Walter Bright and Andrei Alexandrescu
* www.digitalmars.com
* Placed in the Public Domain.
*/
/********************************
* Standard I/O functions that extend $(B std.c.stdio).
* $(B std.c.stdio) is automatically imported when importing
* $(B std.stdio).
* Macros:
* WIKI=Phobos/StdStdio
*/
module std.stdio;
public import std.c.stdio;
import std.format;
import std.utf;
import std.string;
import std.gc;
import std.c.stdlib;
import std.c.string;
import std.c.stddef;
import std.conv;
version (DigitalMars)
{
version (Windows)
{
// Specific to the way Digital Mars C does stdio
version = DIGITAL_MARS_STDIO;
}
}
version (DIGITAL_MARS_STDIO)
{
}
else
{
// Specific to the way Gnu C does stdio
version = GCC_IO;
import std.c.linux.linux;
}
version (DIGITAL_MARS_STDIO)
{
extern (C)
{
/* **
* Digital Mars under-the-hood C I/O functions
*/
int _fputc_nlock(int, FILE*);
int _fputwc_nlock(int, FILE*);
int _fgetc_nlock(FILE*);
int _fgetwc_nlock(FILE*);
int __fp_lock(FILE*);
void __fp_unlock(FILE*);
}
alias _fputc_nlock FPUTC;
alias _fputwc_nlock FPUTWC;
alias _fgetc_nlock FGETC;
alias _fgetwc_nlock FGETWC;
alias __fp_lock FLOCK;
alias __fp_unlock FUNLOCK;
}
else version (GCC_IO)
{
/* **
* Gnu under-the-hood C I/O functions; see
* http://www.gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html#I_002fO-on-Streams
*/
extern (C)
{
int fputc_unlocked(int, FILE*);
int fputwc_unlocked(wchar_t, FILE*);
int fgetc_unlocked(FILE*);
int fgetwc_unlocked(FILE*);
void flockfile(FILE*);
void funlockfile(FILE*);
ssize_t getline(char**, size_t*, FILE*);
ssize_t getdelim (char**, size_t*, int, FILE*);
}
alias fputc_unlocked FPUTC;
alias fputwc_unlocked FPUTWC;
alias fgetc_unlocked FGETC;
alias fgetwc_unlocked FGETWC;
alias flockfile FLOCK;
alias funlockfile FUNLOCK;
}
else
{
static assert(0, "unsupported C I/O system");
}
/*********************
* Thrown if I/O errors happen.
*/
class StdioException : Exception
{
uint errno; // operating system error code
this(const char[] msg)
{
super(msg);
}
this(uint errno)
{ const(char*) s = strerror(errno);
super(std.string.toString(s).dup);
}
static void opCall(const char[] msg)
{
throw new StdioException(msg);
}
static void opCall()
{
throw new StdioException(getErrno());
}
}
private
void writefx(FILE* fp, TypeInfo[] arguments, void* argptr, int newline=false)
{ int orientation;
orientation = fwide(fp, 0);
/* Do the file stream locking at the outermost level
* rather than character by character.
*/
FLOCK(fp);
scope(exit) FUNLOCK(fp);
if (orientation <= 0) // byte orientation or no orientation
{
void putc(dchar c)
{
if (c <= 0x7F)
{
FPUTC(c, fp);
}
else
{ char[4] buf;
string b;
b = std.utf.toUTF8(buf, c);
for (size_t i = 0; i < b.length; i++)
FPUTC(b[i], fp);
}
}
std.format.doFormat(&putc, arguments, argptr);
if (newline)
FPUTC('\n', fp);
}
else if (orientation > 0) // wide orientation
{
version (Windows)
{
void putcw(dchar c)
{
assert(isValidDchar(c));
if (c <= 0xFFFF)
{
FPUTWC(c, fp);
}
else
{ wchar[2] buf;
buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
FPUTWC(buf[0], fp);
FPUTWC(buf[1], fp);
}
}
}
else version (linux)
{
void putcw(dchar c)
{
FPUTWC(c, fp);
}
}
else
{
static assert(0);
}
std.format.doFormat(&putcw, arguments, argptr);
if (newline)
FPUTWC('\n', fp);
}
}
/***********************************
* Arguments are formatted per the
* $(LINK2 std_format.html#format-string, format strings)
* and written to $(B stdout).
*/
void writef(...)
{
writefx(stdout, _arguments, _argptr, 0);
}
/***********************************
* Same as $(B writef), but a newline is appended
* to the output.
*/
void writefln(...)
{
writefx(stdout, _arguments, _argptr, 1);
}
/***********************************
* Same as $(B writef), but output is sent to the
* stream fp instead of $(B stdout).
*/
void fwritef(FILE* fp, ...)
{
writefx(fp, _arguments, _argptr, 0);
}
/***********************************
* Same as $(B writefln), but output is sent to the
* stream fp instead of $(B stdout).
*/
void fwritefln(FILE* fp, ...)
{
writefx(fp, _arguments, _argptr, 1);
}
/**********************************
* Read line from stream fp.
* Returns:
* null for end of file,
* char[] for line read from fp, including terminating '\n'
* Params:
* fp = input stream
* Throws:
* $(B StdioException) on error
* Example:
* Reads $(B stdin) and writes it to $(B stdout).
---
import std.stdio;
int main()
{
char[] buf;
while ((buf = readln()) != null)
writef("%s", buf);
return 0;
}
---
*/
char[] readln(FILE* fp = stdin)
{
char[] buf;
readln(fp, buf);
return buf;
}
/**********************************
* Read line from stream fp and write it to buf[],
* including terminating '\n'.
*
* This is often faster than readln(FILE*) because the buffer
* is reused each call. Note that reusing the buffer means that
* the previous contents of it need to be copied if needed.
* Params:
* fp = input stream
* buf = buffer used to store the resulting line data. buf
* is resized as necessary.
* Returns:
* 0 for end of file, otherwise
* number of characters read
* Throws:
* $(B StdioException) on error
* Example:
* Reads $(B stdin) and writes it to $(B stdout).
---
import std.stdio;
int main()
{
char[] buf;
while (readln(stdin, buf))
writef("%s", buf);
return 0;
}
---
*/
size_t readln(FILE* fp, inout char[] buf)
{
version (DIGITAL_MARS_STDIO)
{
FLOCK(fp);
scope(exit) FUNLOCK(fp);
if (__fhnd_info[fp._file] & FHND_WCHAR)
{ /* Stream is in wide characters.
* Read them and convert to chars.
*/
static assert(wchar_t.sizeof == 2);
buf.length = 0;
int c2;
for (int c; (c = FGETWC(fp)) != -1; )
{
if ((c & ~0x7F) == 0)
{ buf ~= c;
if (c == '\n')
break;
}
else
{
if (c >= 0xD800 && c <= 0xDBFF)
{
if ((c2 = FGETWC(fp)) != -1 ||
c2 < 0xDC00 && c2 > 0xDFFF)
{
StdioException("unpaired UTF-16 surrogate");
}
c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
}
std.utf.encode(buf, c);
}
}
if (ferror(fp))
StdioException();
return buf.length;
}
auto sz = std.gc.capacity(buf.ptr);
//auto sz = buf.length;
buf = buf.ptr[0 .. sz];
if (fp._flag & _IONBF)
{
/* Use this for unbuffered I/O, when running
* across buffer boundaries, or for any but the common
* cases.
*/
L1:
char *p;
if (sz)
{
p = buf.ptr;
}
else
{
sz = 64;
p = cast(char*) std.gc.malloc(sz);
std.gc.hasNoPointers(p);
buf = p[0 .. sz];
}
size_t i = 0;
for (int c; (c = FGETC(fp)) != -1; )
{
if ((p[i] = c) != '\n')
{
i++;
if (i < sz)
continue;
buf = p[0 .. i] ~ readln(fp);
return buf.length;
}
else
{
buf = p[0 .. i + 1];
return i + 1;
}
}
if (ferror(fp))
StdioException();
buf = p[0 .. i];
return i;
}
else
{
int u = fp._cnt;
char* p = fp._ptr;
int i;
if (fp._flag & _IOTRAN)
{ /* Translated mode ignores \r and treats ^Z as end-of-file
*/
char c;
while (1)
{
if (i == u) // if end of buffer
goto L1; // give up
c = p[i];
i++;
if (c != '\r')
{
if (c == '\n')
break;
if (c != 0x1A)
continue;
goto L1;
}
else
{ if (i != u && p[i] == '\n')
break;
goto L1;
}
}
if (i > sz)
{
buf = cast(char[])std.gc.malloc(i);
std.gc.hasNoPointers(buf.ptr);
}
if (i - 1)
memcpy(buf.ptr, p, i - 1);
buf[i - 1] = '\n';
if (c == '\r')
i++;
}
else
{
while (1)
{
if (i == u) // if end of buffer
goto L1; // give up
auto c = p[i];
i++;
if (c == '\n')
break;
}
if (i > sz)
{
buf = cast(char[])std.gc.malloc(i);
std.gc.hasNoPointers(buf.ptr);
}
memcpy(buf.ptr, p, i);
}
fp._cnt -= i;
fp._ptr += i;
buf = buf[0 .. i];
return i;
}
}
else version (GCC_IO)
{
if (fwide(fp, 0) > 0)
{ /* Stream is in wide characters.
* Read them and convert to chars.
*/
FLOCK(fp);
scope(exit) FUNLOCK(fp);
version (Windows)
{
buf.length = 0;
int c2;
for (int c; (c = FGETWC(fp)) != -1; )
{
if ((c & ~0x7F) == 0)
{ buf ~= c;
if (c == '\n')
break;
}
else
{
if (c >= 0xD800 && c <= 0xDBFF)
{
if ((c2 = FGETWC(fp)) != -1 ||
c2 < 0xDC00 && c2 > 0xDFFF)
{
StdioException("unpaired UTF-16 surrogate");
}
c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
}
std.utf.encode(buf, c);
}
}
if (ferror(fp))
StdioException();
return buf.length;
}
else version (linux)
{
buf.length = 0;
for (int c; (c = FGETWC(fp)) != -1; )
{
if ((c & ~0x7F) == 0)
buf ~= c;
else
std.utf.encode(buf, cast(dchar)c);
if (c == '\n')
break;
}
if (ferror(fp))
StdioException();
return buf.length;
}
else
{
static assert(0);
}
}
char *lineptr = null;
size_t n = 0;
auto s = getdelim(&lineptr, &n, '\n', fp);
scope(exit) free(lineptr);
if (s < 0)
{
if (ferror(fp))
StdioException();
buf.length = 0; // end of file
return 0;
}
buf = buf.ptr[0 .. std.gc.capacity(buf.ptr)];
if (s <= buf.length)
{
buf.length = s;
buf[] = lineptr[0 .. s];
}
else
{
buf = lineptr[0 .. s].dup;
}
return s;
}
else
{
static assert(0);
}
}
/** ditto */
size_t readln(inout char[] buf)
{
return readln(stdin, buf);
}
// Added by Andrei
/////////////////////////////////////////////////////////////////////////////////
void writeln(T...)(T args)
{
return write(args, '\n');
}
/////////////////////////////////////////////////////////////////////////////////
void write(T...)(T args) {
static if (!is(typeof(args[0]) : FILE*)) {
return write(stdout, args);
} else {
auto target = args[0];
FLOCK(target);
scope(exit) FUNLOCK(target);
fwrite_unlocked(target, args[1 .. $]);
}
}
/////////////////////////////////////////////////////////////////////////////////
private void fwrite_unlocked(T...)(FILE* f, T args) {
alias typeof(args[0]) T0;
static const bool isString = is(T0 : string) || is(T0 : wstring)
|| is(T0 : dstring);
static if (is(T0 : T0[]) && !isString) {
fwrite_unlocked(f, '[');
final n = args[0].length;
for (size_t i = 0; ; ) {
fwrite_unlocked(f, args[0][i]);
if (++i == n) break;
fwrite_unlocked(f, ',');
}
fwrite_unlocked(f, ']');
} else {
static if (isString) {
final s = args[0];
} else {
final s = to!(string)(args[0]);
}
if (std.c.stdio.fwrite(s.ptr, s[0].sizeof, s.length, f) != s.length) {
StdioException();
}
}
static if (args.length > 1) {
fwrite_unlocked(f, args[1 .. $]);
}
}